mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-04 19:48:56 -05:00
This makes it easier to conditionally exclude video-edit-object.cpp from the build when OpenCV >= 5 drops CascadeClassifier support.
321 lines
11 KiB
C++
321 lines
11 KiB
C++
#include "macro-condition-video.hpp"
|
|
#include "layout-helpers.hpp"
|
|
#include "plugin-state-helpers.hpp"
|
|
#include "ui-helpers.hpp"
|
|
|
|
#include <QDesktopServices>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QUrl>
|
|
#include <QVBoxLayout>
|
|
|
|
namespace advss {
|
|
|
|
const static std::map<tesseract::PageSegMode, std::string> pageSegModes = {
|
|
{tesseract::PageSegMode::PSM_SINGLE_COLUMN,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.singleColumn"},
|
|
{tesseract::PageSegMode::PSM_SINGLE_BLOCK_VERT_TEXT,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.singleBlockVertText"},
|
|
{tesseract::PageSegMode::PSM_SINGLE_BLOCK,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.singleBlock"},
|
|
{tesseract::PageSegMode::PSM_SINGLE_LINE,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.singleLine"},
|
|
{tesseract::PageSegMode::PSM_SINGLE_WORD,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.singleWord"},
|
|
{tesseract::PageSegMode::PSM_CIRCLE_WORD,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.circleWord"},
|
|
{tesseract::PageSegMode::PSM_SINGLE_CHAR,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.singleChar"},
|
|
{tesseract::PageSegMode::PSM_SPARSE_TEXT,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.sparseText"},
|
|
{tesseract::PageSegMode::PSM_SPARSE_TEXT_OSD,
|
|
"AdvSceneSwitcher.condition.video.ocrMode.sparseTextOSD"},
|
|
};
|
|
|
|
static inline void populatePageSegModeSelection(QComboBox *list)
|
|
{
|
|
for (const auto &[mode, name] : pageSegModes) {
|
|
list->addItem(obs_module_text(name.c_str()),
|
|
static_cast<int>(mode));
|
|
}
|
|
}
|
|
|
|
static void openFileInEditor(const std::string &filepath)
|
|
{
|
|
const auto path = QString::fromStdString(filepath);
|
|
const QFileInfo fileInfo(path);
|
|
if (!fileInfo.exists()) {
|
|
QFile file(path);
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
|
DisplayMessage(obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.ocrOpenConfig.createFailed"));
|
|
return;
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
QUrl fileUrl = QUrl::fromLocalFile(path);
|
|
if (!QDesktopServices::openUrl(fileUrl)) {
|
|
DisplayMessage(obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.ocrOpenConfig.openFailed"));
|
|
}
|
|
}
|
|
|
|
OCREdit::OCREdit(QWidget *parent, PreviewDialog *previewDialog,
|
|
const std::shared_ptr<MacroConditionVideo> &data)
|
|
: QWidget(parent),
|
|
_matchText(new VariableTextEdit(this)),
|
|
_regex(new RegexConfigWidget(this)),
|
|
_colorButton(new VariableColorButton(
|
|
this,
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.selectColor"))),
|
|
_colorThreshold(new SliderSpinBox(
|
|
0., 1.,
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.colorDeviationThreshold"),
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription"),
|
|
true)),
|
|
_pageSegMode(new QComboBox()),
|
|
_tesseractBaseDir(new FileSelection(FileSelection::Type::FOLDER)),
|
|
_languageCode(new VariableLineEdit(this)),
|
|
_useConfig(new QCheckBox(obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.ocrUseConfigFile"))),
|
|
_configFile(new FileSelection(FileSelection::Type::WRITE, this)),
|
|
_openConfigFile(new QPushButton(obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.ocrOpenConfigFile"))),
|
|
_reloadConfig(new QPushButton()),
|
|
_configLayout(new QHBoxLayout()),
|
|
_previewDialog(previewDialog),
|
|
_entryData(data)
|
|
{
|
|
populatePageSegModeSelection(_pageSegMode);
|
|
|
|
_reloadConfig->setMaximumWidth(22);
|
|
SetButtonIcon(_reloadConfig, GetThemeTypeName() == "Light"
|
|
? ":res/images/refresh.svg"
|
|
: "theme:Dark/refresh.svg");
|
|
_reloadConfig->setToolTip(obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.ocrConfigReload"));
|
|
|
|
QWidget::connect(_colorButton,
|
|
SIGNAL(ColorVariableChanged(const ColorVariable &)),
|
|
this, SLOT(ColorChanged(const ColorVariable &)));
|
|
QWidget::connect(
|
|
_colorThreshold,
|
|
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
|
|
this,
|
|
SLOT(ColorThresholdChanged(const NumberVariable<double> &)));
|
|
QWidget::connect(_matchText, SIGNAL(textChanged()), this,
|
|
SLOT(MatchTextChanged()));
|
|
QWidget::connect(_regex,
|
|
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
|
SLOT(RegexChanged(const RegexConfig &)));
|
|
QWidget::connect(_pageSegMode, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(PageSegModeChanged(int)));
|
|
QWidget::connect(_tesseractBaseDir,
|
|
SIGNAL(PathChanged(const QString &)), this,
|
|
SLOT(TesseractBaseDirChanged(const QString &)));
|
|
QWidget::connect(_languageCode, SIGNAL(editingFinished()), this,
|
|
SLOT(LanguageChanged()));
|
|
QWidget::connect(_useConfig, SIGNAL(stateChanged(int)), this,
|
|
SLOT(UseConfigChanged(int)));
|
|
QWidget::connect(_configFile, SIGNAL(PathChanged(const QString &)),
|
|
this, SLOT(ConfigFileChanged(const QString &)));
|
|
QWidget::connect(_openConfigFile, &QPushButton::clicked, [this](bool) {
|
|
openFileInEditor(
|
|
_entryData->_ocrParameters.GetCustomConfigFile());
|
|
});
|
|
QWidget::connect(_reloadConfig, &QPushButton::clicked, [this](bool) {
|
|
GUARD_LOADING_AND_LOCK();
|
|
_entryData->_ocrParameters.EnableCustomConfig(true);
|
|
_previewDialog->OCRParametersChanged(
|
|
_entryData->_ocrParameters);
|
|
});
|
|
|
|
auto configFileHint = new QLabel();
|
|
const QString path = GetThemeTypeName() == "Light"
|
|
? ":/res/images/help.svg"
|
|
: ":/res/images/help_light.svg";
|
|
const QIcon icon(path);
|
|
const QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
|
configFileHint->setPixmap(pixmap);
|
|
configFileHint->setToolTip(obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.ocrConfigHint"));
|
|
|
|
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
|
{"{{color}}", _colorButton},
|
|
{"{{textType}}", _pageSegMode},
|
|
{"{{tesseractBaseDir}}", _tesseractBaseDir},
|
|
{"{{languageCode}}", _languageCode},
|
|
{"{{configFile}}", _configFile},
|
|
{"{{openConfigFile}}", _openConfigFile},
|
|
{"{{reloadConfig}}", _reloadConfig},
|
|
{"{{configFileHint}}", configFileHint},
|
|
};
|
|
|
|
auto layout = new QVBoxLayout();
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
auto textLayout = new QHBoxLayout();
|
|
textLayout->setContentsMargins(0, 0, 0, 0);
|
|
textLayout->addWidget(_matchText);
|
|
textLayout->addWidget(_regex);
|
|
layout->addLayout(textLayout);
|
|
auto pageModeSegLayout = new QHBoxLayout();
|
|
PlaceWidgets(
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.layout.ocrTextType"),
|
|
pageModeSegLayout, widgetPlaceholders);
|
|
layout->addLayout(pageModeSegLayout);
|
|
auto baseDirLayout = new QHBoxLayout();
|
|
PlaceWidgets(
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.layout.ocrBaseDir"),
|
|
baseDirLayout, widgetPlaceholders, false);
|
|
layout->addLayout(baseDirLayout);
|
|
auto languageLayout = new QHBoxLayout();
|
|
PlaceWidgets(
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.layout.ocrLanguage"),
|
|
languageLayout, widgetPlaceholders);
|
|
layout->addLayout(languageLayout);
|
|
PlaceWidgets(
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.layout.ocrConfig"),
|
|
_configLayout, widgetPlaceholders, false);
|
|
layout->addWidget(_useConfig);
|
|
layout->addLayout(_configLayout);
|
|
auto colorPickLayout = new QHBoxLayout();
|
|
PlaceWidgets(
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.layout.ocrColorPick"),
|
|
colorPickLayout, widgetPlaceholders);
|
|
layout->addLayout(colorPickLayout);
|
|
layout->addWidget(_colorThreshold);
|
|
setLayout(layout);
|
|
|
|
_matchText->setPlainText(_entryData->_ocrParameters.text);
|
|
_regex->SetRegexConfig(_entryData->_ocrParameters.regex);
|
|
_colorButton->SetValue(_entryData->_ocrParameters.color);
|
|
_colorThreshold->SetDoubleValue(
|
|
_entryData->_ocrParameters.colorThreshold);
|
|
_pageSegMode->setCurrentIndex(_pageSegMode->findData(
|
|
static_cast<int>(_entryData->_ocrParameters.GetPageMode())));
|
|
_tesseractBaseDir->SetPath(
|
|
_entryData->_ocrParameters.GetTesseractBasePath());
|
|
_languageCode->setText(_entryData->_ocrParameters.GetLanguageCode());
|
|
_useConfig->setChecked(
|
|
_entryData->_ocrParameters.CustomConfigIsEnabled());
|
|
_configFile->SetPath(_entryData->_ocrParameters.GetCustomConfigFile());
|
|
SetLayoutVisible(_configLayout,
|
|
_entryData->_ocrParameters.CustomConfigIsEnabled());
|
|
_loading = false;
|
|
}
|
|
|
|
void OCREdit::ColorChanged(const ColorVariable &value)
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
_entryData->_ocrParameters.color = value;
|
|
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
void OCREdit::ColorThresholdChanged(const DoubleVariable &value)
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
_entryData->_ocrParameters.colorThreshold = value;
|
|
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
void OCREdit::MatchTextChanged()
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
_entryData->_ocrParameters.text =
|
|
_matchText->toPlainText().toUtf8().constData();
|
|
|
|
adjustSize();
|
|
updateGeometry();
|
|
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
void OCREdit::RegexChanged(const RegexConfig &conf)
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
_entryData->_ocrParameters.regex = conf;
|
|
adjustSize();
|
|
updateGeometry();
|
|
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
void OCREdit::PageSegModeChanged(int idx)
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
_entryData->SetPageSegMode(static_cast<tesseract::PageSegMode>(
|
|
_pageSegMode->itemData(idx).toInt()));
|
|
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
void OCREdit::TesseractBaseDirChanged(const QString &path)
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
if (!_entryData->SetTesseractBaseDir(path.toStdString())) {
|
|
const QString message(obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.ocrLanguageNotFound"));
|
|
const QDir dataDir(path);
|
|
const QString fileName(_languageCode->text() + ".traineddata");
|
|
DisplayMessage(message.arg(fileName, dataDir.absolutePath()));
|
|
|
|
// Reset to previous value
|
|
const QSignalBlocker b(this);
|
|
_tesseractBaseDir->SetPath(
|
|
_entryData->_ocrParameters.GetTesseractBasePath());
|
|
return;
|
|
}
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
void OCREdit::LanguageChanged()
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
if (!_entryData->SetLanguageCode(_languageCode->text().toStdString())) {
|
|
const QString message(obs_module_text(
|
|
"AdvSceneSwitcher.condition.video.ocrLanguageNotFound"));
|
|
const QDir dataDir(QString::fromStdString(
|
|
_entryData->_ocrParameters.GetTesseractBasePath()));
|
|
const QString fileName(_languageCode->text() + ".traineddata");
|
|
DisplayMessage(message.arg(fileName, dataDir.absolutePath()));
|
|
|
|
// Reset to previous value
|
|
const QSignalBlocker b(this);
|
|
_languageCode->setText(
|
|
_entryData->_ocrParameters.GetLanguageCode());
|
|
return;
|
|
}
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
void OCREdit::UseConfigChanged(int value)
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
_entryData->_ocrParameters.EnableCustomConfig(value);
|
|
SetLayoutVisible(_configLayout, value);
|
|
adjustSize();
|
|
updateGeometry();
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
void OCREdit::ConfigFileChanged(const QString &path)
|
|
{
|
|
GUARD_LOADING_AND_LOCK();
|
|
_entryData->_ocrParameters.SetCustomConfigFile(path.toStdString());
|
|
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
|
|
}
|
|
|
|
} // namespace advss
|