diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 572066df..6bd1a9a7 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -149,6 +149,7 @@ AdvSceneSwitcher.condition.video.condition.hasNotChanged="has not changed" AdvSceneSwitcher.condition.video.condition.noImage="has no output" AdvSceneSwitcher.condition.video.condition.pattern="matches pattern" AdvSceneSwitcher.condition.video.condition.object="contains object" +AdvSceneSwitcher.condition.video.condition.brightness="brightness" AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?" AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file" AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot" @@ -159,6 +160,9 @@ AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="This will al AdvSceneSwitcher.condition.video.patternThreshold="Threshold: " AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely." AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Use alpha channel as mask for pattern." +AdvSceneSwitcher.condition.video.brightnessThreshold="Average brightness:" +AdvSceneSwitcher.condition.video.brightnessThresholdDescription="A high value is indicating a bright image and a low one a darker one." +AdvSceneSwitcher.condition.video.currentBrightness="Current average brightness: %1" AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: " AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load." AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches." diff --git a/src/macro-external/opencv/macro-condition-video.cpp b/src/macro-external/opencv/macro-condition-video.cpp index 2303231f..b62ee607 100644 --- a/src/macro-external/opencv/macro-condition-video.cpp +++ b/src/macro-external/opencv/macro-condition-video.cpp @@ -33,6 +33,8 @@ static std::map conditionTypes = { "AdvSceneSwitcher.condition.video.condition.pattern"}, {VideoCondition::OBJECT, "AdvSceneSwitcher.condition.video.condition.object"}, + {VideoCondition::BRIGHTNESS, + "AdvSceneSwitcher.condition.video.condition.brightness"}, }; cv::CascadeClassifier initObjectCascade(std::string &path) @@ -116,6 +118,7 @@ bool MacroConditionVideo::Save(obs_data_t *obj) _usePatternForChangedCheck); obs_data_set_double(obj, "threshold", _patternThreshold); obs_data_set_bool(obj, "useAlphaAsMask", _useAlphaAsMask); + obs_data_set_double(obj, "brightness", _brightnessThreshold); obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str()); obs_data_set_double(obj, "scaleFactor", _scaleFactor); obs_data_set_int(obj, "minNeighbors", _minNeighbors); @@ -155,6 +158,7 @@ bool MacroConditionVideo::Load(obs_data_t *obj) obs_data_get_bool(obj, "usePatternForChangedCheck"); _patternThreshold = obs_data_get_double(obj, "threshold"); _useAlphaAsMask = obs_data_get_bool(obj, "useAlphaAsMask"); + _brightnessThreshold = obs_data_get_double(obj, "brightness"); _modelDataPath = obs_data_get_string(obj, "modelDataPath"); _scaleFactor = obs_data_get_double(obj, "scaleFactor"); if (!isScaleFactorValid(_scaleFactor)) { @@ -255,6 +259,12 @@ bool MacroConditionVideo::ScreenshotContainsObject() return objects.size() > 0; } +bool MacroConditionVideo::CheckBrightnessThreshold() +{ + _currentBrigthness = getAvgBrightness(_screenshotData.image) / 255.; + return _currentBrigthness > _brightnessThreshold; +} + bool MacroConditionVideo::Compare() { if (_checkAreaEnable && _condition != VideoCondition::NO_IMAGE) { @@ -278,6 +288,8 @@ bool MacroConditionVideo::Compare() return ScreenshotContainsPattern(); case VideoCondition::OBJECT: return ScreenshotContainsObject(); + case VideoCondition::BRIGHTNESS: + return CheckBrightnessThreshold(); default: break; } @@ -309,6 +321,13 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( "AdvSceneSwitcher.condition.video.patternThresholdDescription"))), _useAlphaAsMask(new QCheckBox(obs_module_text( "AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"))), + _brightnessThreshold(new ThresholdSlider( + 0., 1., + obs_module_text( + "AdvSceneSwitcher.condition.video.brightnessThreshold"), + obs_module_text( + "AdvSceneSwitcher.condition.video.brightnessThresholdDescription"))), + _currentBrightness(new QLabel), _modelDataPath(new FileSelection()), _modelPathLayout(new QHBoxLayout), _objectScaleThreshold(new ThresholdSlider( @@ -366,6 +385,9 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( this, SLOT(PatternThresholdChanged(double))); QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this, SLOT(UseAlphaAsMaskChanged(int))); + QWidget::connect(_brightnessThreshold, + SIGNAL(DoubleValueChanged(double)), this, + SLOT(BrightnessThresholdChanged(double))); QWidget::connect(_objectScaleThreshold, SIGNAL(DoubleValueChanged(double)), this, SLOT(ObjectScaleThresholdChanged(double))); @@ -451,6 +473,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( mainLayout->addWidget(_usePatternForChangedCheck); mainLayout->addWidget(_patternThreshold); mainLayout->addWidget(_useAlphaAsMask); + mainLayout->addWidget(_brightnessThreshold); + mainLayout->addWidget(_currentBrightness); mainLayout->addLayout(_modelPathLayout); mainLayout->addWidget(_objectScaleThreshold); mainLayout->addLayout(_neighborsControlLayout); @@ -465,6 +489,10 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( _entryData = entryData; UpdateEntryData(); _loading = false; + + connect(&_updateBrightnessTimer, &QTimer::timeout, this, + &MacroConditionVideoEdit::UpdateCurrentBrightness); + _updateBrightnessTimer.start(1000); } void MacroConditionVideoEdit::UpdatePreviewTooltip() @@ -673,6 +701,16 @@ void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value) _entryData->LoadImageFromFile(); } +void MacroConditionVideoEdit::BrightnessThresholdChanged(double value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(GetSwitcher()->m); + _entryData->_brightnessThreshold = value; +} + void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value) { if (_loading || !_entryData) { @@ -774,6 +812,14 @@ void MacroConditionVideoEdit::ShowMatchClicked() _previewDialog.ShowMatch(); } +void MacroConditionVideoEdit::UpdateCurrentBrightness() +{ + QString text = obs_module_text( + "AdvSceneSwitcher.condition.video.currentBrightness"); + _currentBrightness->setText( + text.arg(_entryData->GetCurrentBrightness())); +} + void MacroConditionVideoEdit::SelectAreaClicked() { _previewDialog.show(); @@ -843,6 +889,10 @@ void MacroConditionVideoEdit::SetWidgetVisibility() _patternThreshold->setVisible(needsThreshold(_entryData->_condition)); _useAlphaAsMask->setVisible(_entryData->_condition == VideoCondition::PATTERN); + _brightnessThreshold->setVisible(_entryData->_condition == + VideoCondition::BRIGHTNESS); + _currentBrightness->setVisible(_entryData->_condition == + VideoCondition::BRIGHTNESS); _showMatch->setVisible(needsShowMatch(_entryData->_condition)); _objectScaleThreshold->setVisible( needsObjectControls(_entryData->_condition)); @@ -884,6 +934,7 @@ void MacroConditionVideoEdit::UpdateEntryData() _entryData->_usePatternForChangedCheck); _patternThreshold->SetDoubleValue(_entryData->_patternThreshold); _useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask); + _brightnessThreshold->SetDoubleValue(_entryData->_brightnessThreshold); _modelDataPath->SetPath(_entryData->GetModelDataPath().c_str()); _objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor); _minNeighbors->setValue(_entryData->_minNeighbors); diff --git a/src/macro-external/opencv/macro-condition-video.hpp b/src/macro-external/opencv/macro-condition-video.hpp index aab7c862..e284bdb0 100644 --- a/src/macro-external/opencv/macro-condition-video.hpp +++ b/src/macro-external/opencv/macro-condition-video.hpp @@ -25,6 +25,7 @@ enum class VideoCondition { NO_IMAGE, PATTERN, OBJECT, + BRIGHTNESS, }; class MacroConditionVideo : public MacroCondition { @@ -45,6 +46,7 @@ public: bool LoadModelData(std::string &path); std::string GetModelDataPath() { return _modelDataPath; } void ResetLastMatch() { _lastMatchResult = false; } + double GetCurrentBrightness() { return _currentBrigthness; } VideoSelection _video; VideoCondition _condition = VideoCondition::MATCH; @@ -60,6 +62,7 @@ public: bool _usePatternForChangedCheck = false; PatternMatchData _patternData; double _patternThreshold = 0.8; + double _brightnessThreshold = 0.5; cv::CascadeClassifier _objectCascade; double _scaleFactor = 1.1; int _minNeighbors = minMinNeighbors; @@ -76,6 +79,7 @@ private: bool OutputChanged(); bool ScreenshotContainsPattern(); bool ScreenshotContainsObject(); + bool CheckBrightnessThreshold(); bool Compare(); bool CheckShouldBeSkipped(); @@ -88,6 +92,7 @@ private: "/res/cascadeClassifiers/haarcascade_frontalface_alt.xml"); bool _lastMatchResult = false; int _runCount = 0; + double _currentBrigthness = 0.; static bool _registered; static const std::string id; @@ -123,6 +128,8 @@ private slots: void PatternThresholdChanged(double); void UseAlphaAsMaskChanged(int value); + void BrightnessThresholdChanged(double); + void ModelPathChanged(const QString &text); void ObjectScaleThresholdChanged(double); void MinNeighborsChanged(int value); @@ -137,6 +144,8 @@ private slots: void ThrottleEnableChanged(int value); void ThrottleCountChanged(int value); void ShowMatchClicked(); + + void UpdateCurrentBrightness(); signals: void HeaderInfoChanged(const QString &); @@ -152,6 +161,9 @@ protected: ThresholdSlider *_patternThreshold; QCheckBox *_useAlphaAsMask; + ThresholdSlider *_brightnessThreshold; + QLabel *_currentBrightness; + FileSelection *_modelDataPath; QHBoxLayout *_modelPathLayout; ThresholdSlider *_objectScaleThreshold; @@ -177,6 +189,8 @@ protected: std::shared_ptr _entryData; private: + QTimer _updateBrightnessTimer; + void SetWidgetVisibility(); bool _loading = true; }; diff --git a/src/macro-external/opencv/opencv-helpers.cpp b/src/macro-external/opencv/opencv-helpers.cpp index bdd51b5b..356e992a 100644 --- a/src/macro-external/opencv/opencv-helpers.cpp +++ b/src/macro-external/opencv/opencv-helpers.cpp @@ -77,6 +77,21 @@ std::vector matchObject(QImage &img, cv::CascadeClassifier &cascade, return objects; } +uchar getAvgBrightness(QImage &img) +{ + auto i = QImageToMat(img); + cv::Mat hsvImage, rgbImage; + cv::cvtColor(i, rgbImage, cv::COLOR_RGBA2RGB); + cv::cvtColor(rgbImage, hsvImage, cv::COLOR_RGB2HSV); + long long brightnessSum = 0; + for (int i = 0; i < hsvImage.rows; ++i) { + for (int j = 0; j < hsvImage.cols; ++j) { + brightnessSum += hsvImage.at(i, j)[2]; + } + } + return brightnessSum / (hsvImage.rows * hsvImage.cols); +} + // Assumption is that QImage uses Format_RGBA8888. // Conversion from: https://github.com/dbzhang800/QtOpenCV cv::Mat QImageToMat(const QImage &img) diff --git a/src/macro-external/opencv/opencv-helpers.hpp b/src/macro-external/opencv/opencv-helpers.hpp index 30b10299..6a14a449 100644 --- a/src/macro-external/opencv/opencv-helpers.hpp +++ b/src/macro-external/opencv/opencv-helpers.hpp @@ -20,5 +20,6 @@ void matchPattern(QImage &img, QImage &pattern, double threshold, std::vector matchObject(QImage &img, cv::CascadeClassifier &cascade, double scaleFactor, int minNeighbors, cv::Size minSize, cv::Size maxSize); +uchar getAvgBrightness(QImage &img); cv::Mat QImageToMat(const QImage &img); QImage MatToQImage(const cv::Mat &mat);