1
0
forked from BRT/arc

Add OCR utilities and backend integration for enhanced screen dump analysis:

- Introduce `OcrUtils` with fuzzy-matching logic for text recognition on screenshots.
- Implement dual-pass OCR to handle light-on-dark and dark-on-light text cases.
- Replace hardcoded "Подтверждаю" button tap logic with OCR-based detection and fallback geometry.
- Update `TaskDumpRecorder` to utilize `NetworkService` for uploading screen dumps, images, and metadata to the backend.
- Add support for `/monitoring/dump` endpoint in `NetworkService`.
- Enhance UI functionality with new error handling and progress feedback during dump creation and upload.
This commit is contained in:
slava 2026-04-30 19:39:21 +07:00
parent 11e8f00e42
commit f611639532
2 changed files with 61 additions and 3 deletions

View File

@ -165,6 +165,9 @@ if (WIN32)
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/images" DESTINATION "${CMAKE_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/rus.traineddata" DESTINATION "${CMAKE_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/eng.traineddata" DESTINATION "${CMAKE_BINARY_DIR}")
# tessdata/ канонический путь, ожидаемый Tesseract 5+ через TESSDATA_PREFIX/datapath
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/rus.traineddata" DESTINATION "${CMAKE_BINARY_DIR}/tessdata")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/eng.traineddata" DESTINATION "${CMAKE_BINARY_DIR}/tessdata")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/config.ini" DESTINATION "${CMAKE_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/no_devices.png" DESTINATION "${CMAKE_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/assets/ADBKeyboard.apk" DESTINATION "${CMAKE_BINARY_DIR}/assets")
@ -198,6 +201,17 @@ if (WIN32)
"${CMAKE_CURRENT_SOURCE_DIR}/eng.traineddata"
"$<TARGET_FILE_DIR:ARC>/eng.traineddata"
COMMAND ${CMAKE_COMMAND} -E make_directory
"$<TARGET_FILE_DIR:ARC>/tessdata"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/rus.traineddata"
"$<TARGET_FILE_DIR:ARC>/tessdata/rus.traineddata"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/eng.traineddata"
"$<TARGET_FILE_DIR:ARC>/tessdata/eng.traineddata"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/config.ini"
"$<TARGET_FILE_DIR:ARC>/config.ini"
@ -271,6 +285,9 @@ elseif (APPLE)
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/images" DESTINATION "${CMAKE_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/rus.traineddata" DESTINATION "${CMAKE_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/eng.traineddata" DESTINATION "${CMAKE_BINARY_DIR}")
# tessdata/ канонический путь, ожидаемый Tesseract 5+ через TESSDATA_PREFIX/datapath
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/rus.traineddata" DESTINATION "${CMAKE_BINARY_DIR}/tessdata")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/eng.traineddata" DESTINATION "${CMAKE_BINARY_DIR}/tessdata")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/config.ini" DESTINATION "${CMAKE_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/no_devices.png" DESTINATION "${CMAKE_BINARY_DIR}")
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/assets/ADBKeyboard.apk" DESTINATION "${CMAKE_BINARY_DIR}/assets")

View File

@ -2,13 +2,47 @@
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QElapsedTimer>
#include <QFile>
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
namespace {
// Возвращает путь, который надо передавать в `TessBaseAPI::Init`, либо пустую
// строку, если ни в одной из ожидаемых раскладок rus.traineddata не найден.
// На Windows (vcpkg) Tesseract 5 без подкаталога `tessdata/` иногда падает
// молча — пробуем оба варианта и логируем результат.
QString resolveTessdataPath() {
static QString cached;
static bool resolved = false;
if (resolved) return cached;
resolved = true;
const QString appDir = QCoreApplication::applicationDirPath();
const QStringList candidates = {
appDir + "/tessdata", // канонический путь
appDir, // legacy: traineddata лежат рядом с exe
};
for (const QString &dir : candidates) {
const QString file = dir + "/rus.traineddata";
if (QFile::exists(file)) {
// Tesseract принимает datapath как РОДИТЕЛЯ tessdata-каталога,
// но также понимает каталог, где лежат сами .traineddata.
cached = QDir::toNativeSeparators(dir);
qDebug() << "[OcrUtils] tessdata located at" << cached;
return cached;
}
}
qWarning() << "[OcrUtils] rus.traineddata NOT FOUND. Checked:" << candidates
<< "— OCR будет работать в fallback-режиме без поиска текста.";
return {};
}
// Расстояние Левенштейна на QString — нужен для нечёткого сравнения OCR-вывода
// с искомым словом. Для "Подтверждаю" допускаем 1-2 ошибки.
int editDistance(const QString &a, const QString &b) {
@ -94,10 +128,17 @@ QPoint findTextCenter(const QByteArray &png,
if (inverted) prepared = inverted;
}
const QString tessdata = resolveTessdataPath();
if (tessdata.isEmpty()) {
if (inverted) pixDestroy(&inverted);
pixDestroy(&gray);
return {-1, -1};
}
auto *api = new tesseract::TessBaseAPI();
if (api->Init(QCoreApplication::applicationDirPath().toUtf8().constData(), "rus")) {
qWarning() << "[OcrUtils] Tesseract init failed (tessdata in"
<< QCoreApplication::applicationDirPath() << ")";
if (api->Init(tessdata.toUtf8().constData(), "rus")) {
qWarning() << "[OcrUtils] Tesseract init failed for datapath" << tessdata
<< "— rus.traineddata присутствует, но не загрузился"
<< "(возможно, повреждён или несовместимая версия)";
delete api;
if (inverted) pixDestroy(&inverted);
pixDestroy(&gray);