Skip to content

Commit 43967f5

Browse files
committed
Add temp variable support for video condition
1 parent c043053 commit 43967f5

File tree

5 files changed

+177
-28
lines changed

5 files changed

+177
-28
lines changed

data/locale/en-US.ini

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,17 @@ AdvSceneSwitcher.tempVar.process.name="Process name"
14951495
AdvSceneSwitcher.tempVar.recording.durationSeconds="Recording duration"
14961496
AdvSceneSwitcher.tempVar.recording.durationSeconds.description="Recording duration in seconds.\nThis value does not change while the recording is paused and will be reset to zero if the recording is stopped."
14971497

1498+
AdvSceneSwitcher.tempVar.video.patternCount="Pattern count"
1499+
AdvSceneSwitcher.tempVar.video.patternCount.description="The number of times the given pattern has been found in a given video input frame."
1500+
AdvSceneSwitcher.tempVar.video.objectCount="Object count"
1501+
AdvSceneSwitcher.tempVar.video.objectCount.description="The number of objects the given model has identified in a given video input frame."
1502+
AdvSceneSwitcher.tempVar.video.brightness="Average brightness"
1503+
AdvSceneSwitcher.tempVar.video.brightness.description="The average brightness in a given video input frame in a range from 0 to 1 (dark to bright)."
1504+
AdvSceneSwitcher.tempVar.video.text="OCR text"
1505+
AdvSceneSwitcher.tempVar.video.text.description="The text detected in a given video input frame."
1506+
AdvSceneSwitcher.tempVar.video.color="Average color"
1507+
AdvSceneSwitcher.tempVar.video.color.description="The average RGB color in a given video input frame in HexArgb format."
1508+
14981509
AdvSceneSwitcher.selectScene="--select scene--"
14991510
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
15001511
AdvSceneSwitcher.selectCurrentScene="Current Scene"

src/macro-external/video/macro-condition-video.cpp

Lines changed: 102 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
177177
{
178178
MacroCondition::Load(obj);
179179
_video.Load(obj);
180-
_condition =
181-
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
180+
SetCondition(static_cast<VideoCondition>(
181+
obs_data_get_int(obj, "condition")));
182182
_file = obs_data_get_string(obj, "filePath");
183183
_blockUntilScreenshotDone =
184184
obs_data_get_bool(obj, "blockUntilScreenshotDone");
@@ -261,6 +261,12 @@ bool MacroConditionVideo::SetLanguage(const std::string &language)
261261
return _ocrParameters.SetLanguageCode(language);
262262
}
263263

264+
void MacroConditionVideo::SetCondition(VideoCondition condition)
265+
{
266+
_condition = condition;
267+
SetupTempVars();
268+
}
269+
264270
bool MacroConditionVideo::ScreenshotContainsPattern()
265271
{
266272
cv::Mat result;
@@ -269,9 +275,12 @@ bool MacroConditionVideo::ScreenshotContainsPattern()
269275
_patternMatchParameters.useAlphaAsMask,
270276
_patternMatchParameters.matchMode);
271277
if (result.total() == 0) {
278+
SetTempVarValue("patternCount", "0");
272279
return false;
273280
}
274-
return countNonZero(result) > 0;
281+
const auto count = countNonZero(result);
282+
SetTempVarValue("patternCount", std::to_string(count));
283+
return count > 0;
275284
}
276285

277286
bool MacroConditionVideo::OutputChanged()
@@ -300,12 +309,15 @@ bool MacroConditionVideo::ScreenshotContainsObject()
300309
_objMatchParameters.minNeighbors,
301310
_objMatchParameters.minSize.CV(),
302311
_objMatchParameters.maxSize.CV());
303-
return objects.size() > 0;
312+
const auto count = objects.size();
313+
SetTempVarValue("objectCount", std::to_string(count));
314+
return count > 0;
304315
}
305316

306317
bool MacroConditionVideo::CheckBrightnessThreshold()
307318
{
308319
_currentBrightness = GetAvgBrightness(_screenshotData.image) / 255.;
320+
SetTempVarValue("brightness", std::to_string(_currentBrightness));
309321
return _currentBrightness > _brightnessThreshold;
310322
}
311323

@@ -318,6 +330,7 @@ bool MacroConditionVideo::CheckOCR()
318330
auto text = RunOCR(_ocrParameters.GetOCR(), _screenshotData.image,
319331
_ocrParameters.color, _ocrParameters.colorThreshold);
320332
SetVariableValue(text);
333+
SetTempVarValue("text", text);
321334
if (!_ocrParameters.regex.Enabled()) {
322335
return text == std::string(_ocrParameters.text);
323336
}
@@ -332,10 +345,18 @@ bool MacroConditionVideo::CheckOCR()
332345

333346
bool MacroConditionVideo::CheckColor()
334347
{
335-
return ContainsPixelsInColorRange(_screenshotData.image,
336-
_colorParameters.color,
337-
_colorParameters.colorThreshold,
338-
_colorParameters.matchThreshold);
348+
const bool ret = ContainsPixelsInColorRange(
349+
_screenshotData.image, _colorParameters.color,
350+
_colorParameters.colorThreshold,
351+
_colorParameters.matchThreshold);
352+
// Way too slow for now
353+
//SetTempVarValue("dominantColor", GetDominantColor(_screenshotData.image, 3)
354+
// .name(QColor::HexArgb)
355+
// .toStdString());
356+
SetTempVarValue("color", GetAverageColor(_screenshotData.image)
357+
.name(QColor::HexArgb)
358+
.toStdString());
359+
return ret;
339360
}
340361

341362
bool MacroConditionVideo::Compare()
@@ -378,6 +399,58 @@ bool MacroConditionVideo::Compare()
378399
return false;
379400
}
380401

402+
void MacroConditionVideo::SetupTempVars()
403+
{
404+
MacroCondition::SetupTempVars();
405+
switch (_condition) {
406+
case VideoCondition::PATTERN:
407+
AddTempvar(
408+
"patternCount",
409+
obs_module_text(
410+
"AdvSceneSwitcher.tempVar.video.patternCount"),
411+
obs_module_text(
412+
"AdvSceneSwitcher.tempVar.video.patternCount.description"));
413+
break;
414+
case VideoCondition::OBJECT:
415+
AddTempvar(
416+
"objectCount",
417+
obs_module_text(
418+
"AdvSceneSwitcher.tempVar.video.objectCount"),
419+
obs_module_text(
420+
"AdvSceneSwitcher.tempVar.video.objectCount.description"));
421+
break;
422+
case VideoCondition::BRIGHTNESS:
423+
AddTempvar(
424+
"brightness",
425+
obs_module_text(
426+
"AdvSceneSwitcher.tempVar.video.brightness"),
427+
obs_module_text(
428+
"AdvSceneSwitcher.tempVar.video.brightness.description"));
429+
break;
430+
case VideoCondition::OCR:
431+
AddTempvar(
432+
"text",
433+
obs_module_text("AdvSceneSwitcher.tempVar.video.text"),
434+
obs_module_text(
435+
"AdvSceneSwitcher.tempVar.video.text.description"));
436+
break;
437+
case VideoCondition::COLOR:
438+
AddTempvar(
439+
"color",
440+
obs_module_text("AdvSceneSwitcher.tempVar.video.color"),
441+
obs_module_text(
442+
"AdvSceneSwitcher.tempVar.video.color.description"));
443+
break;
444+
case VideoCondition::MATCH:
445+
case VideoCondition::DIFFER:
446+
case VideoCondition::HAS_NOT_CHANGED:
447+
case VideoCondition::HAS_CHANGED:
448+
case VideoCondition::NO_IMAGE:
449+
default:
450+
break;
451+
}
452+
}
453+
381454
static inline void populateVideoInputSelection(QComboBox *list)
382455
{
383456
for (const auto &[_, name] : videoInputTypes) {
@@ -1155,7 +1228,7 @@ void MacroConditionVideoEdit::UpdatePreviewTooltip()
11551228
return;
11561229
}
11571230

1158-
if (!requiresFileInput(_entryData->_condition)) {
1231+
if (!requiresFileInput(_entryData->GetCondition())) {
11591232
this->setToolTip("");
11601233
return;
11611234
}
@@ -1224,7 +1297,7 @@ void MacroConditionVideoEdit::ConditionChanged(int cond)
12241297
}
12251298

12261299
auto lock = LockContext();
1227-
_entryData->_condition = static_cast<VideoCondition>(cond);
1300+
_entryData->SetCondition(static_cast<VideoCondition>(cond));
12281301
_entryData->ResetLastMatch();
12291302
SetWidgetVisibility();
12301303

@@ -1239,7 +1312,7 @@ void MacroConditionVideoEdit::ConditionChanged(int cond)
12391312
_previewDialog.PatternMatchParametersChanged(
12401313
_entryData->_patternMatchParameters);
12411314

1242-
if (_entryData->_condition == VideoCondition::OBJECT) {
1315+
if (_entryData->GetCondition() == VideoCondition::OBJECT) {
12431316
auto path = _entryData->GetModelDataPath();
12441317
_entryData->_objMatchParameters.cascade =
12451318
initObjectCascade(path);
@@ -1482,27 +1555,28 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
14821555
_sources->setVisible(_entryData->_video.type ==
14831556
VideoInput::Type::SOURCE);
14841557
_scenes->setVisible(_entryData->_video.type == VideoInput::Type::SCENE);
1485-
_imagePath->setVisible(requiresFileInput(_entryData->_condition));
1558+
_imagePath->setVisible(requiresFileInput(_entryData->GetCondition()));
14861559
_usePatternForChangedCheck->setVisible(
1487-
patternControlIsOptional(_entryData->_condition));
1488-
_patternThreshold->setVisible(needsThreshold(_entryData->_condition));
1489-
_useAlphaAsMask->setVisible(_entryData->_condition ==
1560+
patternControlIsOptional(_entryData->GetCondition()));
1561+
_patternThreshold->setVisible(
1562+
needsThreshold(_entryData->GetCondition()));
1563+
_useAlphaAsMask->setVisible(_entryData->GetCondition() ==
14901564
VideoCondition::PATTERN);
14911565
SetLayoutVisible(_patternMatchModeLayout,
1492-
_entryData->_condition == VideoCondition::PATTERN);
1493-
_brightness->setVisible(_entryData->_condition ==
1566+
_entryData->GetCondition() == VideoCondition::PATTERN);
1567+
_brightness->setVisible(_entryData->GetCondition() ==
14941568
VideoCondition::BRIGHTNESS);
1495-
_showMatch->setVisible(needsShowMatch(_entryData->_condition));
1496-
_ocr->setVisible(_entryData->_condition == VideoCondition::OCR);
1497-
_objectDetect->setVisible(_entryData->_condition ==
1569+
_showMatch->setVisible(needsShowMatch(_entryData->GetCondition()));
1570+
_ocr->setVisible(_entryData->GetCondition() == VideoCondition::OCR);
1571+
_objectDetect->setVisible(_entryData->GetCondition() ==
14981572
VideoCondition::OBJECT);
1499-
_color->setVisible(_entryData->_condition == VideoCondition::COLOR);
1573+
_color->setVisible(_entryData->GetCondition() == VideoCondition::COLOR);
15001574
SetLayoutVisible(_throttleControlLayout,
1501-
needsThrottleControls(_entryData->_condition));
1502-
_area->setVisible(needsAreaControls(_entryData->_condition));
1575+
needsThrottleControls(_entryData->GetCondition()));
1576+
_area->setVisible(needsAreaControls(_entryData->GetCondition()));
15031577

1504-
if (_entryData->_condition == VideoCondition::HAS_CHANGED ||
1505-
_entryData->_condition == VideoCondition::HAS_NOT_CHANGED) {
1578+
if (_entryData->GetCondition() == VideoCondition::HAS_CHANGED ||
1579+
_entryData->GetCondition() == VideoCondition::HAS_NOT_CHANGED) {
15061580
_patternThreshold->setVisible(
15071581
_entryData->_patternMatchParameters.useForChangedCheck);
15081582
SetLayoutVisible(
@@ -1528,7 +1602,7 @@ void MacroConditionVideoEdit::SetupPreviewDialogParams()
15281602
_previewDialog.VideoSelectionChanged(_entryData->_video);
15291603
_previewDialog.AreaParametersChanged(_entryData->_areaParameters);
15301604
_previewDialog.ConditionChanged(
1531-
static_cast<int>(_entryData->_condition));
1605+
static_cast<int>(_entryData->GetCondition()));
15321606
}
15331607

15341608
void MacroConditionVideoEdit::UpdateEntryData()
@@ -1541,7 +1615,8 @@ void MacroConditionVideoEdit::UpdateEntryData()
15411615
static_cast<int>(_entryData->_video.type));
15421616
_scenes->SetScene(_entryData->_video.scene);
15431617
_sources->SetSource(_entryData->_video.source);
1544-
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
1618+
_condition->setCurrentIndex(
1619+
static_cast<int>(_entryData->GetCondition()));
15451620
_reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone);
15461621
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
15471622
_usePatternForChangedCheck->setChecked(

src/macro-external/video/macro-condition-video.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ class MacroConditionVideo : public MacroCondition {
4545
void SetPageSegMode(tesseract::PageSegMode);
4646
bool SetLanguage(const std::string &);
4747

48+
void SetCondition(VideoCondition);
49+
VideoCondition GetCondition() const { return _condition; }
50+
4851
VideoInput _video;
49-
VideoCondition _condition = VideoCondition::MATCH;
5052
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
5153
// Enabling this will reduce matching latency, but slow down the
5254
// the condition checks of all macros overall.
@@ -74,6 +76,10 @@ class MacroConditionVideo : public MacroCondition {
7476
bool Compare();
7577
bool CheckShouldBeSkipped();
7678

79+
void SetupTempVars();
80+
81+
VideoCondition _condition = VideoCondition::MATCH;
82+
7783
bool _getNextScreenshot = true;
7884
ScreenshotHelper _screenshotData;
7985
QImage _matchImage;

src/macro-external/video/opencv-helpers.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,61 @@ bool ContainsPixelsInColorRange(const QImage &image, const QColor &color,
225225
return matchPercentage >= totalPixelMatchThreshold;
226226
}
227227

228+
QColor GetAverageColor(const QImage &img)
229+
{
230+
if (img.isNull()) {
231+
return QColor();
232+
}
233+
234+
auto image = QImageToMat(img);
235+
cv::Scalar meanColor = cv::mean(image);
236+
int averageBlue = cvRound(meanColor[0]);
237+
int averageGreen = cvRound(meanColor[1]);
238+
int averageRed = cvRound(meanColor[2]);
239+
240+
return QColor(averageRed, averageGreen, averageBlue);
241+
}
242+
243+
QColor GetDominantColor(const QImage &img, int k)
244+
{
245+
if (img.isNull()) {
246+
return QColor();
247+
}
248+
249+
auto image = QImageToMat(img);
250+
cv::Mat reshapedImage = image.reshape(1, image.rows * image.cols);
251+
reshapedImage.convertTo(reshapedImage, CV_32F);
252+
253+
cv::mean(reshapedImage);
254+
255+
// Apply k-means clustering to group similar colors
256+
cv::TermCriteria criteria(
257+
cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 100, 0.2);
258+
cv::Mat labels, centers;
259+
cv::kmeans(reshapedImage, k, labels, criteria, 1,
260+
cv::KMEANS_RANDOM_CENTERS, centers);
261+
262+
// Find the dominant color
263+
// Center of the cluster with the largest number of pixels
264+
cv::Mat counts = cv::Mat::zeros(1, k, CV_32SC1);
265+
for (int i = 0; i < labels.rows; i++) {
266+
counts.at<int>(0, labels.at<int>(i))++;
267+
}
268+
269+
cv::Point max_loc;
270+
cv::minMaxLoc(counts, nullptr, nullptr, nullptr, &max_loc);
271+
try {
272+
cv::Scalar dominantColor = centers.at<cv::Scalar>(max_loc.y);
273+
const int blue = cv::saturate_cast<int>(dominantColor.val[0]);
274+
const int green = cv::saturate_cast<int>(dominantColor.val[1]);
275+
const int red = cv::saturate_cast<int>(dominantColor.val[2]);
276+
const int alpha = cv::saturate_cast<int>(dominantColor.val[3]);
277+
return QColor(red, green, blue, alpha);
278+
} catch (...) {
279+
}
280+
return QColor();
281+
}
282+
228283
// Assumption is that QImage uses Format_RGBA8888.
229284
// Conversion from: https://github.com/dbzhang800/QtOpenCV
230285
cv::Mat QImageToMat(const QImage &img)

src/macro-external/video/opencv-helpers.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ std::string RunOCR(tesseract::TessBaseAPI *, const QImage &, const QColor &,
6666
bool ContainsPixelsInColorRange(const QImage &image, const QColor &color,
6767
double colorDeviationThreshold,
6868
double totalPixelMatchThreshold);
69+
QColor GetAverageColor(const QImage &img);
70+
QColor GetDominantColor(const QImage &image, int k);
6971
cv::Mat QImageToMat(const QImage &img);
7072
QImage MatToQImage(const cv::Mat &mat);
7173

0 commit comments

Comments
 (0)