Add option to visually select area to check

This commit is contained in:
WarmUpTill
2022-03-13 15:25:38 +01:00
committed by WarmUpTill
parent 5462334693
commit d450210d39
7 changed files with 203 additions and 71 deletions

View File

@@ -161,7 +161,7 @@ AdvSceneSwitcher.condition.video.entry="{{videoSources}} {{condition}} {{imagePa
AdvSceneSwitcher.condition.video.entry.modelPath="Model data (haar cascade classifier): {{modelDataPath}}"
AdvSceneSwitcher.condition.video.entry.minNeighbor="Minimum neighbors: {{minNeighbors}}"
AdvSceneSwitcher.condition.video.entry.throttle="{{throttleEnable}}Reduce CPU load by performing check only every {{throttleCount}} milliseconds"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}Perform check only in area {{checkArea}}"
AdvSceneSwitcher.condition.video.entry.checkArea="{{checkAreaEnable}}Perform check only in area {{checkArea}} {{selectArea}}"
AdvSceneSwitcher.condition.video.minSize="Minimum size:"
AdvSceneSwitcher.condition.video.maxSize="Maximum size:"
AdvSceneSwitcher.condition.video.selectArea="Select area"

View File

@@ -21,8 +21,8 @@ set(module_SOURCES
area-selection.hpp
macro-condition-video.cpp
macro-condition-video.hpp
video-match-dialog.cpp
video-match-dialog.hpp
preview-dialog.cpp
preview-dialog.hpp
threshold-slider.cpp
threshold-slider.hpp
opencv-helpers.cpp

View File

@@ -312,7 +312,9 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
_throttleCount(new QSpinBox()),
_showMatch(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.video.showMatch"))),
_matchDialog(this, entryData.get(), &GetSwitcher()->m),
_selectArea(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.video.selectArea"))),
_previewDialog(this, entryData.get(), &GetSwitcher()->m),
_modelPathLayout(new QHBoxLayout),
_neighborsControlLayout(new QHBoxLayout),
_checkAreaControlLayout(new QHBoxLayout),
@@ -364,6 +366,10 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
SLOT(ThrottleCountChanged(int)));
QWidget::connect(_showMatch, SIGNAL(clicked()), this,
SLOT(ShowMatchClicked()));
QWidget::connect(&_previewDialog, SIGNAL(SelectionAreaChanged(QRect)),
this, SLOT(CheckAreaChanged(QRect)));
QWidget::connect(_selectArea, SIGNAL(clicked()), this,
SLOT(SelectAreaClicked()));
populateVideoSelection(_videoSelection);
populateConditionSelection(_condition);
@@ -381,6 +387,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
{"{{throttleCount}}", _throttleCount},
{"{{checkAreaEnable}}", _checkAreaEnable},
{"{{checkArea}}", _checkArea},
{"{{selectArea}}", _selectArea},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.video.entry"),
entryLine1Layout, widgetPlaceholders);
@@ -662,6 +669,7 @@ void MacroConditionVideoEdit::CheckAreaEnableChanged(int value)
std::lock_guard<std::mutex> lock(GetSwitcher()->m);
_entryData->_checkAreaEnable = value;
_checkArea->setEnabled(value);
_selectArea->setEnabled(value);
}
void MacroConditionVideoEdit::CheckAreaChanged(advss::Area value)
@@ -674,6 +682,13 @@ void MacroConditionVideoEdit::CheckAreaChanged(advss::Area value)
_entryData->_checkArea = value;
}
void MacroConditionVideoEdit::CheckAreaChanged(QRect rect)
{
advss::Area area{rect.topLeft().x(), rect.y(), rect.width(),
rect.height()};
_checkArea->SetArea(area);
}
void MacroConditionVideoEdit::ThrottleEnableChanged(int value)
{
if (_loading || !_entryData) {
@@ -697,10 +712,18 @@ void MacroConditionVideoEdit::ThrottleCountChanged(int value)
void MacroConditionVideoEdit::ShowMatchClicked()
{
_matchDialog.show();
_matchDialog.raise();
_matchDialog.activateWindow();
_matchDialog.ShowMatch();
_previewDialog.show();
_previewDialog.raise();
_previewDialog.activateWindow();
_previewDialog.ShowMatch();
}
void MacroConditionVideoEdit::SelectAreaClicked()
{
_previewDialog.show();
_previewDialog.raise();
_previewDialog.activateWindow();
_previewDialog.SelectArea();
}
void MacroConditionVideoEdit::ModelPathChanged(const QString &text)

View File

@@ -1,7 +1,7 @@
#pragma once
#include "opencv-helpers.hpp"
#include "threshold-slider.hpp"
#include "video-match-dialog.hpp"
#include "preview-dialog.hpp"
#include "area-selection.hpp"
#include <macro.hpp>
@@ -14,6 +14,7 @@
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QRect>
enum class VideoCondition {
MATCH,
@@ -119,6 +120,8 @@ private slots:
void CheckAreaEnableChanged(int value);
void CheckAreaChanged(advss::Area);
void CheckAreaChanged(QRect area);
void SelectAreaClicked();
void ThrottleEnableChanged(int value);
void ThrottleCountChanged(int value);
@@ -148,13 +151,14 @@ protected:
QHBoxLayout *_checkAreaControlLayout;
QCheckBox *_checkAreaEnable;
AreaSelection *_checkArea;
QPushButton *_selectArea;
QHBoxLayout *_throttleControlLayout;
QCheckBox *_throttleEnable;
QSpinBox *_throttleCount;
QPushButton *_showMatch;
ShowMatchDialog _matchDialog;
PreviewDialog _previewDialog;
std::shared_ptr<MacroConditionVideo> _entryData;

View File

@@ -1,22 +1,24 @@
#include "video-match-dialog.hpp"
#include "preview-dialog.hpp"
#include "macro-condition-video.hpp"
#include "opencv-helpers.hpp"
#include "utility.hpp"
#include <condition_variable>
ShowMatchDialog::ShowMatchDialog(QWidget *parent,
MacroConditionVideo *conditionData,
std::mutex *mutex)
PreviewDialog::PreviewDialog(QWidget *parent,
MacroConditionVideo *conditionData,
std::mutex *mutex)
: QDialog(parent),
_conditionData(conditionData),
_imageLabel(new QLabel),
_imageLabel(new QLabel(this)),
_scrollArea(new QScrollArea),
_rubberBand(new QRubberBand(QRubberBand::Rectangle, this)),
_mtx(mutex)
{
setWindowTitle("Advanced Scene Switcher");
_statusLabel = new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.showMatch.loading"));
resize(640, 480);
_scrollArea->setBackgroundRole(QPalette::Dark);
_scrollArea->setWidget(_imageLabel);
@@ -26,17 +28,53 @@ ShowMatchDialog::ShowMatchDialog(QWidget *parent,
setLayout(layout);
// This is a workaround to handle random segfaults triggered when using:
// QMetaObject::invokeMethod(this, "RedrawImage", -Qt::QueuedConnection,
// Q_ARG(QImage, image));
// QMetaObject::invokeMethod(this, "RedrawImage", Qt::QueuedConnection,
// Q_ARG(QImage, image));
// from within CheckForMatchLoop().
// Even using BlockingQueuedConnection causes deadlocks
_timer.setInterval(500);
QWidget::connect(&_timer, &QTimer::timeout, this,
&ShowMatchDialog::Resize);
&PreviewDialog::Resize);
_timer.start();
}
ShowMatchDialog::~ShowMatchDialog()
void PreviewDialog::mousePressEvent(QMouseEvent *event)
{
_selectingArea = true;
if (_type == Type::SHOW_MATCH) {
return;
}
_origin = event->pos();
_rubberBand->setGeometry(QRect(_origin, QSize()));
_rubberBand->show();
}
void PreviewDialog::mouseMoveEvent(QMouseEvent *event)
{
_rubberBand->setGeometry(QRect(_origin, event->pos()).normalized());
}
void PreviewDialog::mouseReleaseEvent(QMouseEvent *)
{
auto selectionStart =
_rubberBand->mapToGlobal(_rubberBand->rect().topLeft());
QRect selectionArea(selectionStart, _rubberBand->size());
auto imageStart =
_imageLabel->mapToGlobal(_imageLabel->rect().topLeft());
QRect imageArea(imageStart, _imageLabel->size());
auto intersected = imageArea.intersected(selectionArea);
QRect checksize(QPoint(intersected.topLeft() - imageStart),
intersected.size());
if (checksize.x() >= 0 && checksize.y() >= 0 && checksize.width() > 0 &&
checksize.height() > 0) {
emit SelectionAreaChanged(checksize);
}
_selectingArea = false;
}
PreviewDialog::~PreviewDialog()
{
_stop = true;
if (_thread.joinable()) {
@@ -44,7 +82,32 @@ ShowMatchDialog::~ShowMatchDialog()
}
}
void ShowMatchDialog::ShowMatch()
void PreviewDialog::ShowMatch()
{
Start();
_rubberBand->hide();
_type = Type::SHOW_MATCH;
}
void PreviewDialog::SelectArea()
{
_selectingArea = false;
Start();
_type = Type::SELECT_AREA;
DrawFrame();
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.selectArea.status"));
}
void PreviewDialog::Resize()
{
_imageLabel->adjustSize();
if (_type == Type::SELECT_AREA && !_selectingArea) {
DrawFrame();
}
}
void PreviewDialog::Start()
{
if (_thread.joinable()) {
return;
@@ -54,15 +117,10 @@ void ShowMatchDialog::ShowMatch()
"AdvSceneSwitcher.condition.video.screenshotFail"));
return;
}
_thread = std::thread(&ShowMatchDialog::CheckForMatchLoop, this);
_thread = std::thread(&PreviewDialog::CheckForMatchLoop, this);
}
void ShowMatchDialog::Resize()
{
_imageLabel->adjustSize();
}
void ShowMatchDialog::CheckForMatchLoop()
void PreviewDialog::CheckForMatchLoop()
{
std::condition_variable cv;
while (!_stop) {
@@ -75,6 +133,9 @@ void ShowMatchDialog::CheckForMatchLoop()
if (_stop) {
return;
}
if (isHidden()) {
continue;
}
if (!screenshot.done) {
_statusLabel->setText(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
@@ -89,14 +150,16 @@ void ShowMatchDialog::CheckForMatchLoop()
continue;
}
if (_conditionData->_checkAreaEnable) {
screenshot.image = screenshot.image.copy(
_conditionData->_checkArea.x,
_conditionData->_checkArea.y,
_conditionData->_checkArea.width,
_conditionData->_checkArea.height);
if (_type == Type::SHOW_MATCH) {
if (_conditionData->_checkAreaEnable) {
screenshot.image = screenshot.image.copy(
_conditionData->_checkArea.x,
_conditionData->_checkArea.y,
_conditionData->_checkArea.width,
_conditionData->_checkArea.height);
}
MarkMatch(screenshot.image);
}
MarkMatch(screenshot.image);
_imageLabel->setPixmap(QPixmap::fromImage(screenshot.image));
}
}
@@ -127,7 +190,7 @@ void markObjects(QImage &image, std::vector<cv::Rect> &objects)
}
}
void ShowMatchDialog::MarkMatch(QImage &screenshot)
void PreviewDialog::MarkMatch(QImage &screenshot)
{
if (_conditionData->_condition == VideoCondition::PATTERN) {
cv::Mat result;
@@ -160,3 +223,20 @@ void ShowMatchDialog::MarkMatch(QImage &screenshot)
}
}
}
void PreviewDialog::DrawFrame()
{
if (!_conditionData) {
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->show();
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include <QDialog>
#include <QLabel>
#include <QScrollArea>
#include <QTimer>
#include <QMouseEvent>
#include <QRubberBand>
#include <QPoint>
#include <thread>
#include <mutex>
class MacroConditionVideo;
class PreviewDialog : public QDialog {
Q_OBJECT
public:
PreviewDialog(QWidget *parent, MacroConditionVideo *_conditionData,
std::mutex *mutex);
virtual ~PreviewDialog();
void ShowMatch();
void SelectArea();
private slots:
void Resize();
signals:
void SelectionAreaChanged(QRect area);
private:
void Start();
void CheckForMatchLoop();
void MarkMatch(QImage &screenshot);
void DrawFrame();
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
MacroConditionVideo *_conditionData;
QScrollArea *_scrollArea;
QLabel *_statusLabel;
QLabel *_imageLabel;
QTimer _timer;
QPoint _origin;
QRubberBand *_rubberBand = nullptr;
std::atomic_bool _selectingArea = {false};
std::mutex *_mtx;
std::thread _thread;
std::atomic_bool _stop = {false};
enum class Type {
SHOW_MATCH,
SELECT_AREA,
};
Type _type = Type::SHOW_MATCH;
};

View File

@@ -1,36 +0,0 @@
#pragma once
#include <QDialog>
#include <QLabel>
#include <QScrollArea>
#include <QTimer>
#include <thread>
#include <mutex>
class MacroConditionVideo;
class ShowMatchDialog : public QDialog {
Q_OBJECT
public:
ShowMatchDialog(QWidget *parent, MacroConditionVideo *_conditionData,
std::mutex *mutex);
virtual ~ShowMatchDialog();
void ShowMatch();
private slots:
void Resize();
private:
void CheckForMatchLoop();
void MarkMatch(QImage &screenshot);
MacroConditionVideo *_conditionData;
QScrollArea *_scrollArea;
QLabel *_statusLabel;
QLabel *_imageLabel;
QTimer _timer;
std::mutex *_mtx;
std::thread _thread;
std::atomic_bool _stop = {false};
};