FOSSredder 0.5.0
Loading...
Searching...
No Matches
TesseractCore.cpp
Go to the documentation of this file.
1
5
10#include <tesseract/baseapi.h>
11#include <leptonica/allheaders.h>
12#include <sstream>
13#include <fstream>
14#include <algorithm>
15#include <array>
16#include <filesystem>
17#include <cstdlib>
18#include <system_error>
19#if defined(_WIN32)
20#define NOMINMAX
21#include <Windows.h>
22#endif
23
24using namespace std;
26
27static Pix* pixFromBytes(const std::vector<uint8_t>& data) {
28 if (data.empty()) return nullptr;
29 Pix* p = pixReadMem(data.data(), static_cast<size_t>(data.size()));
30 return p;
31}
32
33static std::string readEnvironmentVariable(const char* name) {
34#if defined(_MSC_VER)
35 char* value = nullptr;
36 size_t valueLength = 0;
37 if (_dupenv_s(&value, &valueLength, name) != 0 || value == nullptr) {
38 return {};
39 }
40
41 std::string result(value, valueLength > 0 ? valueLength - 1 : 0);
42 free(value);
43 return result;
44#else
45 if (const char* value = std::getenv(name)) {
46 return std::string(value);
47 }
48
49 return {};
50#endif
51}
52
53static std::filesystem::path executableDirectory() {
54#if defined(_WIN32)
55 std::array<wchar_t, MAX_PATH> buffer{};
56 const DWORD length = GetModuleFileNameW(nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
57 if (length == 0 || length >= buffer.size()) {
58 return {};
59 }
60
61 return std::filesystem::path(buffer.data()).parent_path();
62#else
63 return {};
64#endif
65}
66
67static std::string resolveTessdataPath(const std::string& provided) {
68 auto existsNoThrow = [](const std::filesystem::path& p) {
69 std::error_code ec;
70 return !p.empty() && std::filesystem::exists(p, ec) && !ec;
71 };
72
73 if (!provided.empty()) {
74 std::filesystem::path p(provided);
75 if (existsNoThrow(p)) return p.string();
76 }
77
78 const std::string envTessdata = readEnvironmentVariable("TESSDATA_PREFIX");
79 if (!envTessdata.empty()) {
80 if (existsNoThrow(envTessdata)) return envTessdata;
81 }
82
83 std::vector<std::filesystem::path> candidates;
84 const auto exeDir = executableDirectory();
85 if (!exeDir.empty()) {
86 candidates.push_back(exeDir / "res" / "tessdata");
87 candidates.push_back(exeDir / "tessdata");
88 candidates.push_back(exeDir.parent_path() / "res" / "tessdata");
89 candidates.push_back(exeDir.parent_path() / "tessdata");
90 }
91 candidates.push_back(std::filesystem::current_path() / "res" / "tessdata");
92 candidates.push_back(std::filesystem::current_path() / "tessdata");
93 candidates.push_back(std::filesystem::path(__FILE__).parent_path().parent_path() / "res" / "tessdata");
94 candidates.push_back(std::filesystem::path(__FILE__).parent_path().parent_path() / "tessdata");
95
96 for (const auto &c : candidates) {
97 if (existsNoThrow(c)) return c.string();
98 }
99
100 return std::string();
101}
102
103static tesseract::OcrEngineMode toTesseractOem(ports::EngineMode mode) {
104 switch (mode) {
105 case ports::EngineMode::LegacyOnly:
106 return tesseract::OEM_TESSERACT_ONLY;
107 case ports::EngineMode::LstmOnly:
108 return tesseract::OEM_LSTM_ONLY;
109 case ports::EngineMode::LegacyAndLstm:
110 return tesseract::OEM_TESSERACT_LSTM_COMBINED;
111 case ports::EngineMode::Default:
112 default:
113 return tesseract::OEM_DEFAULT;
114 }
115}
116
117static std::string resolveLanguage(const ports::Settings& recognition) {
118 return recognition.language.empty() ? std::string("deu") : recognition.language;
119}
120
121static void configureForStatements(tesseract::TessBaseAPI& ocr, const ports::Settings& recognition) {
122 ocr.SetVariable("preserve_interword_spaces", recognition.preserveInterwordSpaces ? "1" : "0");
123
124 if (!recognition.charWhitelist.empty()) {
125 ocr.SetVariable("tessedit_char_whitelist", recognition.charWhitelist.c_str());
126 }
127}
128
129static void applyPsm(tesseract::TessBaseAPI& ocr, int psm) {
130 const int maxPsm = static_cast<int>(tesseract::PSM_COUNT) - 1;
131 if (psm < 0 || psm > maxPsm) {
132 psm = 3;
133 }
134 ocr.SetPageSegMode(static_cast<tesseract::PageSegMode>(psm));
135}
136
137static ports::Text recognizeTextFromBytes(const std::vector<uint8_t>& data,
138 const std::string& tessdataPath,
139 const ports::Settings& recognition,
140 std::shared_ptr<core::ports::diagnostics::IDiagnostics> dbg) {
141 ports::Text out;
142 tesseract::TessBaseAPI ocr;
143 std::string resolved = resolveTessdataPath(tessdataPath);
144 const std::string language = resolveLanguage(recognition);
145 if (!resolved.empty()) ocr.SetVariable("TESSDATA_PREFIX", resolved.c_str());
146 if (resolved.empty() || ocr.Init(resolved.c_str(), language.c_str(), toTesseractOem(recognition.engineMode)) != 0) {
147 // last resort: try default Init without explicit path
148 if (ocr.Init(nullptr, language.c_str(), toTesseractOem(recognition.engineMode)) != 0) return out;
149 }
150
151 configureForStatements(ocr, recognition);
152 applyPsm(ocr, recognition.psm);
153
154 Pix* pix = pixFromBytes(data);
155 if (!pix) { ocr.End(); return out; }
156 ocr.SetImage(pix);
157 ocr.Recognize(0);
158 char* txt = ocr.GetUTF8Text();
159 if (txt) { out.text = string(txt); delete[] txt; }
160 out.meanConfidence = ocr.MeanTextConf();
161 if (dbg && dbg->enabled()) dbg->writeText(string("tesseract/output/text.txt"), out.text);
162 ocr.End(); pixDestroy(&pix);
163 return out;
164}
165
166static vector<ports::Word> getWordsFromBytes(const std::vector<uint8_t>& data,
167 const std::string& tessdataPath,
168 const ports::Settings& recognition,
169 std::shared_ptr<core::ports::diagnostics::IDiagnostics> dbg) {
170 std::vector<ports::Word> out;
171 Pix* pix = pixFromBytes(data);
172 if (!pix) return out;
173 tesseract::TessBaseAPI ocr;
174 std::string resolved = resolveTessdataPath(tessdataPath);
175 const std::string language = resolveLanguage(recognition);
176 if (!resolved.empty()) ocr.SetVariable("TESSDATA_PREFIX", resolved.c_str());
177 if (resolved.empty() || ocr.Init(resolved.c_str(), language.c_str(), toTesseractOem(recognition.engineMode)) != 0) {
178 if (ocr.Init(nullptr, language.c_str(), toTesseractOem(recognition.engineMode)) != 0) { pixDestroy(&pix); return out; }
179 }
180
181 configureForStatements(ocr, recognition);
182 applyPsm(ocr, recognition.psm);
183
184 ocr.SetImage(pix);
185 ocr.Recognize(0);
186
187 // Stable extraction via TSV (word level == 5).
188 char* tsv = ocr.GetTSVText(0);
189 if (!tsv) { ocr.End(); pixDestroy(&pix); return out; }
190 string s(tsv);
191 if (dbg && dbg->enabled()) dbg->writeText(string("tesseract/tsv/words.tsv"), s);
192
193 istringstream iss(s);
194 string line;
195 bool first = true;
196 while (getline(iss, line)) {
197 if (first) { first = false; continue; }
198 if (line.empty()) continue;
199 vector<string> cols;
200 string cell;
201 istringstream ls(line);
202 while (getline(ls, cell, '\t')) cols.push_back(cell);
203 if (cols.size() < 12) continue;
204 int level = 0;
205 try { level = stoi(cols[0]); } catch (...) { continue; }
206 if (level != 5) continue;
207 int left = 0, top = 0, width = 0, height = 0, conf = 0;
208 try {
209 left = stoi(cols[6]);
210 top = stoi(cols[7]);
211 width = stoi(cols[8]);
212 height = stoi(cols[9]);
213 conf = stoi(cols[10]);
214 } catch (...) {
215 continue;
216 }
217 string text = cols[11];
218 ports::Word w;
219 w.bbox.x = left;
220 w.bbox.y = top;
221 w.bbox.width = width;
222 w.bbox.height = height;
223 w.text = text;
224 w.confidence = conf;
225 out.push_back(std::move(w));
226 }
227
228 delete[] tsv;
229 ocr.End();
230 pixDestroy(&pix);
231 return out;
232}
233
234std::pair<ports::Text, std::vector<ports::Word>> TesseractCore::extractFromBytes(
235 const std::vector<uint8_t>& data,
236 const std::string& tessdataPath,
237 const ports::Settings& recognition,
238 std::shared_ptr<core::ports::diagnostics::IDiagnostics> dbg) {
239 auto t = recognizeTextFromBytes(data, tessdataPath, recognition, dbg);
240 auto w = getWordsFromBytes(data, tessdataPath, recognition, dbg);
241 return {t, w};
242}
Declares the diagnostics artifact writing port.
static std::string resolveTessdataPath(const std::string &provided)
static std::string resolveLanguage(const ports::Settings &recognition)
static tesseract::OcrEngineMode toTesseractOem(ports::EngineMode mode)
static ports::Text recognizeTextFromBytes(const std::vector< uint8_t > &data, const std::string &tessdataPath, const ports::Settings &recognition, std::shared_ptr< core::ports::diagnostics::IDiagnostics > dbg)
static Pix * pixFromBytes(const std::vector< uint8_t > &data)
static std::string readEnvironmentVariable(const char *name)
static void applyPsm(tesseract::TessBaseAPI &ocr, int psm)
static std::filesystem::path executableDirectory()
static vector< ports::Word > getWordsFromBytes(const std::vector< uint8_t > &data, const std::string &tessdataPath, const ports::Settings &recognition, std::shared_ptr< core::ports::diagnostics::IDiagnostics > dbg)
static void configureForStatements(tesseract::TessBaseAPI &ocr, const ports::Settings &recognition)
Declares the Tesseract implementation helpers used by the import workflow.
Shared text recognition data types.
static std::pair< core::ports::text_recognition::Text, std::vector< core::ports::text_recognition::Word > > extractFromBytes(const std::vector< uint8_t > &data, const std::string &tessdataPath, const core::ports::text_recognition::Settings &recognition, std::shared_ptr< core::ports::diagnostics::IDiagnostics > debugger=nullptr)
Extracts recognized text and word-level OCR data from image bytes.
Precompiled header for the text-recognition infrastructure module.