mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-27 11:45:34 -05:00
Add option to visually select area to check
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
61
src/external-macro-modules/opencv/preview-dialog.hpp
Normal file
61
src/external-macro-modules/opencv/preview-dialog.hpp
Normal 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;
|
||||
};
|
||||
@@ -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};
|
||||
};
|
||||
Reference in New Issue
Block a user