diff --git a/plugins/video/macro-condition-video.cpp b/plugins/video/macro-condition-video.cpp index 600013be..e6b5fb9d 100644 --- a/plugins/video/macro-condition-video.cpp +++ b/plugins/video/macro-condition-video.cpp @@ -133,7 +133,7 @@ bool MacroConditionVideo::Save(obs_data_t *obj) const _video.Save(obj); obs_data_set_bool(obj, "keepActive", _keepActive); obs_data_set_int(obj, "condition", static_cast(_condition)); - obs_data_set_string(obj, "filePath", _file.c_str()); + _file.Save(obj, "filePath"); obs_data_set_bool(obj, "blockUntilScreenshotDone", _blockUntilScreenshotDone); _brightnessThreshold.Save(obj, "brightnessThreshold"); @@ -154,7 +154,7 @@ bool MacroConditionVideo::Load(obs_data_t *obj) _keepActive = obs_data_get_bool(obj, "keepActive"); SetCondition(static_cast( obs_data_get_int(obj, "condition"))); - _file = obs_data_get_string(obj, "filePath"); + _file.Load(obj, "filePath"); _blockUntilScreenshotDone = obs_data_get_bool(obj, "blockUntilScreenshotDone"); // TODO: Remove this fallback in a future version @@ -202,12 +202,13 @@ void MacroConditionVideo::GetScreenshot(bool blocking) bool MacroConditionVideo::LoadImageFromFile() { - const QFileInfo info(QString::fromStdString(_file)); + const std::string file = _file; + const QFileInfo info(QString::fromStdString(file)); _loadedFileLastModified = info.lastModified(); - _loadedFile = _file; + _loadedFile = file; if (!_matchImage.load(info.absoluteFilePath())) { blog(LOG_WARNING, "Cannot load image data from file '%s'", - _file.c_str()); + file.c_str()); (&_matchImage)->~QImage(); new (&_matchImage) QImage(); _patternImageData = {}; @@ -304,9 +305,10 @@ bool MacroConditionVideo::FileInputIsUpToDate() const if (!requiresFileInput(_condition)) { return true; } - const QFileInfo info(QString::fromStdString(_file)); + const std::string file = _file; + const QFileInfo info(QString::fromStdString(file)); return (_loadedFileLastModified == info.lastModified()) && - (_file == _loadedFile); + (file == _loadedFile); } bool MacroConditionVideo::OutputChanged() @@ -1099,7 +1101,7 @@ void MacroConditionVideoEdit::UpdateEntryData() _condition->setCurrentIndex(_condition->findData( static_cast(_entryData->GetCondition()))); _reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone); - _imagePath->SetPath(QString::fromStdString(_entryData->_file)); + _imagePath->SetPath(_entryData->_file); _usePatternForChangedCheck->setChecked( _entryData->_patternMatchParameters.useForChangedCheck); _patternThreshold->SetDoubleValue( diff --git a/plugins/video/macro-condition-video.hpp b/plugins/video/macro-condition-video.hpp index 3013db05..552e7128 100644 --- a/plugins/video/macro-condition-video.hpp +++ b/plugins/video/macro-condition-video.hpp @@ -60,7 +60,7 @@ public: VideoInput _video; bool _keepActive = false; - std::string _file = obs_module_text("AdvSceneSwitcher.enterPath"); + StringVariable _file = obs_module_text("AdvSceneSwitcher.enterPath"); // Enabling this will reduce matching latency, but slow down the // the condition checks of all macros overall. // diff --git a/plugins/video/parameter-wrappers.cpp b/plugins/video/parameter-wrappers.cpp index 315e7437..af767ec4 100644 --- a/plugins/video/parameter-wrappers.cpp +++ b/plugins/video/parameter-wrappers.cpp @@ -62,13 +62,14 @@ static bool isMinNeighborsValid(int minNeighbors) bool CascadeClassifierParameters::LoadModelData() { - const auto path = QString::fromStdString(_modelPath); + _loadedModelPath = _modelPath; + const auto path = QString::fromStdString(_loadedModelPath); if (!QFileInfo(path).exists(path)) { _detector.reset(); return false; } auto det = std::make_unique(); - if (!det->Load(_modelPath)) { + if (!det->Load(_loadedModelPath)) { _detector.reset(); return false; } @@ -79,7 +80,7 @@ bool CascadeClassifierParameters::LoadModelData() bool CascadeClassifierParameters::Save(obs_data_t *obj) const { auto data = obs_data_create(); - obs_data_set_string(data, "modelPath", _modelPath.c_str()); + _modelPath.Save(data, "modelPath"); scaleFactor.Save(data, "scaleFactor"); obs_data_set_int(data, "minNeighbors", minNeighbors); minSize.Save(data, "minSize"); @@ -108,7 +109,7 @@ bool CascadeClassifierParameters::Load(obs_data_t *obj) return true; } auto data = obs_data_get_obj(obj, "objectMatchData"); - _modelPath = obs_data_get_string(data, "modelPath"); + _modelPath.Load(data, "modelPath"); scaleFactor.Load(data, "scaleFactor"); // TODO: Remove this fallback in a future version if (!obs_data_has_user_value(data, "version")) { @@ -121,11 +122,12 @@ bool CascadeClassifierParameters::Load(obs_data_t *obj) // which invalidates previously saved default model paths. const std::string oldPrefix = "../../data/obs-plugins/advanced-scene-switcher/res/cascadeClassifiers/"; - if (_modelPath.substr(0, oldPrefix.size()) == oldPrefix) { + const std::string oldPath = _modelPath.UnresolvedValue(); + if (oldPath.substr(0, oldPrefix.size()) == oldPrefix) { _modelPath = std::string(obs_get_module_data_path( obs_current_module())) + "/res/cascadeClassifiers/" + - _modelPath.substr(oldPrefix.size()); + oldPath.substr(oldPrefix.size()); } #endif } @@ -150,7 +152,8 @@ bool CascadeClassifierParameters::SetModelPath(const std::string &path) ObjectDetector *CascadeClassifierParameters::GetDetector() { - if (!_detector || !_detector->IsLoaded()) { + if (!_detector || !_detector->IsLoaded() || + std::string(_modelPath) != _loadedModelPath) { if (!LoadModelData()) { return nullptr; } @@ -316,7 +319,7 @@ bool OCRParameters::Save(obs_data_t *obj) const tesseractBasePath.Save(data, "tesseractBasePath"); languageCode.Save(data, "language"); obs_data_set_bool(data, "useConfig", useConfig); - obs_data_set_string(data, "configFile", configFile.c_str()); + configFile.Save(data, "configFile"); color.Save(data, "textColor"); colorThreshold.Save(data, "colorThreshold"); obs_data_set_int(data, "pageSegMode", static_cast(pageSegMode)); @@ -358,7 +361,7 @@ bool OCRParameters::Load(obs_data_t *obj) #endif } useConfig = obs_data_get_bool(data, "useConfig"); - configFile = obs_data_get_string(data, "configFile"); + configFile.Load(data, "configFile"); color.Load(data, "textColor"); if (obs_data_has_user_value(data, "version")) { @@ -382,18 +385,31 @@ void OCRParameters::SetPageMode(tesseract::PageSegMode mode) tesseract::TessBaseAPI *OCRParameters::GetOCR() { - if (!initDone) { + if (!initDone || !SetupIsUpToDate()) { Setup(); } return initDone ? ocr.get() : nullptr; } +static bool containsVariables(const std::string &value) +{ + return std::string(StringVariable(value)) != value; +} + +static bool modelFileExists(const std::string &basePath, + const std::string &languageCode) +{ + const auto dataPath = QString::fromStdString(basePath) + "/" + + QString::fromStdString(languageCode) + + ".traineddata"; + return QFileInfo::exists(dataPath); +} + bool OCRParameters::SetLanguageCode(const std::string &value) { - const auto dataPath = QString::fromStdString(tesseractBasePath) + "/" + - QString::fromStdString(value) + ".traineddata"; - QFileInfo fileInfo(dataPath); - if (!fileInfo.exists(dataPath)) { + // Variable values can change later, so only fixed values are validated + if (!containsVariables(value) && + !modelFileExists(tesseractBasePath, value)) { return false; } languageCode = value; @@ -401,18 +417,10 @@ bool OCRParameters::SetLanguageCode(const std::string &value) return true; } -std::string OCRParameters::GetLanguageCode() const -{ - return languageCode; -} - bool OCRParameters::SetTesseractBasePath(const std::string &value) { - const auto dataPath = QString::fromStdString(value) + "/" + - QString::fromStdString(languageCode) + - ".traineddata"; - QFileInfo fileInfo(dataPath); - if (!fileInfo.exists(dataPath)) { + if (!containsVariables(value) && + !modelFileExists(value, languageCode)) { return false; } tesseractBasePath = value; @@ -420,11 +428,6 @@ bool OCRParameters::SetTesseractBasePath(const std::string &value) return true; } -std::string OCRParameters::GetTesseractBasePath() const -{ - return tesseractBasePath; -} - void OCRParameters::EnableCustomConfig(bool enable) { useConfig = enable; @@ -437,17 +440,27 @@ void OCRParameters::SetCustomConfigFile(const std::string &filename) initDone = false; } +bool OCRParameters::SetupIsUpToDate() const +{ + return setupBasePath == std::string(tesseractBasePath) && + setupLanguageCode == std::string(languageCode) && + setupConfigFile == std::string(configFile); +} + void OCRParameters::Setup() { + setupBasePath = tesseractBasePath; + setupLanguageCode = languageCode; + setupConfigFile = configFile; + ocr = std::make_unique(); if (!ocr) { initDone = false; return; } - const std::string dataPath = std::string(tesseractBasePath) + "/"; - const std::string modelFile = - std::string(languageCode) + ".traineddata"; + const std::string dataPath = setupBasePath + "/"; + const std::string modelFile = setupLanguageCode + ".traineddata"; const auto modelFullPath = QString::fromStdString(dataPath) + QString::fromStdString(modelFile); QFileInfo modelFileInfo(modelFullPath); @@ -459,7 +472,7 @@ void OCRParameters::Setup() return; } - auto configPath = QString::fromStdString(configFile); + auto configPath = QString::fromStdString(setupConfigFile); QFileInfo configFileInfo(configPath); bool configFileExists = configFileInfo.exists(configPath); @@ -471,8 +484,8 @@ void OCRParameters::Setup() setupWithConfig = false; } - char *configs[] = {configFile.data()}; - if (ocr->Init(dataPath.c_str(), languageCode.c_str(), + char *configs[] = {setupConfigFile.data()}; + if (ocr->Init(dataPath.c_str(), setupLanguageCode.c_str(), tesseract::OEM_DEFAULT, setupWithConfig ? configs : nullptr, setupWithConfig ? 1 : 0, nullptr, nullptr, false) != 0) { diff --git a/plugins/video/parameter-wrappers.hpp b/plugins/video/parameter-wrappers.hpp index 51ff760a..492b9e0d 100644 --- a/plugins/video/parameter-wrappers.hpp +++ b/plugins/video/parameter-wrappers.hpp @@ -94,7 +94,7 @@ public: } bool SetModelPath(const std::string &path); - const std::string &GetModelPath() const { return _modelPath; } + const StringVariable &GetModelPath() const { return _modelPath; } ObjectDetector *GetDetector(); NumberVariable scaleFactor = defaultScaleFactor; @@ -106,10 +106,11 @@ private: bool LoadModelData(); std::unique_ptr _detector; - std::string _modelPath = + StringVariable _modelPath = obs_get_module_data_path(obs_current_module()) + std::string( "/res/cascadeClassifiers/haarcascade_frontalface_alt.xml"); + std::string _loadedModelPath; }; class OCRParameters { @@ -125,13 +126,16 @@ public: bool Initialized() const { return initDone; } void SetPageMode(tesseract::PageSegMode); bool SetLanguageCode(const std::string &); - std::string GetLanguageCode() const; + const StringVariable &GetLanguageCode() const { return languageCode; } bool SetTesseractBasePath(const std::string &); - std::string GetTesseractBasePath() const; + const StringVariable &GetTesseractBasePath() const + { + return tesseractBasePath; + } void EnableCustomConfig(bool enable); bool CustomConfigIsEnabled() const { return useConfig; } void SetCustomConfigFile(const std::string &); - std::string GetCustomConfigFile() const { return configFile; } + const StringVariable &GetCustomConfigFile() const { return configFile; } tesseract::PageSegMode GetPageMode() const { return pageSegMode; } tesseract::TessBaseAPI *GetOCR(); @@ -142,6 +146,7 @@ public: private: void Setup(); + bool SetupIsUpToDate() const; tesseract::PageSegMode pageSegMode = tesseract::PSM_SINGLE_BLOCK; StringVariable tesseractBasePath = @@ -149,9 +154,12 @@ private: std::string("/res/ocr"); StringVariable languageCode = "eng"; bool useConfig = false; - std::string configFile = "config.txt"; + StringVariable configFile = "config.txt"; std::unique_ptr ocr; bool initDone = false; + std::string setupBasePath; + std::string setupLanguageCode; + std::string setupConfigFile; }; class ColorParameters {