Compare commits

...

9 Commits

Author SHA1 Message Date
WarmUpTill
68c6492c3f Revert "Add OCL support to improve performance"
This reverts commit 284c8020b2.
2023-08-09 17:04:00 +02:00
WarmUpTill
0ba7ba77d8 Fix potential crash related to OCR 2023-08-09 17:04:00 +02:00
WarmUpTill
04b2b3474d Fix crash in output change check with pattern matching enabled 2023-08-09 17:04:00 +02:00
WarmUpTill
ecbb5ebbd7 Update issue template 2023-08-09 17:04:00 +02:00
WarmUpTill
b62757b65d Rely on output flag to identify media sources
Using OBS_SOURCE_CONTROLLABLE_MEDIA instead of a hardcoded list of
source names is much more reliable and has the upside of also supporting
plugin sources
2023-08-09 14:01:19 +02:00
WarmUpTill
b1a3ab5493 Fix window condition ignoring title matching if regex is disabled
If the only enabled option was window title matching and regular
expressions were not used any window title would match regardless if it
existed or not
2023-08-09 13:50:32 +02:00
WarmUpTill
abc3357180 Make macro properties dialog resizable 2023-08-07 19:17:41 +02:00
WarmUpTill
29f9cba236 Add option to skip execution of given macro on OBS startup 2023-08-07 19:17:41 +02:00
WarmUpTill
9a62522140 Fix DisplayMessage() not being visible when OBS is always on top
The previous behaviour would cause the impression of OSB being frozen
due to dialogs windows being opened behind OBS while they take over the
input focus.
2023-08-07 18:03:12 +02:00
16 changed files with 180 additions and 111 deletions

View File

@@ -22,6 +22,7 @@ A clear and concise description of what you expected to happen.
**Logs** **Logs**
Please provide a log of your issue with verbose logging enabled (See General tab of the plugin). Please provide a log of your issue with verbose logging enabled (See General tab of the plugin).
In case of a crash, please also include the corresponding crash log.
See [here](https://obsproject.com/forum/threads/please-post-a-log-with-your-issue-heres-how.23074/) for a description where to find the log files and how to share them. See [here](https://obsproject.com/forum/threads/please-post-a-log-with-your-issue-heres-how.23074/) for a description where to find the log files and how to share them.
Please share the currently used plugin settings by exporting them them to a file (See General tab of the plugin). Please share the currently used plugin settings by exporting them them to a file (See General tab of the plugin).
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.

View File

@@ -104,12 +104,14 @@ AdvSceneSwitcher.macroTab.maximize="Maximize"
AdvSceneSwitcher.macroTab.minimize="Minimize" AdvSceneSwitcher.macroTab.minimize="Minimize"
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings" AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings" AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
AdvSceneSwitcher.macroTab.generalSettings="General settings"
AdvSceneSwitcher.macroTab.dockSettings="Dock settings" AdvSceneSwitcher.macroTab.dockSettings="Dock settings"
AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros" AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed macros"
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently" AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro" AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros" AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro" AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro"
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Skip execution of actions of current macro on startup"
AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually" AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually"
AdvSceneSwitcher.macroTab.currentDockAddRunButton="Add button to run the macro" AdvSceneSwitcher.macroTab.currentDockAddRunButton="Add button to run the macro"
AdvSceneSwitcher.macroTab.currentDockAddPauseButton="Add button to pause or unpause the macro" AdvSceneSwitcher.macroTab.currentDockAddPauseButton="Add button to pause or unpause the macro"

View File

@@ -40,7 +40,8 @@ static bool windowContainsText(const std::string &window,
return text == matchText; return text == matchText;
} }
bool MacroConditionWindow::WindowMatches(const std::string &window) bool MacroConditionWindow::WindowMatchesRequirements(
const std::string &window) const
{ {
const bool focusCheckOK = (!_focus || window == switcher->currentTitle); const bool focusCheckOK = (!_focus || window == switcher->currentTitle);
if (!focusCheckOK) { if (!focusCheckOK) {
@@ -60,26 +61,52 @@ bool MacroConditionWindow::WindowMatches(const std::string &window)
return false; return false;
} }
if (_checkText) {
auto text = GetTextInWindow(window);
SetVariableValue(text.value_or(""));
}
return true; return true;
} }
bool MacroConditionWindow::WindowMatches(
const std::vector<std::string> &windowList)
{
bool match = !_checkTitle ||
std::find(windowList.begin(), windowList.end(),
std::string(_window)) != windowList.end();
match = match && WindowMatchesRequirements(_window);
SetVariableValueBasedOnMatch(_window);
return match;
}
bool MacroConditionWindow::WindowRegexMatches( bool MacroConditionWindow::WindowRegexMatches(
const std::vector<std::string> &windowList) const std::vector<std::string> &windowList)
{ {
// No need to test if checking for window title is required as if the
// user has disabled window title matching the option will always be
// enabled in the backend and use the regular expression ".*".
for (const auto &window : windowList) { for (const auto &window : windowList) {
if (matchRegex(_windowRegex, window, _window) && if (matchRegex(_windowRegex, window, _window) &&
WindowMatches(window)) { WindowMatchesRequirements(window)) {
SetVariableValueBasedOnMatch(window);
return true; return true;
} }
} }
SetVariableValueBasedOnMatch("");
return false; return false;
} }
void MacroConditionWindow::SetVariableValueBasedOnMatch(
const std::string &matchWindow)
{
if (!IsReferencedInVars()) {
return;
}
if (_checkText) {
auto text = GetTextInWindow(matchWindow);
SetVariableValue(text.value_or(""));
} else {
SetVariableValue(switcher->currentTitle);
}
}
static bool foregroundWindowChanged() static bool foregroundWindowChanged()
{ {
return switcher->currentTitle != switcher->lastTitle; return switcher->currentTitle != switcher->lastTitle;
@@ -87,19 +114,13 @@ static bool foregroundWindowChanged()
bool MacroConditionWindow::CheckCondition() bool MacroConditionWindow::CheckCondition()
{ {
SetVariableValue("");
if (!_checkText) {
SetVariableValue(switcher->currentTitle);
}
std::vector<std::string> windowList; std::vector<std::string> windowList;
GetWindowList(windowList); GetWindowList(windowList);
bool match = false; bool match = false;
if (_windowRegex.Enabled()) { if (_windowRegex.Enabled()) {
match = WindowRegexMatches(windowList); match = WindowRegexMatches(windowList);
} else { } else {
match = WindowMatches(_window); match = WindowMatches(windowList);
} }
match = match && (!_windowFocusChanged || foregroundWindowChanged()); match = match && (!_windowFocusChanged || foregroundWindowChanged());
return match; return match;

View File

@@ -21,10 +21,6 @@ public:
return std::make_shared<MacroConditionWindow>(m); return std::make_shared<MacroConditionWindow>(m);
} }
private:
bool WindowMatches(const std::string &window);
bool WindowRegexMatches(const std::vector<std::string> &windowList);
public: public:
StringVariable _window; StringVariable _window;
RegexConfig _windowRegex; RegexConfig _windowRegex;
@@ -40,6 +36,11 @@ public:
RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig(); RegexConfig _textRegex = RegexConfig::PartialMatchRegexConfig();
private: private:
bool WindowMatchesRequirements(const std::string &window) const;
bool WindowMatches(const std::vector<std::string> &windowList);
bool WindowRegexMatches(const std::vector<std::string> &windowList);
void SetVariableValueBasedOnMatch(const std::string &matchWindow);
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };

View File

@@ -4,6 +4,8 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QScrollArea>
#include <QScrollBar>
namespace advss { namespace advss {
@@ -44,6 +46,8 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))), "AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text( _currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))), "AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
_currentSkipOnStartup(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
_currentMacroRegisterDock(new QCheckBox(obs_module_text( _currentMacroRegisterDock(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.macroTab.currentRegisterDock"))), "AdvSceneSwitcher.macroTab.currentRegisterDock"))),
_currentMacroDockAddRunButton(new QCheckBox(obs_module_text( _currentMacroDockAddRunButton(new QCheckBox(obs_module_text(
@@ -82,6 +86,12 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
hotkeyLayout->addWidget(_currentMacroRegisterHotkeys); hotkeyLayout->addWidget(_currentMacroRegisterHotkeys);
hotkeyOptions->setLayout(hotkeyLayout); hotkeyOptions->setLayout(hotkeyLayout);
auto generalOptions = new QGroupBox(
obs_module_text("AdvSceneSwitcher.macroTab.generalSettings"));
auto generalLayout = new QVBoxLayout;
generalLayout->addWidget(_currentSkipOnStartup);
generalOptions->setLayout(generalLayout);
int row = 0; int row = 0;
_dockLayout->addWidget(_currentMacroRegisterDock, row, 1, 1, 2); _dockLayout->addWidget(_currentMacroRegisterDock, row, 1, 1, 2);
row++; row++;
@@ -146,12 +156,23 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this, connect(_currentMacroDockAddStatusLabel, &QCheckBox::stateChanged, this,
&MacroPropertiesDialog::StatusLabelEnableChanged); &MacroPropertiesDialog::StatusLabelEnableChanged);
auto layout = new QVBoxLayout; auto scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setFrameShape(QFrame::NoFrame);
auto contentWidget = new QWidget(scrollArea);
auto layout = new QVBoxLayout(contentWidget);
layout->addWidget(highlightOptions); layout->addWidget(highlightOptions);
layout->addWidget(hotkeyOptions); layout->addWidget(hotkeyOptions);
layout->addWidget(generalOptions);
layout->addWidget(_dockOptions); layout->addWidget(_dockOptions);
layout->addWidget(buttonbox); layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout); scrollArea->setWidget(contentWidget);
auto dialogLayout = new QVBoxLayout();
dialogLayout->addWidget(scrollArea);
dialogLayout->addWidget(buttonbox);
setLayout(dialogLayout);
_executed->setChecked(prop._highlightExecuted); _executed->setChecked(prop._highlightExecuted);
_conditions->setChecked(prop._highlightConditions); _conditions->setChecked(prop._highlightConditions);
@@ -159,10 +180,12 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
_newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys); _newMacroRegisterHotkeys->setChecked(prop._newMacroRegisterHotkeys);
if (!macro || macro->IsGroup()) { if (!macro || macro->IsGroup()) {
hotkeyOptions->hide(); hotkeyOptions->hide();
generalOptions->hide();
_dockOptions->hide(); _dockOptions->hide();
return; return;
} }
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled()); _currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
const bool dockEnabled = macro->DockEnabled(); const bool dockEnabled = macro->DockEnabled();
_currentMacroRegisterDock->setChecked(dockEnabled); _currentMacroRegisterDock->setChecked(dockEnabled);
_currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton()); _currentMacroDockAddRunButton->setChecked(macro->DockHasRunButton());
@@ -194,6 +217,20 @@ MacroPropertiesDialog::MacroPropertiesDialog(QWidget *parent,
dockEnabled && macro->DockHasStatusLabel()); dockEnabled && macro->DockHasStatusLabel());
MinimizeSizeOfColumn(_dockLayout, 0); MinimizeSizeOfColumn(_dockLayout, 0);
Resize(); Resize();
// Try to set sensible initial size for the dialog window
QSize contentSize = contentWidget->sizeHint();
resize(contentSize.width() + layout->contentsMargins().left() +
layout->contentsMargins().right() +
dialogLayout->contentsMargins().left() +
dialogLayout->contentsMargins().right() +
scrollArea->verticalScrollBar()->sizeHint().width() + 20,
contentSize.height() + dialogLayout->spacing() +
buttonbox->sizeHint().height() +
dialogLayout->contentsMargins().top() +
dialogLayout->contentsMargins().bottom() +
scrollArea->horizontalScrollBar()->sizeHint().height() +
20);
} }
void MacroPropertiesDialog::DockEnableChanged(int enabled) void MacroPropertiesDialog::DockEnableChanged(int enabled)
@@ -244,8 +281,6 @@ void MacroPropertiesDialog::Resize()
{ {
_dockOptions->adjustSize(); _dockOptions->adjustSize();
_dockOptions->updateGeometry(); _dockOptions->updateGeometry();
adjustSize();
updateGeometry();
} }
bool MacroPropertiesDialog::AskForSettings(QWidget *parent, bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
@@ -268,6 +303,7 @@ bool MacroPropertiesDialog::AskForSettings(QWidget *parent,
macro->EnablePauseHotkeys( macro->EnablePauseHotkeys(
dialog._currentMacroRegisterHotkeys->isChecked()); dialog._currentMacroRegisterHotkeys->isChecked());
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
macro->EnableDock(dialog._currentMacroRegisterDock->isChecked()); macro->EnableDock(dialog._currentMacroRegisterDock->isChecked());
macro->SetDockHasRunButton( macro->SetDockHasRunButton(
dialog._currentMacroDockAddRunButton->isChecked()); dialog._currentMacroDockAddRunButton->isChecked());

View File

@@ -47,6 +47,7 @@ private:
QCheckBox *_newMacroRegisterHotkeys; QCheckBox *_newMacroRegisterHotkeys;
// Current macro specific settings // Current macro specific settings
QCheckBox *_currentMacroRegisterHotkeys; QCheckBox *_currentMacroRegisterHotkeys;
QCheckBox *_currentSkipOnStartup;
QCheckBox *_currentMacroRegisterDock; QCheckBox *_currentMacroRegisterDock;
QCheckBox *_currentMacroDockAddRunButton; QCheckBox *_currentMacroDockAddRunButton;
QCheckBox *_currentMacroDockAddPauseButton; QCheckBox *_currentMacroDockAddPauseButton;

View File

@@ -31,7 +31,7 @@ Macro::~Macro()
Stop(); Stop();
ClearHotkeys(); ClearHotkeys();
// Keep the dock widgets in case of shutdown so they can be rostored by // Keep the dock widgets in case of shutdown so they can be restored by
// OBS on startup // OBS on startup
if (!switcher->obsIsShuttingDown) { if (!switcher->obsIsShuttingDown) {
RemoveDock(); RemoveDock();
@@ -361,6 +361,7 @@ bool Macro::Save(obs_data_t *obj) const
obs_data_set_bool(obj, "pause", _paused); obs_data_set_bool(obj, "pause", _paused);
obs_data_set_bool(obj, "parallel", _runInParallel); obs_data_set_bool(obj, "parallel", _runInParallel);
obs_data_set_bool(obj, "onChange", _matchOnChange); obs_data_set_bool(obj, "onChange", _matchOnChange);
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
obs_data_set_bool(obj, "group", _isGroup); obs_data_set_bool(obj, "group", _isGroup);
if (_isGroup) { if (_isGroup) {
@@ -453,6 +454,7 @@ bool Macro::Load(obs_data_t *obj)
_paused = obs_data_get_bool(obj, "pause"); _paused = obs_data_get_bool(obj, "pause");
_runInParallel = obs_data_get_bool(obj, "parallel"); _runInParallel = obs_data_get_bool(obj, "parallel");
_matchOnChange = obs_data_get_bool(obj, "onChange"); _matchOnChange = obs_data_get_bool(obj, "onChange");
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
_isGroup = obs_data_get_bool(obj, "group"); _isGroup = obs_data_get_bool(obj, "group");
if (_isGroup) { if (_isGroup) {
@@ -1026,7 +1028,7 @@ bool SwitcherData::CheckMacros()
bool SwitcherData::RunMacros() bool SwitcherData::RunMacros()
{ {
// Create copy of macor list as elements might be removed, inserted, or // Create copy of macro list as elements might be removed, inserted, or
// reordered while macros are currently being executed. // reordered while macros are currently being executed.
// For example, this can happen if a macro is performing a wait action, // For example, this can happen if a macro is performing a wait action,
// as the main lock will be unlocked during this time. // as the main lock will be unlocked during this time.
@@ -1047,12 +1049,18 @@ bool SwitcherData::RunMacros()
} }
for (auto &m : runPhaseMacros) { for (auto &m : runPhaseMacros) {
if (m && m->Matched()) { if (!m || !m->Matched()) {
vblog(LOG_INFO, "running macro: %s", m->Name().c_str()); continue;
if (!m->PerformActions()) { }
blog(LOG_WARNING, "abort macro: %s", if (firstInterval && m->SkipExecOnStart()) {
m->Name().c_str()); blog(LOG_INFO,
} "skip execution of macro \"%s\" at startup",
m->Name().c_str());
continue;
}
vblog(LOG_INFO, "running macro: %s", m->Name().c_str());
if (!m->PerformActions()) {
blog(LOG_WARNING, "abort macro: %s", m->Name().c_str());
} }
} }
if (GetLock()) { if (GetLock()) {

View File

@@ -37,6 +37,8 @@ public:
bool Paused() const { return _paused; } bool Paused() const { return _paused; }
void SetMatchOnChange(bool onChange) { _matchOnChange = onChange; } void SetMatchOnChange(bool onChange) { _matchOnChange = onChange; }
bool MatchOnChange() const { return _matchOnChange; } bool MatchOnChange() const { return _matchOnChange; }
void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; }
bool SkipExecOnStart() const { return _skipExecOnStart; }
int RunCount() const { return _runCount; }; int RunCount() const { return _runCount; };
void ResetRunCount() { _runCount = 0; }; void ResetRunCount() { _runCount = 0; };
void ResetTimers(); void ResetTimers();
@@ -141,6 +143,7 @@ private:
bool _matched = false; bool _matched = false;
bool _lastMatched = false; bool _lastMatched = false;
bool _matchOnChange = true; bool _matchOnChange = true;
bool _skipExecOnStart = false;
bool _paused = false; bool _paused = false;
int _runCount = 0; int _runCount = 0;
bool _registerHotkeys = true; bool _registerHotkeys = true;

View File

@@ -83,11 +83,6 @@ const static std::map<tesseract::PageSegMode, std::string> pageSegModes = {
"AdvSceneSwitcher.condition.video.ocrMode.sparseTextOSD"}, "AdvSceneSwitcher.condition.video.ocrMode.sparseTextOSD"},
}; };
MacroConditionVideo::MacroConditionVideo(Macro *m) : MacroCondition(m, true)
{
SetupOpenCL();
}
cv::CascadeClassifier initObjectCascade(std::string &path) cv::CascadeClassifier initObjectCascade(std::string &path)
{ {
cv::CascadeClassifier cascade; cv::CascadeClassifier cascade;
@@ -266,7 +261,7 @@ bool MacroConditionVideo::SetLanguage(const std::string &language)
bool MacroConditionVideo::ScreenshotContainsPattern() bool MacroConditionVideo::ScreenshotContainsPattern()
{ {
cv::UMat result; cv::Mat result;
MatchPattern(_screenshotData.image, _patternImageData, MatchPattern(_screenshotData.image, _patternImageData,
_patternMatchParameters.threshold, result, _patternMatchParameters.threshold, result,
_patternMatchParameters.useAlphaAsMask, _patternMatchParameters.useAlphaAsMask,
@@ -279,16 +274,20 @@ bool MacroConditionVideo::ScreenshotContainsPattern()
bool MacroConditionVideo::OutputChanged() bool MacroConditionVideo::OutputChanged()
{ {
if (_patternMatchParameters.useForChangedCheck) { if (!_patternMatchParameters.useForChangedCheck) {
cv::UMat result; return _screenshotData.image != _matchImage;
_patternImageData = CreatePatternData(_matchImage);
MatchPattern(_screenshotData.image, _patternImageData,
_patternMatchParameters.threshold, result,
_patternMatchParameters.useAlphaAsMask,
_patternMatchParameters.matchMode);
return countNonZero(result) == 0;
} }
return _screenshotData.image != _matchImage;
cv::Mat result;
_patternImageData = CreatePatternData(_matchImage);
MatchPattern(_screenshotData.image, _patternImageData,
_patternMatchParameters.threshold, result,
_patternMatchParameters.useAlphaAsMask,
_patternMatchParameters.matchMode);
if (result.total() == 0) {
return false;
}
return countNonZero(result) == 0;
} }
bool MacroConditionVideo::ScreenshotContainsObject() bool MacroConditionVideo::ScreenshotContainsObject()

View File

@@ -25,7 +25,7 @@ class PreviewDialog;
class MacroConditionVideo : public MacroCondition { class MacroConditionVideo : public MacroCondition {
public: public:
MacroConditionVideo(Macro *m); MacroConditionVideo(Macro *m) : MacroCondition(m, true){};
bool CheckCondition(); bool CheckCondition();
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);

View File

@@ -1,8 +1,6 @@
#include "opencv-helpers.hpp" #include "opencv-helpers.hpp"
#include "log-helper.hpp"
#include <opencv2/core/ocl.hpp> #include <log-helper.hpp>
#include <opencv2/core/mat.hpp>
namespace advss { namespace advss {
@@ -24,19 +22,17 @@ PatternImageData CreatePatternData(const QImage &pattern)
return data; return data;
} }
static void invertPatternMatchResult(cv::UMat &umat) static void invertPatternMatchResult(cv::Mat &mat)
{ {
auto mat = umat.getMat(cv::ACCESS_RW);
for (int r = 0; r < mat.rows; r++) { for (int r = 0; r < mat.rows; r++) {
for (int c = 0; c < mat.cols; c++) { for (int c = 0; c < mat.cols; c++) {
mat.at<float>(r, c) = 1.0 - mat.at<float>(r, c); mat.at<float>(r, c) = 1.0 - mat.at<float>(r, c);
} }
} }
umat = mat.getUMat(cv::ACCESS_RW);
} }
void MatchPattern(QImage &img, const PatternImageData &patternData, void MatchPattern(QImage &img, const PatternImageData &patternData,
double threshold, cv::UMat &result, bool useAlphaAsMask, double threshold, cv::Mat &result, bool useAlphaAsMask,
cv::TemplateMatchModes matchMode) cv::TemplateMatchModes matchMode)
{ {
if (img.isNull() || patternData.rgbaPattern.empty()) { if (img.isNull() || patternData.rgbaPattern.empty()) {
@@ -55,12 +51,13 @@ void MatchPattern(QImage &img, const PatternImageData &patternData,
// thus should not be used while matching the pattern as well // thus should not be used while matching the pattern as well
// //
// Input format is Format_RGBA8888 so discard the 4th channel // Input format is Format_RGBA8888 so discard the 4th channel
std::vector<cv::UMat> inputChannels; std::vector<cv::Mat1b> inputChannels;
cv::split(input, inputChannels); cv::split(input, inputChannels);
std::vector<cv::UMat> rgbChanlesImage( std::vector<cv::Mat1b> rgbChanlesImage(
inputChannels.begin(), inputChannels.begin() + 3); inputChannels.begin(), inputChannels.begin() + 3);
cv::UMat rgbInput; cv::Mat3b rgbInput;
cv::merge(rgbChanlesImage, rgbInput); cv::merge(rgbChanlesImage, rgbInput);
cv::matchTemplate(rgbInput, patternData.rgbPattern, result, cv::matchTemplate(rgbInput, patternData.rgbPattern, result,
matchMode, patternData.mask); matchMode, patternData.mask);
} else { } else {
@@ -79,7 +76,7 @@ void MatchPattern(QImage &img, const PatternImageData &patternData,
} }
void MatchPattern(QImage &img, QImage &pattern, double threshold, void MatchPattern(QImage &img, QImage &pattern, double threshold,
cv::UMat &result, bool useAlphaAsMask, cv::Mat &result, bool useAlphaAsMask,
cv::TemplateMatchModes matchColor) cv::TemplateMatchModes matchColor)
{ {
auto data = CreatePatternData(pattern); auto data = CreatePatternData(pattern);
@@ -96,12 +93,16 @@ std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade,
} }
auto image = QImageToMat(img); auto image = QImageToMat(img);
cv::UMat frameGray; cv::Mat frameGray;
cv::cvtColor(image, frameGray, cv::COLOR_RGBA2GRAY); cv::cvtColor(image, frameGray, cv::COLOR_RGBA2GRAY);
cv::equalizeHist(frameGray, frameGray); cv::equalizeHist(frameGray, frameGray);
std::vector<cv::Rect> objects; std::vector<cv::Rect> objects;
cascade.detectMultiScale(frameGray, objects, scaleFactor, minNeighbors, try {
0, minSize, maxSize); cascade.detectMultiScale(frameGray, objects, scaleFactor,
minNeighbors, 0, minSize, maxSize);
} catch (const std::exception &e) {
vblog(LOG_INFO, "detectMultiScale failed: %s", e.what());
}
return objects; return objects;
} }
@@ -111,9 +112,9 @@ uchar GetAvgBrightness(QImage &img)
return 0; return 0;
} }
auto i = QImageToMat(img); auto image = QImageToMat(img);
cv::Mat hsvImage, rgbImage; cv::Mat hsvImage, rgbImage;
cv::cvtColor(i, rgbImage, cv::COLOR_RGBA2RGB); cv::cvtColor(image, rgbImage, cv::COLOR_RGBA2RGB);
cv::cvtColor(rgbImage, hsvImage, cv::COLOR_RGB2HSV); cv::cvtColor(rgbImage, hsvImage, cv::COLOR_RGB2HSV);
long long brightnessSum = 0; long long brightnessSum = 0;
for (int i = 0; i < hsvImage.rows; ++i) { for (int i = 0; i < hsvImage.rows; ++i) {
@@ -138,8 +139,7 @@ static bool colorIsSimilar(const QColor &color1, const QColor &color2,
cv::Mat PreprocessForOCR(const QImage &image, const QColor &textColor, cv::Mat PreprocessForOCR(const QImage &image, const QColor &textColor,
double colorDiff) double colorDiff)
{ {
auto umat = QImageToMat(image); auto mat = QImageToMat(image);
auto mat = umat.getMat(cv::ACCESS_RW);
// Tesseract works best when matching black text on a white background, // Tesseract works best when matching black text on a white background,
// so everything that matches the text color will be displayed black // so everything that matches the text color will be displayed black
@@ -224,14 +224,13 @@ bool ContainsPixelsInColorRange(const QImage &image, const QColor &color,
// Assumption is that QImage uses Format_RGBA8888. // Assumption is that QImage uses Format_RGBA8888.
// Conversion from: https://github.com/dbzhang800/QtOpenCV // Conversion from: https://github.com/dbzhang800/QtOpenCV
cv::UMat QImageToMat(const QImage &img) cv::Mat QImageToMat(const QImage &img)
{ {
if (img.isNull()) { if (img.isNull()) {
return cv::UMat(); return cv::Mat();
} }
auto temp = cv::Mat(img.height(), img.width(), CV_8UC(img.depth() / 8), return cv::Mat(img.height(), img.width(), CV_8UC(img.depth() / 8),
(uchar *)img.bits(), img.bytesPerLine()); (uchar *)img.bits(), img.bytesPerLine());
return temp.getUMat(cv::ACCESS_RW);
} }
QImage MatToQImage(const cv::Mat &mat) QImage MatToQImage(const cv::Mat &mat)
@@ -243,12 +242,4 @@ QImage MatToQImage(const cv::Mat &mat)
QImage::Format::Format_RGBA8888); QImage::Format::Format_RGBA8888);
} }
void SetupOpenCL()
{
if (cv::ocl::haveOpenCL() && !cv::ocl::useOpenCL()) {
blog(LOG_INFO, "enabled OpenCL support for OpenCV");
cv::ocl::setUseOpenCL(true);
}
}
} // namespace advss } // namespace advss

View File

@@ -42,17 +42,17 @@ constexpr int maxMinNeighbors = 6;
constexpr double defaultScaleFactor = 1.1; constexpr double defaultScaleFactor = 1.1;
struct PatternImageData { struct PatternImageData {
cv::UMat rgbaPattern; cv::Mat4b rgbaPattern;
cv::UMat rgbPattern; cv::Mat3b rgbPattern;
cv::UMat mask; cv::Mat1b mask;
}; };
PatternImageData CreatePatternData(const QImage &pattern); PatternImageData CreatePatternData(const QImage &pattern);
void MatchPattern(QImage &img, const PatternImageData &patternData, void MatchPattern(QImage &img, const PatternImageData &patternData,
double threshold, cv::UMat &result, bool useAlphaAsMask, double threshold, cv::Mat &result, bool useAlphaAsMask,
cv::TemplateMatchModes matchMode); cv::TemplateMatchModes matchMode);
void MatchPattern(QImage &img, QImage &pattern, double threshold, void MatchPattern(QImage &img, QImage &pattern, double threshold,
cv::UMat &result, bool useAlphaAsMask, cv::Mat &result, bool useAlphaAsMask,
cv::TemplateMatchModes matchMode); cv::TemplateMatchModes matchMode);
std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade, std::vector<cv::Rect> MatchObject(QImage &img, cv::CascadeClassifier &cascade,
double scaleFactor, int minNeighbors, double scaleFactor, int minNeighbors,
@@ -66,8 +66,7 @@ std::string RunOCR(tesseract::TessBaseAPI *, const QImage &, const QColor &,
bool ContainsPixelsInColorRange(const QImage &image, const QColor &color, bool ContainsPixelsInColorRange(const QImage &image, const QColor &color,
double colorDeviationThreshold, double colorDeviationThreshold,
double totalPixelMatchThreshold); double totalPixelMatchThreshold);
cv::UMat QImageToMat(const QImage &img); cv::Mat QImageToMat(const QImage &img);
QImage MatToQImage(const cv::Mat &mat); QImage MatToQImage(const cv::Mat &mat);
void SetupOpenCL();
} // namespace advss } // namespace advss

View File

@@ -252,7 +252,9 @@ OCRParameters::OCRParameters(const OCRParameters &other)
colorThreshold(other.colorThreshold), colorThreshold(other.colorThreshold),
pageSegMode(other.pageSegMode) pageSegMode(other.pageSegMode)
{ {
Setup(); if (!initDone) {
Setup();
}
if (initDone) { if (initDone) {
ocr->SetPageSegMode(pageSegMode); ocr->SetPageSegMode(pageSegMode);
} }
@@ -265,7 +267,12 @@ OCRParameters &OCRParameters::operator=(const OCRParameters &other)
color = other.color; color = other.color;
colorThreshold = other.colorThreshold; colorThreshold = other.colorThreshold;
pageSegMode = other.pageSegMode; pageSegMode = other.pageSegMode;
ocr->SetPageSegMode(pageSegMode); if (!initDone) {
Setup();
}
if (initDone) {
ocr->SetPageSegMode(pageSegMode);
}
return *this; return *this;
} }

View File

@@ -122,6 +122,7 @@ void PreviewDialog::PatternMatchParametersChanged(
{ {
std::unique_lock<std::mutex> lock(_mtx); std::unique_lock<std::mutex> lock(_mtx);
_patternMatchParams = params; _patternMatchParams = params;
_patternImageData = CreatePatternData(_patternMatchParams.image);
} }
void PreviewDialog::ObjDetectParametersChanged(const ObjDetectParameters &params) void PreviewDialog::ObjDetectParametersChanged(const ObjDetectParameters &params)
@@ -169,8 +170,8 @@ void PreviewDialog::UpdateImage(const QPixmap &image)
if (_type == PreviewType::SELECT_AREA && !_selectingArea) { if (_type == PreviewType::SELECT_AREA && !_selectingArea) {
DrawFrame(); DrawFrame();
} }
emit NeedImage(_video, _type, _patternMatchParams, _objDetectParams, emit NeedImage(_video, _type, _patternMatchParams, _patternImageData,
_ocrParams, _areaParams, _condition); _objDetectParams, _ocrParams, _areaParams, _condition);
} }
void PreviewDialog::Start() void PreviewDialog::Start()
@@ -186,7 +187,7 @@ void PreviewDialog::Start()
return; return;
} }
PreviewImage *worker = new PreviewImage(_mtx); auto worker = new PreviewImage(_mtx);
worker->moveToThread(&_thread); worker->moveToThread(&_thread);
connect(&_thread, &QThread::finished, worker, &QObject::deleteLater); connect(&_thread, &QThread::finished, worker, &QObject::deleteLater);
connect(worker, &PreviewImage::ImageReady, this, connect(worker, &PreviewImage::ImageReady, this,
@@ -197,8 +198,8 @@ void PreviewDialog::Start()
&PreviewImage::CreateImage); &PreviewImage::CreateImage);
_thread.start(); _thread.start();
emit NeedImage(_video, _type, _patternMatchParams, _objDetectParams, emit NeedImage(_video, _type, _patternMatchParams, _patternImageData,
_ocrParams, _areaParams, _condition); _objDetectParams, _ocrParams, _areaParams, _condition);
} }
void PreviewDialog::DrawFrame() void PreviewDialog::DrawFrame()
@@ -216,14 +217,13 @@ void PreviewDialog::DrawFrame()
_rubberBand->show(); _rubberBand->show();
} }
static void markPatterns(cv::UMat &matchResult, QImage &image, static void markPatterns(cv::Mat &matchResult, QImage &image,
const cv::UMat &pattern) const cv::Mat &pattern)
{ {
auto temp = matchResult.getMat(cv::ACCESS_RW);
auto matchImg = QImageToMat(image); auto matchImg = QImageToMat(image);
for (int row = 0; row < temp.rows - 1; row++) { for (int row = 0; row < matchResult.rows - 1; row++) {
for (int col = 0; col < temp.cols - 1; col++) { for (int col = 0; col < matchResult.cols - 1; col++) {
if (temp.at<float>(row, col) != 0.0) { if (matchResult.at<float>(row, col) != 0.0) {
rectangle(matchImg, {col, row}, rectangle(matchImg, {col, row},
cv::Point(col + pattern.cols, cv::Point(col + pattern.cols,
row + pattern.rows), row + pattern.rows),
@@ -231,7 +231,6 @@ static void markPatterns(cv::UMat &matchResult, QImage &image,
} }
} }
} }
matchResult = temp.getUMat(cv::ACCESS_RW);
} }
static void markObjects(QImage &image, std::vector<cv::Rect> &objects) static void markObjects(QImage &image, std::vector<cv::Rect> &objects)
@@ -249,6 +248,7 @@ PreviewImage::PreviewImage(std::mutex &mtx) : _mtx(mtx) {}
void PreviewImage::CreateImage(const VideoInput &video, PreviewType type, void PreviewImage::CreateImage(const VideoInput &video, PreviewType type,
const PatternMatchParameters &patternMatchParams, const PatternMatchParameters &patternMatchParams,
const PatternImageData &patternImageData,
ObjDetectParameters objDetectParams, ObjDetectParameters objDetectParams,
OCRParameters ocrParams, OCRParameters ocrParams,
const AreaParameters &areaParams, const AreaParameters &areaParams,
@@ -279,8 +279,6 @@ void PreviewImage::CreateImage(const VideoInput &video, PreviewType type,
areaParams.area.x, areaParams.area.y, areaParams.area.x, areaParams.area.y,
areaParams.area.width, areaParams.area.height); areaParams.area.width, areaParams.area.height);
} }
const auto patternImageData =
CreatePatternData(patternMatchParams.image);
// Will emit status label update // Will emit status label update
MarkMatch(screenshot.image, patternMatchParams, MarkMatch(screenshot.image, patternMatchParams,
patternImageData, objDetectParams, ocrParams, patternImageData, objDetectParams, ocrParams,
@@ -299,7 +297,7 @@ void PreviewImage::MarkMatch(QImage &screenshot,
VideoCondition condition) VideoCondition condition)
{ {
if (condition == VideoCondition::PATTERN) { if (condition == VideoCondition::PATTERN) {
cv::UMat result; cv::Mat result;
MatchPattern(screenshot, patternImageData, MatchPattern(screenshot, patternImageData,
patternMatchParams.threshold, result, patternMatchParams.threshold, result,
patternMatchParams.useAlphaAsMask, patternMatchParams.useAlphaAsMask,

View File

@@ -25,7 +25,8 @@ public:
public slots: public slots:
void CreateImage(const VideoInput &, PreviewType, void CreateImage(const VideoInput &, PreviewType,
const PatternMatchParameters &, ObjDetectParameters, const PatternMatchParameters &,
const PatternImageData &, ObjDetectParameters,
OCRParameters, const AreaParameters &, VideoCondition); OCRParameters, const AreaParameters &, VideoCondition);
signals: signals:
void ImageReady(const QPixmap &); void ImageReady(const QPixmap &);
@@ -63,8 +64,9 @@ private slots:
signals: signals:
void SelectionAreaChanged(QRect area); void SelectionAreaChanged(QRect area);
void NeedImage(const VideoInput &, PreviewType, void NeedImage(const VideoInput &, PreviewType,
const PatternMatchParameters &, ObjDetectParameters, const PatternMatchParameters &, const PatternImageData &,
OCRParameters, const AreaParameters &, VideoCondition); ObjDetectParameters, OCRParameters,
const AreaParameters &, VideoCondition);
private: private:
void Start(); void Start();
@@ -76,6 +78,7 @@ private:
VideoInput _video; VideoInput _video;
PatternMatchParameters _patternMatchParams; PatternMatchParameters _patternMatchParams;
PatternImageData _patternImageData;
ObjDetectParameters _objDetectParams; ObjDetectParameters _objDetectParams;
OCRParameters _ocrParams; OCRParameters _ocrParams;
AreaParameters _areaParams; AreaParameters _areaParams;

View File

@@ -510,7 +510,8 @@ bool DisplayMessage(const QString &msg, bool question, bool modal)
return (answer == QMessageBox::Yes); return (answer == QMessageBox::Yes);
} else if (question && modal) { } else if (question && modal) {
auto answer = QMessageBox::question( auto answer = QMessageBox::question(
nullptr, static_cast<QMainWindow *>(
obs_frontend_get_main_window()),
obs_module_text("AdvSceneSwitcher.windowTitle"), msg, obs_module_text("AdvSceneSwitcher.windowTitle"), msg,
QMessageBox::Yes | QMessageBox::No); QMessageBox::Yes | QMessageBox::No);
return answer == QMessageBox::Yes; return answer == QMessageBox::Yes;
@@ -842,11 +843,9 @@ QStringList GetMediaSourceNames()
auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */ auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */
{ {
QStringList *list = reinterpret_cast<QStringList *>(param); QStringList *list = reinterpret_cast<QStringList *>(param);
std::string sourceId = obs_source_get_id(source); uint32_t flags = obs_source_get_output_flags(source);
if (sourceId.compare("ffmpeg_source") == 0 ||
sourceId.compare("vlc_source") == 0 || if ((flags & OBS_SOURCE_CONTROLLABLE_MEDIA) != 0) {
sourceId.compare("slideshow") == 0 ||
sourceId.compare("media_playlist_source_codeyan") == 0) {
*list << obs_source_get_name(source); *list << obs_source_get_name(source);
} }
return true; return true;