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:
WarmUpTill 2022-12-22 21:46:35 +01:00 committed by WarmUpTill
parent b7c6deddde
commit 70c9a3bd83
6 changed files with 262 additions and 198 deletions

View File

@ -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));
}

View File

@ -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.;

View File

@ -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()) {

View File

@ -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);

View File

@ -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 &params)
{
std::unique_lock<std::mutex> lock(_mtx);
_patternMatchParams = params;
_patternImageData = createPatternData(_patternMatchParams.image);
}
void PreviewDialog::ObjDetectParamtersChanged(const ObjDetectParamerts &params)
{
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 &params)
{
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();
}

View File

@ -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;
};