mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-04-21 01:27:24 -05:00
Improve video condition and preview dialog
* Fix crashes in preview dialog * Pass copy of parameters instead of working directly with condition data * Set sensible default paths in file selection dialogs * Add maximize button to preview dialog * Derive preview dialog size from scene switcher settings window * Use parameter helper classes
This commit is contained in:
parent
b7c6deddde
commit
70c9a3bd83
|
|
@ -106,7 +106,7 @@ bool MacroConditionVideo::CheckCondition()
|
|||
return match;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Save(obs_data_t *obj)
|
||||
bool MacroConditionVideo::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_video.Save(obj);
|
||||
|
|
@ -114,34 +114,15 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
|
|||
obs_data_set_string(obj, "filePath", _file.c_str());
|
||||
obs_data_set_bool(obj, "blockUntilScreenshotDone",
|
||||
_blockUntilScreenshotDone);
|
||||
obs_data_set_bool(obj, "usePatternForChangedCheck",
|
||||
_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);
|
||||
_minSize.Save(obj, "minSize");
|
||||
_maxSize.Save(obj, "maxSize");
|
||||
_patternMatchParameters.Save(obj);
|
||||
_objMatchParameters.Save(obj);
|
||||
obs_data_set_bool(obj, "throttleEnabled", _throttleEnabled);
|
||||
obs_data_set_int(obj, "throttleCount", _throttleCount);
|
||||
obs_data_set_bool(obj, "checkAreaEnabled", _checkAreaEnable);
|
||||
_checkArea.Save(obj, "checkArea");
|
||||
_areaParameters.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isScaleFactorValid(double scaleFactor)
|
||||
{
|
||||
return scaleFactor > 1.;
|
||||
}
|
||||
|
||||
bool isMinNeighborsValid(int minNeighbors)
|
||||
{
|
||||
return minNeighbors >= minMinNeighbors &&
|
||||
minNeighbors <= maxMinNeighbors;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
|
|
@ -154,46 +135,24 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
|
|||
_file = obs_data_get_string(obj, "filePath");
|
||||
_blockUntilScreenshotDone =
|
||||
obs_data_get_bool(obj, "blockUntilScreenshotDone");
|
||||
_usePatternForChangedCheck =
|
||||
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)) {
|
||||
_scaleFactor = 1.1;
|
||||
}
|
||||
_minNeighbors = obs_data_get_int(obj, "minNeighbors");
|
||||
if (!isMinNeighborsValid(_minNeighbors)) {
|
||||
_minNeighbors = minMinNeighbors;
|
||||
}
|
||||
// TODO: Remove this fallback in future version
|
||||
if (obs_data_has_user_value(obj, "minSizeX")) {
|
||||
_minSize.width = obs_data_get_int(obj, "minSizeX");
|
||||
_minSize.height = obs_data_get_int(obj, "minSizeY");
|
||||
_maxSize.width = obs_data_get_int(obj, "maxSizeX");
|
||||
_maxSize.height = obs_data_get_int(obj, "maxSizeY");
|
||||
} else {
|
||||
_minSize.Load(obj, "minSize");
|
||||
_maxSize.Load(obj, "maxSize");
|
||||
}
|
||||
_patternMatchParameters.Load(obj);
|
||||
_objMatchParameters.Load(obj);
|
||||
_throttleEnabled = obs_data_get_bool(obj, "throttleEnabled");
|
||||
_throttleCount = obs_data_get_int(obj, "throttleCount");
|
||||
_checkAreaEnable = obs_data_get_bool(obj, "checkAreaEnabled");
|
||||
_checkArea.Load(obj, "checkArea");
|
||||
_areaParameters.Load(obj);
|
||||
if (requiresFileInput(_condition)) {
|
||||
(void)LoadImageFromFile();
|
||||
}
|
||||
|
||||
if (_condition == VideoCondition::OBJECT) {
|
||||
LoadModelData(_modelDataPath);
|
||||
LoadModelData(_objMatchParameters.modelPath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionVideo::GetShortDesc()
|
||||
std::string MacroConditionVideo::GetShortDesc() const
|
||||
{
|
||||
return _video.ToString();
|
||||
}
|
||||
|
|
@ -215,37 +174,46 @@ bool MacroConditionVideo::LoadImageFromFile()
|
|||
_file.c_str());
|
||||
(&_matchImage)->~QImage();
|
||||
new (&_matchImage) QImage();
|
||||
_patternImageData = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
_matchImage =
|
||||
_matchImage.convertToFormat(QImage::Format::Format_RGBA8888);
|
||||
_patternData = createPatternData(_matchImage);
|
||||
_patternMatchParameters.image = _matchImage;
|
||||
_patternImageData = createPatternData(_matchImage);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::LoadModelData(std::string &path)
|
||||
{
|
||||
_modelDataPath = path;
|
||||
_objectCascade = initObjectCascade(path);
|
||||
return !_objectCascade.empty();
|
||||
_objMatchParameters.modelPath = path;
|
||||
_objMatchParameters.cascade = initObjectCascade(path);
|
||||
return !_objMatchParameters.cascade.empty();
|
||||
}
|
||||
|
||||
std::string MacroConditionVideo::GetModelDataPath() const
|
||||
{
|
||||
return _objMatchParameters.modelPath;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::ScreenshotContainsPattern()
|
||||
{
|
||||
cv::Mat result;
|
||||
matchPattern(_screenshotData.image, _patternData, _patternThreshold,
|
||||
result, _useAlphaAsMask);
|
||||
matchPattern(_screenshotData.image, _patternImageData,
|
||||
_patternMatchParameters.threshold, result,
|
||||
_patternMatchParameters.useAlphaAsMask);
|
||||
return countNonZero(result) > 0;
|
||||
}
|
||||
|
||||
bool MacroConditionVideo::OutputChanged()
|
||||
{
|
||||
if (_usePatternForChangedCheck) {
|
||||
if (_patternMatchParameters.useForChangedCheck) {
|
||||
cv::Mat result;
|
||||
_patternData = createPatternData(_matchImage);
|
||||
matchPattern(_screenshotData.image, _patternData,
|
||||
_patternThreshold, result, _useAlphaAsMask);
|
||||
_patternImageData = createPatternData(_matchImage);
|
||||
matchPattern(_screenshotData.image, _patternImageData,
|
||||
_patternMatchParameters.threshold, result,
|
||||
_patternMatchParameters.useAlphaAsMask);
|
||||
return countNonZero(result) == 0;
|
||||
}
|
||||
return _screenshotData.image != _matchImage;
|
||||
|
|
@ -253,9 +221,12 @@ bool MacroConditionVideo::OutputChanged()
|
|||
|
||||
bool MacroConditionVideo::ScreenshotContainsObject()
|
||||
{
|
||||
auto objects = matchObject(_screenshotData.image, _objectCascade,
|
||||
_scaleFactor, _minNeighbors, _minSize.CV(),
|
||||
_maxSize.CV());
|
||||
auto objects = matchObject(_screenshotData.image,
|
||||
_objMatchParameters.cascade,
|
||||
_objMatchParameters.scaleFactor,
|
||||
_objMatchParameters.minNeighbors,
|
||||
_objMatchParameters.minSize.CV(),
|
||||
_objMatchParameters.maxSize.CV());
|
||||
return objects.size() > 0;
|
||||
}
|
||||
|
||||
|
|
@ -267,10 +238,11 @@ bool MacroConditionVideo::CheckBrightnessThreshold()
|
|||
|
||||
bool MacroConditionVideo::Compare()
|
||||
{
|
||||
if (_checkAreaEnable && _condition != VideoCondition::NO_IMAGE) {
|
||||
if (_areaParameters.enable && _condition != VideoCondition::NO_IMAGE) {
|
||||
_screenshotData.image = _screenshotData.image.copy(
|
||||
_checkArea.x, _checkArea.y, _checkArea.width,
|
||||
_checkArea.height);
|
||||
_areaParameters.area.x, _areaParameters.area.y,
|
||||
_areaParameters.area.width,
|
||||
_areaParameters.area.height);
|
||||
}
|
||||
|
||||
switch (_condition) {
|
||||
|
|
@ -354,7 +326,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||
_throttleCount(new QSpinBox()),
|
||||
_showMatch(new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.showMatch"))),
|
||||
_previewDialog(this, entryData.get(), &GetSwitcher()->m)
|
||||
_previewDialog(this, GetSwitcher()->interval)
|
||||
{
|
||||
_reduceLatency->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.reduceLatency.tooltip"));
|
||||
|
|
@ -411,6 +383,12 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||
SLOT(ShowMatchClicked()));
|
||||
QWidget::connect(&_previewDialog, SIGNAL(SelectionAreaChanged(QRect)),
|
||||
this, SLOT(CheckAreaChanged(QRect)));
|
||||
QWidget::connect(_videoSelection,
|
||||
SIGNAL(VideoSelectionChange(const VideoSelection &)),
|
||||
&_previewDialog,
|
||||
SLOT(VideoSelectionChanged(const VideoSelection &)));
|
||||
QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)),
|
||||
&_previewDialog, SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_selectArea, SIGNAL(clicked()), this,
|
||||
SLOT(SelectAreaClicked()));
|
||||
|
||||
|
|
@ -553,10 +531,13 @@ void MacroConditionVideoEdit::ConditionChanged(int cond)
|
|||
if (_entryData->LoadImageFromFile()) {
|
||||
UpdatePreviewTooltip();
|
||||
}
|
||||
_previewDialog.PatternMatchParamtersChanged(
|
||||
_entryData->_patternMatchParameters);
|
||||
|
||||
if (_entryData->_condition == VideoCondition::OBJECT) {
|
||||
auto path = _entryData->GetModelDataPath();
|
||||
_entryData->_objectCascade = initObjectCascade(path);
|
||||
_entryData->_objMatchParameters.cascade =
|
||||
initObjectCascade(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -572,6 +553,8 @@ void MacroConditionVideoEdit::ImagePathChanged(const QString &text)
|
|||
if (_entryData->LoadImageFromFile()) {
|
||||
UpdatePreviewTooltip();
|
||||
}
|
||||
_previewDialog.PatternMatchParamtersChanged(
|
||||
_entryData->_patternMatchParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ImageBrowseButtonClicked()
|
||||
|
|
@ -618,7 +601,10 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
|
|||
}
|
||||
|
||||
if (useExistingFile) {
|
||||
path = QFileDialog::getOpenFileName(this);
|
||||
path = QFileDialog::getOpenFileName(
|
||||
this, "",
|
||||
FileSelection::ValidPathOrDesktop(
|
||||
QString::fromStdString(_entryData->_file)));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -629,7 +615,11 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
|
|||
ScreenshotHelper screenshot(source);
|
||||
obs_source_release(source);
|
||||
|
||||
path = QFileDialog::getSaveFileName(this, "", "", "*.png");
|
||||
path = QFileDialog::getSaveFileName(
|
||||
this, "",
|
||||
FileSelection::ValidPathOrDesktop(
|
||||
QString::fromStdString(_entryData->_file)),
|
||||
"*.png");
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -645,12 +635,12 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
|
|||
"AdvSceneSwitcher.condition.video.screenshotFail"));
|
||||
return;
|
||||
}
|
||||
if (_entryData->_checkAreaEnable) {
|
||||
if (_entryData->_areaParameters.enable) {
|
||||
screenshot.image = screenshot.image.copy(
|
||||
_entryData->_checkArea.x,
|
||||
_entryData->_checkArea.y,
|
||||
_entryData->_checkArea.width,
|
||||
_entryData->_checkArea.height);
|
||||
_entryData->_areaParameters.area.x,
|
||||
_entryData->_areaParameters.area.y,
|
||||
_entryData->_areaParameters.area.width,
|
||||
_entryData->_areaParameters.area.height);
|
||||
}
|
||||
screenshot.image.save(path);
|
||||
}
|
||||
|
|
@ -665,7 +655,7 @@ void MacroConditionVideoEdit::UsePatternForChangedCheckChanged(int value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_usePatternForChangedCheck = value;
|
||||
_entryData->_patternMatchParameters.useForChangedCheck = value;
|
||||
_patternThreshold->setVisible(value);
|
||||
adjustSize();
|
||||
}
|
||||
|
|
@ -677,7 +667,9 @@ void MacroConditionVideoEdit::PatternThresholdChanged(double value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_patternThreshold = value;
|
||||
_entryData->_patternMatchParameters.threshold = value;
|
||||
_previewDialog.PatternMatchParamtersChanged(
|
||||
_entryData->_patternMatchParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::ReduceLatencyChanged(int value)
|
||||
|
|
@ -697,8 +689,10 @@ void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_useAlphaAsMask = value;
|
||||
_entryData->_patternMatchParameters.useAlphaAsMask = value;
|
||||
_entryData->LoadImageFromFile();
|
||||
_previewDialog.PatternMatchParamtersChanged(
|
||||
_entryData->_patternMatchParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::BrightnessThresholdChanged(double value)
|
||||
|
|
@ -718,7 +712,9 @@ void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_scaleFactor = value;
|
||||
_entryData->_objMatchParameters.scaleFactor = value;
|
||||
_previewDialog.ObjDetectParamtersChanged(
|
||||
_entryData->_objMatchParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::MinNeighborsChanged(int value)
|
||||
|
|
@ -728,7 +724,9 @@ void MacroConditionVideoEdit::MinNeighborsChanged(int value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_minNeighbors = value;
|
||||
_entryData->_objMatchParameters.minNeighbors = value;
|
||||
_previewDialog.ObjDetectParamtersChanged(
|
||||
_entryData->_objMatchParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::MinSizeChanged(advss::Size value)
|
||||
|
|
@ -738,7 +736,9 @@ void MacroConditionVideoEdit::MinSizeChanged(advss::Size value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_minSize = value;
|
||||
_entryData->_objMatchParameters.minSize = value;
|
||||
_previewDialog.ObjDetectParamtersChanged(
|
||||
_entryData->_objMatchParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::MaxSizeChanged(advss::Size value)
|
||||
|
|
@ -748,7 +748,9 @@ void MacroConditionVideoEdit::MaxSizeChanged(advss::Size value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_maxSize = value;
|
||||
_entryData->_objMatchParameters.maxSize = value;
|
||||
_previewDialog.ObjDetectParamtersChanged(
|
||||
_entryData->_objMatchParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::CheckAreaEnableChanged(int value)
|
||||
|
|
@ -758,12 +760,13 @@ void MacroConditionVideoEdit::CheckAreaEnableChanged(int value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_checkAreaEnable = value;
|
||||
_entryData->_areaParameters.enable = value;
|
||||
_checkArea->setEnabled(value);
|
||||
_selectArea->setEnabled(value);
|
||||
_checkArea->setVisible(value);
|
||||
_selectArea->setVisible(value);
|
||||
adjustSize();
|
||||
_previewDialog.AreaParamtersChanged(_entryData->_areaParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::CheckAreaChanged(advss::Area value)
|
||||
|
|
@ -773,7 +776,8 @@ void MacroConditionVideoEdit::CheckAreaChanged(advss::Area value)
|
|||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
|
||||
_entryData->_checkArea = value;
|
||||
_entryData->_areaParameters.area = value;
|
||||
_previewDialog.AreaParamtersChanged(_entryData->_areaParameters);
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::CheckAreaChanged(QRect rect)
|
||||
|
|
@ -908,13 +912,13 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
|
|||
needsThrottleControls(_entryData->_condition));
|
||||
setLayoutVisible(_checkAreaControlLayout,
|
||||
needsAreaControls(_entryData->_condition));
|
||||
_checkArea->setVisible(_entryData->_checkAreaEnable);
|
||||
_selectArea->setVisible(_entryData->_checkAreaEnable);
|
||||
_checkArea->setVisible(_entryData->_areaParameters.enable);
|
||||
_selectArea->setVisible(_entryData->_areaParameters.enable);
|
||||
|
||||
if (_entryData->_condition == VideoCondition::HAS_CHANGED ||
|
||||
_entryData->_condition == VideoCondition::HAS_NOT_CHANGED) {
|
||||
_patternThreshold->setVisible(
|
||||
_entryData->_usePatternForChangedCheck);
|
||||
_entryData->_patternMatchParameters.useForChangedCheck);
|
||||
}
|
||||
|
||||
adjustSize();
|
||||
|
|
@ -931,20 +935,33 @@ void MacroConditionVideoEdit::UpdateEntryData()
|
|||
_reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone);
|
||||
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||
_usePatternForChangedCheck->setChecked(
|
||||
_entryData->_usePatternForChangedCheck);
|
||||
_patternThreshold->SetDoubleValue(_entryData->_patternThreshold);
|
||||
_useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask);
|
||||
_entryData->_patternMatchParameters.useForChangedCheck);
|
||||
_patternThreshold->SetDoubleValue(
|
||||
_entryData->_patternMatchParameters.threshold);
|
||||
_useAlphaAsMask->setChecked(
|
||||
_entryData->_patternMatchParameters.useAlphaAsMask);
|
||||
_brightnessThreshold->SetDoubleValue(_entryData->_brightnessThreshold);
|
||||
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
|
||||
_objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor);
|
||||
_minNeighbors->setValue(_entryData->_minNeighbors);
|
||||
_minSize->SetSize(_entryData->_minSize);
|
||||
_maxSize->SetSize(_entryData->_maxSize);
|
||||
_objectScaleThreshold->SetDoubleValue(
|
||||
_entryData->_objMatchParameters.scaleFactor);
|
||||
_minNeighbors->setValue(_entryData->_objMatchParameters.minNeighbors);
|
||||
_minSize->SetSize(_entryData->_objMatchParameters.minSize);
|
||||
_maxSize->SetSize(_entryData->_objMatchParameters.maxSize);
|
||||
_throttleEnable->setChecked(_entryData->_throttleEnabled);
|
||||
_throttleCount->setValue(_entryData->_throttleCount *
|
||||
GetSwitcher()->interval);
|
||||
_checkAreaEnable->setChecked(_entryData->_checkAreaEnable);
|
||||
_checkArea->SetArea(_entryData->_checkArea);
|
||||
_checkAreaEnable->setChecked(_entryData->_areaParameters.enable);
|
||||
_checkArea->SetArea(_entryData->_areaParameters.area);
|
||||
UpdatePreviewTooltip();
|
||||
SetWidgetVisibility();
|
||||
|
||||
// Prepare previewDialog
|
||||
_previewDialog.PatternMatchParamtersChanged(
|
||||
_entryData->_patternMatchParameters);
|
||||
_previewDialog.ObjDetectParamtersChanged(
|
||||
_entryData->_objMatchParameters);
|
||||
_previewDialog.VideoSelectionChanged(_entryData->_video);
|
||||
_previewDialog.AreaParamtersChanged(_entryData->_areaParameters);
|
||||
_previewDialog.ConditionChanged(
|
||||
static_cast<int>(_entryData->_condition));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#pragma once
|
||||
#include "opencv-helpers.hpp"
|
||||
#include "threshold-slider.hpp"
|
||||
#include "preview-dialog.hpp"
|
||||
#include "area-selection.hpp"
|
||||
#include "video-selection.hpp"
|
||||
#include "preview-dialog.hpp"
|
||||
#include "paramerter-wrappers.hpp"
|
||||
|
||||
#include <macro.hpp>
|
||||
#include <file-selection.hpp>
|
||||
|
|
@ -17,36 +18,27 @@
|
|||
#include <QLabel>
|
||||
#include <QRect>
|
||||
|
||||
enum class VideoCondition {
|
||||
MATCH,
|
||||
DIFFER,
|
||||
HAS_NOT_CHANGED,
|
||||
HAS_CHANGED,
|
||||
NO_IMAGE,
|
||||
PATTERN,
|
||||
OBJECT,
|
||||
BRIGHTNESS,
|
||||
};
|
||||
class PreviewDialog;
|
||||
|
||||
class MacroConditionVideo : public MacroCondition {
|
||||
public:
|
||||
MacroConditionVideo(Macro *m) : MacroCondition(m) {}
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
QImage GetMatchImage() { return _matchImage; };
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionVideo>(m);
|
||||
}
|
||||
QImage GetMatchImage() const { return _matchImage; };
|
||||
void GetScreenshot(bool blocking = false);
|
||||
bool LoadImageFromFile();
|
||||
bool LoadModelData(std::string &path);
|
||||
std::string GetModelDataPath() { return _modelDataPath; }
|
||||
std::string GetModelDataPath() const;
|
||||
void ResetLastMatch() { _lastMatchResult = false; }
|
||||
double GetCurrentBrightness() { return _currentBrigthness; }
|
||||
double GetCurrentBrightness() const { return _currentBrigthness; }
|
||||
|
||||
VideoSelection _video;
|
||||
VideoCondition _condition = VideoCondition::MATCH;
|
||||
|
|
@ -58,20 +50,10 @@ public:
|
|||
// checked in the next one.
|
||||
// If set both operations will happen in the same interval.
|
||||
bool _blockUntilScreenshotDone = false;
|
||||
bool _useAlphaAsMask = false;
|
||||
bool _usePatternForChangedCheck = false;
|
||||
PatternMatchData _patternData;
|
||||
double _patternThreshold = 0.8;
|
||||
double _brightnessThreshold = 0.5;
|
||||
cv::CascadeClassifier _objectCascade;
|
||||
double _scaleFactor = 1.1;
|
||||
int _minNeighbors = minMinNeighbors;
|
||||
advss::Size _minSize{0, 0};
|
||||
advss::Size _maxSize{0, 0};
|
||||
|
||||
bool _checkAreaEnable = false;
|
||||
advss::Area _checkArea{0, 0, 0, 0};
|
||||
|
||||
PatternMatchParameters _patternMatchParameters;
|
||||
ObjDetectParamerts _objMatchParameters;
|
||||
AreaParamters _areaParameters;
|
||||
bool _throttleEnabled = false;
|
||||
int _throttleCount = 3;
|
||||
|
||||
|
|
@ -86,10 +68,8 @@ private:
|
|||
bool _getNextScreenshot = true;
|
||||
ScreenshotHelper _screenshotData;
|
||||
QImage _matchImage;
|
||||
std::string _modelDataPath =
|
||||
obs_get_module_data_path(obs_current_module()) +
|
||||
std::string(
|
||||
"/res/cascadeClassifiers/haarcascade_frontalface_alt.xml");
|
||||
PatternImageData _patternImageData;
|
||||
|
||||
bool _lastMatchResult = false;
|
||||
int _runCount = 0;
|
||||
double _currentBrigthness = 0.;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#include "opencv-helpers.hpp"
|
||||
|
||||
PatternMatchData createPatternData(QImage &pattern)
|
||||
PatternImageData createPatternData(QImage &pattern)
|
||||
{
|
||||
PatternMatchData data{};
|
||||
PatternImageData data{};
|
||||
if (pattern.isNull()) {
|
||||
return data;
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ PatternMatchData createPatternData(QImage &pattern)
|
|||
return data;
|
||||
}
|
||||
|
||||
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
|
||||
void matchPattern(QImage &img, PatternImageData &patternData, double threshold,
|
||||
cv::Mat &result, bool useAlphaAsMask)
|
||||
{
|
||||
if (img.isNull() || patternData.rgbaPattern.empty()) {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@
|
|||
|
||||
constexpr int minMinNeighbors = 3;
|
||||
constexpr int maxMinNeighbors = 6;
|
||||
constexpr double defaultScaleFactor = 1.1;
|
||||
|
||||
struct PatternMatchData {
|
||||
struct PatternImageData {
|
||||
cv::Mat4b rgbaPattern;
|
||||
cv::Mat3b rgbPattern;
|
||||
cv::Mat1b mask;
|
||||
};
|
||||
|
||||
PatternMatchData createPatternData(QImage &pattern);
|
||||
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
|
||||
PatternImageData createPatternData(QImage &pattern);
|
||||
void matchPattern(QImage &img, PatternImageData &patternData, double threshold,
|
||||
cv::Mat &result, bool useAlphaAsMask = true);
|
||||
void matchPattern(QImage &img, QImage &pattern, double threshold,
|
||||
cv::Mat &result, bool useAlphaAsMask);
|
||||
|
|
|
|||
|
|
@ -1,27 +1,41 @@
|
|||
#include "preview-dialog.hpp"
|
||||
#include "macro-condition-video.hpp"
|
||||
|
||||
#include "opencv-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <screenshot-helper.hpp>
|
||||
#include <condition_variable>
|
||||
|
||||
PreviewDialog::PreviewDialog(QWidget *parent,
|
||||
MacroConditionVideo *conditionData,
|
||||
std::mutex *mutex)
|
||||
PreviewDialog::PreviewDialog(QWidget *parent, int delay)
|
||||
: QDialog(parent),
|
||||
_conditionData(conditionData),
|
||||
_scrollArea(new QScrollArea),
|
||||
_imageLabel(new QLabel(this)),
|
||||
_rubberBand(new QRubberBand(QRubberBand::Rectangle, this)),
|
||||
_mtx(mutex)
|
||||
_delay(delay)
|
||||
{
|
||||
setWindowTitle("Advanced Scene Switcher");
|
||||
setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint |
|
||||
Qt::WindowCloseButtonHint);
|
||||
|
||||
_statusLabel = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.showMatch.loading"));
|
||||
resize(640, 480);
|
||||
resize(parent->window()->size());
|
||||
|
||||
// Center image
|
||||
auto wrapper = new QWidget();
|
||||
auto wrapperHLayout = new QHBoxLayout();
|
||||
wrapperHLayout->addStretch();
|
||||
wrapperHLayout->addWidget(_imageLabel);
|
||||
wrapperHLayout->addStretch();
|
||||
auto wrapperVLayout = new QVBoxLayout();
|
||||
wrapperVLayout->addStretch();
|
||||
wrapperVLayout->addLayout(wrapperHLayout);
|
||||
wrapperVLayout->addStretch();
|
||||
wrapper->setLayout(wrapperVLayout);
|
||||
|
||||
_scrollArea->setBackgroundRole(QPalette::Dark);
|
||||
_scrollArea->setWidget(_imageLabel);
|
||||
_scrollArea->setWidget(wrapper);
|
||||
_scrollArea->setWidgetResizable(true);
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(_statusLabel);
|
||||
layout->addWidget(_scrollArea);
|
||||
|
|
@ -32,9 +46,9 @@ PreviewDialog::PreviewDialog(QWidget *parent,
|
|||
// Q_ARG(QImage, image));
|
||||
// from within CheckForMatchLoop().
|
||||
// Even using BlockingQueuedConnection causes deadlocks
|
||||
_timer.setInterval(500);
|
||||
_timer.setInterval(300);
|
||||
QWidget::connect(&_timer, &QTimer::timeout, this,
|
||||
&PreviewDialog::Resize);
|
||||
&PreviewDialog::ResizeImageLabel);
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
|
|
@ -82,10 +96,7 @@ void PreviewDialog::mouseReleaseEvent(QMouseEvent *)
|
|||
|
||||
PreviewDialog::~PreviewDialog()
|
||||
{
|
||||
_stop = true;
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
Stop();
|
||||
}
|
||||
|
||||
void PreviewDialog::ShowMatch()
|
||||
|
|
@ -105,7 +116,55 @@ void PreviewDialog::SelectArea()
|
|||
"AdvSceneSwitcher.condition.video.selectArea.status"));
|
||||
}
|
||||
|
||||
void PreviewDialog::Resize()
|
||||
void PreviewDialog::Stop()
|
||||
{
|
||||
_stop = true;
|
||||
if (_thread.joinable()) {
|
||||
_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void PreviewDialog::PatternMatchParamtersChanged(
|
||||
const PatternMatchParameters ¶ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_patternMatchParams = params;
|
||||
_patternImageData = createPatternData(_patternMatchParams.image);
|
||||
}
|
||||
|
||||
void PreviewDialog::ObjDetectParamtersChanged(const ObjDetectParamerts ¶ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_objDetectParams = params;
|
||||
}
|
||||
|
||||
void PreviewDialog::VideoSelectionChanged(const VideoSelection &video)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_video = video;
|
||||
}
|
||||
|
||||
void PreviewDialog::AreaParamtersChanged(const AreaParamters ¶ms)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_areaParams = params;
|
||||
}
|
||||
|
||||
void PreviewDialog::ConditionChanged(int cond)
|
||||
{
|
||||
Stop();
|
||||
close();
|
||||
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_condition = static_cast<VideoCondition>(cond);
|
||||
}
|
||||
|
||||
void PreviewDialog::ResizeImageLabel()
|
||||
{
|
||||
_imageLabel->adjustSize();
|
||||
if (_type == Type::SELECT_AREA && !_selectingArea) {
|
||||
|
|
@ -118,11 +177,13 @@ void PreviewDialog::Start()
|
|||
if (_thread.joinable()) {
|
||||
return;
|
||||
}
|
||||
if (!_conditionData) {
|
||||
if (!_video.ValidSelection()) {
|
||||
DisplayMessage(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.screenshotFail"));
|
||||
close();
|
||||
return;
|
||||
}
|
||||
_stop = false;
|
||||
_thread = std::thread(&PreviewDialog::CheckForMatchLoop, this);
|
||||
}
|
||||
|
||||
|
|
@ -130,20 +191,15 @@ void PreviewDialog::CheckForMatchLoop()
|
|||
{
|
||||
std::condition_variable cv;
|
||||
while (!_stop) {
|
||||
std::unique_lock<std::mutex> lock(*_mtx);
|
||||
auto source = obs_weak_source_get_source(
|
||||
_conditionData->_video.GetVideo());
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
auto source = obs_weak_source_get_source(_video.GetVideo());
|
||||
ScreenshotHelper screenshot(source);
|
||||
obs_source_release(source);
|
||||
cv.wait_for(lock, std::chrono::seconds(1));
|
||||
if (_stop) {
|
||||
cv.wait_for(lock, std::chrono::milliseconds(_delay));
|
||||
if (_stop || isHidden()) {
|
||||
return;
|
||||
}
|
||||
if (isHidden()) {
|
||||
continue;
|
||||
}
|
||||
if (!screenshot.done ||
|
||||
!_conditionData->_video.ValidSelection()) {
|
||||
if (!screenshot.done || !_video.ValidSelection()) {
|
||||
_statusLabel->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.screenshotFail"));
|
||||
_imageLabel->setPixmap(QPixmap());
|
||||
|
|
@ -158,12 +214,11 @@ void PreviewDialog::CheckForMatchLoop()
|
|||
}
|
||||
|
||||
if (_type == Type::SHOW_MATCH) {
|
||||
if (_conditionData->_checkAreaEnable) {
|
||||
if (_areaParams.enable) {
|
||||
screenshot.image = screenshot.image.copy(
|
||||
_conditionData->_checkArea.x,
|
||||
_conditionData->_checkArea.y,
|
||||
_conditionData->_checkArea.width,
|
||||
_conditionData->_checkArea.height);
|
||||
_areaParams.area.x, _areaParams.area.y,
|
||||
_areaParams.area.width,
|
||||
_areaParams.area.height);
|
||||
}
|
||||
MarkMatch(screenshot.image);
|
||||
}
|
||||
|
|
@ -171,15 +226,15 @@ void PreviewDialog::CheckForMatchLoop()
|
|||
}
|
||||
}
|
||||
|
||||
void markPatterns(cv::Mat &matchResult, QImage &image, QImage &pattern)
|
||||
void markPatterns(cv::Mat &matchResult, QImage &image, cv::Mat &pattern)
|
||||
{
|
||||
auto matchImg = QImageToMat(image);
|
||||
for (int row = 0; row < matchResult.rows - 1; row++) {
|
||||
for (int col = 0; col < matchResult.cols - 1; col++) {
|
||||
if (matchResult.at<float>(row, col) != 0.0) {
|
||||
rectangle(matchImg, {col, row},
|
||||
cv::Point(col + pattern.width(),
|
||||
row + pattern.height()),
|
||||
cv::Point(col + pattern.cols,
|
||||
row + pattern.rows),
|
||||
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -199,27 +254,26 @@ void markObjects(QImage &image, std::vector<cv::Rect> &objects)
|
|||
|
||||
void PreviewDialog::MarkMatch(QImage &screenshot)
|
||||
{
|
||||
if (_conditionData->_condition == VideoCondition::PATTERN) {
|
||||
if (_condition == VideoCondition::PATTERN) {
|
||||
cv::Mat result;
|
||||
QImage pattern = _conditionData->GetMatchImage();
|
||||
matchPattern(screenshot, pattern,
|
||||
_conditionData->_patternThreshold, result,
|
||||
_conditionData->_useAlphaAsMask);
|
||||
matchPattern(screenshot, _patternImageData,
|
||||
_patternMatchParams.threshold, result,
|
||||
_patternMatchParams.useAlphaAsMask);
|
||||
if (countNonZero(result) == 0) {
|
||||
_statusLabel->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternMatchFail"));
|
||||
} else {
|
||||
_statusLabel->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.patternMatchSuccess"));
|
||||
markPatterns(result, screenshot, pattern);
|
||||
markPatterns(result, screenshot,
|
||||
_patternImageData.rgbaPattern);
|
||||
}
|
||||
} else if (_conditionData->_condition == VideoCondition::OBJECT) {
|
||||
auto objects = matchObject(screenshot,
|
||||
_conditionData->_objectCascade,
|
||||
_conditionData->_scaleFactor,
|
||||
_conditionData->_minNeighbors,
|
||||
_conditionData->_minSize.CV(),
|
||||
_conditionData->_maxSize.CV());
|
||||
} else if (_condition == VideoCondition::OBJECT) {
|
||||
auto objects = matchObject(screenshot, _objDetectParams.cascade,
|
||||
_objDetectParams.scaleFactor,
|
||||
_objDetectParams.minNeighbors,
|
||||
_objDetectParams.minSize.CV(),
|
||||
_objDetectParams.maxSize.CV());
|
||||
if (objects.empty()) {
|
||||
_statusLabel->setText(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.video.objectMatchFail"));
|
||||
|
|
@ -233,17 +287,15 @@ void PreviewDialog::MarkMatch(QImage &screenshot)
|
|||
|
||||
void PreviewDialog::DrawFrame()
|
||||
{
|
||||
if (!_conditionData) {
|
||||
if (!_video.ValidSelection()) {
|
||||
return;
|
||||
}
|
||||
auto imageStart =
|
||||
_imageLabel->mapToGlobal(_imageLabel->rect().topLeft());
|
||||
auto windowStart = mapToGlobal(rect().topLeft());
|
||||
_rubberBand->resize(_conditionData->_checkArea.width,
|
||||
_conditionData->_checkArea.height);
|
||||
_rubberBand->move(_conditionData->_checkArea.x +
|
||||
(imageStart.x() - windowStart.x()),
|
||||
_conditionData->_checkArea.y +
|
||||
(imageStart.y() - windowStart.y()));
|
||||
_rubberBand->resize(_areaParams.area.width, _areaParams.area.height);
|
||||
_rubberBand->move(
|
||||
_areaParams.area.x + (imageStart.x() - windowStart.x()),
|
||||
_areaParams.area.y + (imageStart.y() - windowStart.y()));
|
||||
_rubberBand->show();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#pragma once
|
||||
#include "video-selection.hpp"
|
||||
#include "paramerter-wrappers.hpp"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
|
|
@ -12,20 +14,25 @@
|
|||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
class MacroConditionVideo;
|
||||
|
||||
class PreviewDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PreviewDialog(QWidget *parent, MacroConditionVideo *_conditionData,
|
||||
std::mutex *mutex);
|
||||
PreviewDialog(QWidget *parent, int delay);
|
||||
virtual ~PreviewDialog();
|
||||
void ShowMatch();
|
||||
void SelectArea();
|
||||
void Stop();
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
public slots:
|
||||
void PatternMatchParamtersChanged(const PatternMatchParameters &);
|
||||
void ObjDetectParamtersChanged(const ObjDetectParamerts &);
|
||||
void VideoSelectionChanged(const VideoSelection &);
|
||||
void AreaParamtersChanged(const AreaParamters &);
|
||||
void ConditionChanged(int cond);
|
||||
private slots:
|
||||
void Resize();
|
||||
void ResizeImageLabel();
|
||||
signals:
|
||||
void SelectionAreaChanged(QRect area);
|
||||
|
||||
|
|
@ -39,7 +46,13 @@ private:
|
|||
void mouseMoveEvent(QMouseEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
|
||||
MacroConditionVideo *_conditionData;
|
||||
VideoSelection _video;
|
||||
PatternMatchParameters _patternMatchParams;
|
||||
PatternImageData _patternImageData;
|
||||
ObjDetectParamerts _objDetectParams;
|
||||
AreaParamters _areaParams;
|
||||
|
||||
VideoCondition _condition = VideoCondition::PATTERN;
|
||||
QScrollArea *_scrollArea;
|
||||
QLabel *_statusLabel;
|
||||
QLabel *_imageLabel;
|
||||
|
|
@ -49,13 +62,14 @@ private:
|
|||
QRubberBand *_rubberBand = nullptr;
|
||||
std::atomic_bool _selectingArea = {false};
|
||||
|
||||
std::mutex *_mtx;
|
||||
std::mutex _mtx;
|
||||
std::thread _thread;
|
||||
std::atomic_bool _stop = {false};
|
||||
std::atomic_bool _stop = {true};
|
||||
|
||||
enum class Type {
|
||||
SHOW_MATCH,
|
||||
SELECT_AREA,
|
||||
};
|
||||
Type _type = Type::SHOW_MATCH;
|
||||
int _delay = 300;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user