Files
hrynco-kswitcher/tests/ocr.cpp
T

96 lines
4.6 KiB
C++

#include "ocr.h"
#include "app.h"
#include <iostream>
namespace ks {
struct InputRegressionAccess {
static void scheduleOcr(App &app) { app.scheduleHistoryOcr(app.settings); }
};
}
int main(int argc, char **argv)
{
const bool tesseract = argc == 2 && std::string(argv[1]) == "--tesseract";
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
try
{
BITMAPINFO info{};
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = 800;
info.bmiHeader.biHeight = -150;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 32;
void *data{};
HBITMAP bitmap = CreateDIBSection(nullptr, &info, DIB_RGB_COLORS, &data, nullptr, 0);
if (!bitmap)
throw std::runtime_error("Cannot create OCR fixture");
HDC dc = CreateCompatibleDC(nullptr);
auto old = SelectObject(dc, bitmap);
RECT rect{0, 0, 800, 150};
FillRect(dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
HFONT font = CreateFontW(-54, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0,
ANTIALIASED_QUALITY, 0, L"Arial");
auto oldFont = SelectObject(dc, font);
SetTextColor(dc, RGB(0, 0, 0));
SetBkColor(dc, RGB(255, 255, 255));
std::wstring fixture = tesseract ? L"Привіт Україно HELLO" : L"HELLO KSWITCHER";
TextOutW(dc, 20, 40, fixture.c_str(), (int)fixture.size());
GdiFlush();
ks::Pixels pixels{800, 150, ks::Bytes((BYTE *)data, (BYTE *)data + 800 * 150 * 4)};
SelectObject(dc, oldFont);
SelectObject(dc, old);
DeleteObject(font);
DeleteObject(bitmap);
DeleteDC(dc);
ks::Clip clip{{ks::imageDib(pixels)}};
auto decoded = ks::decodeImage(clip);
if (decoded.width != 800 || decoded.height != 150)
throw std::runtime_error("WIC decode size mismatch");
ks::Settings config;
config.useTesseract = tesseract;
auto text = ks::recognizeText(clip, config);
std::cout << "OCR: " << ks::utf8(text) << '\n';
if (ks::upper(text).find(L"HELLO") == std::wstring::npos)
throw std::runtime_error("OCR fixture was not recognized");
if (tesseract && ks::lower(text).find(L"привіт") == std::wstring::npos)
throw std::runtime_error("Ukrainian OCR fixture was not recognized");
auto temp = std::filesystem::temp_directory_path() / (L"KSwitcher-auto-ocr-" + std::to_wstring(GetCurrentProcessId()));
auto sequence = GetClipboardSequenceNumber();
{
ks::App app(GetModuleHandleW(nullptr), true, temp);
app.settings.history = true;
app.history->add(clip, L"OCR fixture", app.settings);
auto id = app.history->entries().front().id;
if (tesseract)
{
app.history->saveOcr(id, L"old Windows result", app.settings);
app.settings.useTesseract = true;
}
ks::InputRegressionAccess::scheduleOcr(app);
auto deadline = GetTickCount64() + 35000;
while ((app.history->entries().front().ocrState == 1 ||
app.history->entries().front().ocrProfile != ks::ocrProfile(app.settings)) &&
GetTickCount64() < deadline) Sleep(20);
if (app.history->search(L"HELLO").size() != 1)
throw std::runtime_error("Background OCR did not make the image searchable");
ks::History restored(temp / L"history");
if (!restored.ocrText(id) || restored.search(L"HELLO").size() != 1)
throw std::runtime_error("Background OCR did not persist its result");
if (tesseract && (restored.search(L"привіт").size() != 1 || !restored.search(L"old Windows result").empty()))
throw std::runtime_error("Engine change did not replace cached Windows OCR with Ukrainian text");
if (GetClipboardSequenceNumber() != sequence)
throw std::runtime_error("Background OCR changed the clipboard");
std::cout << "PASS automatic background OCR, persisted search and unchanged clipboard\n";
}
std::filesystem::remove_all(temp);
std::cout << (tesseract ? "PASS optional Tesseract, mixed Ukrainian/English and cache migration\n" :
"PASS native Windows OCR, DIB decoding and async result retrieval\n");
CoUninitialize();
return 0;
}
catch (const std::exception &e)
{
std::cerr << e.what() << '\n';
CoUninitialize();
return 1;
}
}