@luii/node-tesseract-ocr 2.1.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +3 -3
- package/README.md +461 -104
- package/binding-options.js +4 -0
- package/dist/cjs/index.cjs +21 -9
- package/dist/cjs/index.d.ts +4 -926
- package/dist/cjs/types.d.ts +1283 -0
- package/dist/cjs/types.js +17 -0
- package/dist/cjs/utils.js +15 -0
- package/dist/esm/index.d.ts +4 -926
- package/dist/esm/index.mjs +16 -9
- package/dist/esm/types.d.ts +1283 -0
- package/dist/esm/types.js +16 -0
- package/dist/esm/utils.js +15 -0
- package/package.json +6 -3
- package/prebuilds/node-tesseract-ocr-darwin-arm64/node-napi-v10.node +0 -0
- package/prebuilds/node-tesseract-ocr-linux-x64/node-napi-v10.node +0 -0
- package/src/commands.hpp +688 -88
- package/src/tesseract_wrapper.cpp +652 -187
- package/src/tesseract_wrapper.hpp +27 -2
- package/src/worker_thread.cpp +146 -2
- package/src/worker_thread.hpp +4 -1
package/src/commands.hpp
CHANGED
|
@@ -18,14 +18,21 @@
|
|
|
18
18
|
|
|
19
19
|
#include "monitor.hpp"
|
|
20
20
|
#include "utils.hpp"
|
|
21
|
+
#include <allheaders.h>
|
|
22
|
+
#include <atomic>
|
|
23
|
+
#include <cstddef>
|
|
24
|
+
#include <cstdint>
|
|
25
|
+
#include <exception>
|
|
26
|
+
#include <iostream>
|
|
21
27
|
#include <memory>
|
|
22
28
|
#include <napi.h>
|
|
23
29
|
#include <optional>
|
|
24
|
-
#include <
|
|
30
|
+
#include <ostream>
|
|
25
31
|
#include <string>
|
|
26
32
|
#include <tesseract/baseapi.h>
|
|
27
33
|
#include <tesseract/ocrclass.h>
|
|
28
34
|
#include <tesseract/publictypes.h>
|
|
35
|
+
#include <tesseract/renderer.h>
|
|
29
36
|
#include <unordered_map>
|
|
30
37
|
#include <variant>
|
|
31
38
|
#include <vector>
|
|
@@ -52,20 +59,27 @@ struct ResultString {
|
|
|
52
59
|
std::string value;
|
|
53
60
|
};
|
|
54
61
|
|
|
62
|
+
struct ResultBuffer {
|
|
63
|
+
std::vector<uint8_t> value;
|
|
64
|
+
};
|
|
65
|
+
|
|
55
66
|
using ObjectValue = std::variant<bool, int, double, float, std::string,
|
|
56
|
-
std::vector<std::string
|
|
67
|
+
std::vector<std::string>, std::vector<uint8_t>,
|
|
68
|
+
std::vector<int>>;
|
|
57
69
|
|
|
58
70
|
struct ResultObject {
|
|
59
71
|
std::unordered_map<std::string, ObjectValue> value;
|
|
60
72
|
};
|
|
61
73
|
|
|
74
|
+
using ArrayValue = std::variant<std::vector<int>, std::vector<std::string>>;
|
|
75
|
+
|
|
62
76
|
struct ResultArray {
|
|
63
|
-
|
|
77
|
+
ArrayValue value;
|
|
64
78
|
};
|
|
65
79
|
|
|
66
80
|
using Result =
|
|
67
81
|
std::variant<ResultVoid, ResultBool, ResultInt, ResultDouble, ResultFloat,
|
|
68
|
-
ResultString, ResultArray, ResultObject>;
|
|
82
|
+
ResultString, ResultArray, ResultBuffer, ResultObject>;
|
|
69
83
|
|
|
70
84
|
template <class... Ts> struct match : Ts... {
|
|
71
85
|
using Ts::operator()...;
|
|
@@ -73,6 +87,15 @@ template <class... Ts> struct match : Ts... {
|
|
|
73
87
|
|
|
74
88
|
template <class... Ts> match(Ts...) -> match<Ts...>;
|
|
75
89
|
|
|
90
|
+
template <typename T>
|
|
91
|
+
static Napi::Array VectorToNapiArray(Napi::Env env, const std::vector<T> &vec) {
|
|
92
|
+
Napi::Array arr = Napi::Array::New(env, vec.size());
|
|
93
|
+
for (size_t i = 0; i < vec.size(); ++i) {
|
|
94
|
+
arr.Set(static_cast<uint32_t>(i), vec[i]);
|
|
95
|
+
}
|
|
96
|
+
return arr;
|
|
97
|
+
}
|
|
98
|
+
|
|
76
99
|
static Napi::Value ToNapiValue(Napi::Env env, const ObjectValue &v) {
|
|
77
100
|
return std::visit(
|
|
78
101
|
match{
|
|
@@ -80,15 +103,18 @@ static Napi::Value ToNapiValue(Napi::Env env, const ObjectValue &v) {
|
|
|
80
103
|
[&](int i) -> Napi::Value { return Napi::Number::New(env, i); },
|
|
81
104
|
[&](double d) -> Napi::Value { return Napi::Number::New(env, d); },
|
|
82
105
|
[&](float f) -> Napi::Value { return Napi::Number::New(env, f); },
|
|
83
|
-
[&](const std::string &s) -> Napi::Value {
|
|
106
|
+
[&](const std::string &s) -> Napi::Value { // String
|
|
84
107
|
return Napi::String::New(env, s);
|
|
85
108
|
},
|
|
86
|
-
[&](const std::vector<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
109
|
+
[&](const std::vector<uint8_t> &vec) -> Napi::Value { // Buffer
|
|
110
|
+
return Napi::Buffer<uint8_t>::Copy(env, vec.data(), vec.size());
|
|
111
|
+
},
|
|
112
|
+
[&](const std::vector<int> &vec) -> Napi::Value {
|
|
113
|
+
return VectorToNapiArray(env, vec);
|
|
114
|
+
},
|
|
115
|
+
[&](const std::vector<std::string> &vec)
|
|
116
|
+
-> Napi::Value { // string array
|
|
117
|
+
return VectorToNapiArray(env, vec);
|
|
92
118
|
},
|
|
93
119
|
},
|
|
94
120
|
v);
|
|
@@ -112,19 +138,16 @@ inline Napi::Value MatchResult(Napi::Env env, const Result &r) {
|
|
|
112
138
|
[&](const ResultString &v) -> Napi::Value {
|
|
113
139
|
return Napi::String::New(env, v.value);
|
|
114
140
|
},
|
|
141
|
+
[&](const ResultBuffer &v) -> Napi::Value {
|
|
142
|
+
return Napi::Buffer<uint8_t>::Copy(env, v.value.data(),
|
|
143
|
+
v.value.size());
|
|
144
|
+
},
|
|
115
145
|
[&](const ResultArray &v) -> Napi::Value {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
Napi::Array array = Napi::Array::New(env, v.value.size());
|
|
124
|
-
for (size_t i = 0; i < v.value.size(); i++) {
|
|
125
|
-
array.Set(static_cast<uint32_t>(i), v.value[i]);
|
|
126
|
-
}
|
|
127
|
-
return array;
|
|
146
|
+
return std::visit(
|
|
147
|
+
[&](const auto &vec) -> Napi::Value {
|
|
148
|
+
return VectorToNapiArray(env, vec);
|
|
149
|
+
},
|
|
150
|
+
v.value);
|
|
128
151
|
},
|
|
129
152
|
[&](const ResultObject &v) -> Napi::Value {
|
|
130
153
|
Napi::Object obj = Napi::Object::New(env);
|
|
@@ -137,6 +160,179 @@ inline Napi::Value MatchResult(Napi::Env env, const Result &r) {
|
|
|
137
160
|
r);
|
|
138
161
|
}
|
|
139
162
|
|
|
163
|
+
inline void RequireInitialized(const std::atomic<bool> &initialized,
|
|
164
|
+
const char *method) {
|
|
165
|
+
if (!initialized.load(std::memory_order_acquire)) {
|
|
166
|
+
throw_runtime("{}: call init(...) first", method);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
struct CommandVersion {
|
|
171
|
+
Result invoke(tesseract::TessBaseAPI &api) const {
|
|
172
|
+
return ResultString{api.Version()};
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
struct CommandIsInitialized {
|
|
177
|
+
Result invoke(tesseract::TessBaseAPI &,
|
|
178
|
+
const std::atomic<bool> &initialized) const {
|
|
179
|
+
return ResultBool{initialized.load(std::memory_order_acquire)};
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
struct CommandSetInputName {
|
|
184
|
+
std::string input_name;
|
|
185
|
+
Result invoke(tesseract::TessBaseAPI &api) const {
|
|
186
|
+
api.SetInputName(input_name.c_str());
|
|
187
|
+
return ResultVoid{};
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
struct CommandGetInputName {
|
|
192
|
+
Result invoke(tesseract::TessBaseAPI &api) const {
|
|
193
|
+
return ResultString{api.GetInputName()};
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
struct CommandSetInputImage {
|
|
198
|
+
std::vector<uint8_t> bytes;
|
|
199
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
200
|
+
const std::atomic<bool> &initialized) const {
|
|
201
|
+
RequireInitialized(initialized, "setInputImage");
|
|
202
|
+
if (bytes.size() == 0) {
|
|
203
|
+
throw_runtime("setInputImage: input buffer is empty");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
Pix *pix = pixReadMem(bytes.data(), bytes.size());
|
|
207
|
+
if (pix == nullptr) {
|
|
208
|
+
throw_runtime("setInputImage: failed to decode image buffer");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// TessBaseAPI::SetInputImage takes ownership of pix.
|
|
212
|
+
api.SetInputImage(pix);
|
|
213
|
+
return ResultVoid{};
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
struct CommandGetInputImage {
|
|
218
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
219
|
+
const std::atomic<bool> &initialized) const {
|
|
220
|
+
RequireInitialized(initialized, "getInputImage");
|
|
221
|
+
Pix *source = api.GetInputImage();
|
|
222
|
+
|
|
223
|
+
std::cout << source << std::endl;
|
|
224
|
+
|
|
225
|
+
if (source == nullptr) {
|
|
226
|
+
throw_runtime("getInputImage: TessBaseAPI::GetInputImage returned null");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// GetInputImage has no caller-ownership contract; work on a clone.
|
|
230
|
+
Pix *pix = pixClone(source);
|
|
231
|
+
if (pix == nullptr) {
|
|
232
|
+
throw_runtime("getInputImage: failed to clone source image");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
l_uint32 *data = pixGetData(pix);
|
|
236
|
+
l_int32 wpl = pixGetWpl(pix);
|
|
237
|
+
l_int32 h = pixGetHeight(pix);
|
|
238
|
+
|
|
239
|
+
size_t bytecount = wpl * 4 * h;
|
|
240
|
+
const uint8_t *start = reinterpret_cast<const uint8_t *>(data);
|
|
241
|
+
std::vector<uint8_t> buffer(start, start + bytecount);
|
|
242
|
+
pixDestroy(&pix);
|
|
243
|
+
|
|
244
|
+
return ResultBuffer{buffer};
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
struct CommandGetSourceYResolution {
|
|
249
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
250
|
+
const std::atomic<bool> &initialized) const {
|
|
251
|
+
RequireInitialized(initialized, "getSourceYResolution");
|
|
252
|
+
int source_y_resolution = api.GetSourceYResolution();
|
|
253
|
+
return ResultInt{source_y_resolution};
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
struct CommandGetDataPath {
|
|
258
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
259
|
+
const std::atomic<bool> &initialized) const {
|
|
260
|
+
RequireInitialized(initialized, "getDataPath");
|
|
261
|
+
const char *data_path = api.GetDatapath();
|
|
262
|
+
|
|
263
|
+
if (data_path == nullptr) {
|
|
264
|
+
throw_runtime("getDataPath: TessBaseAPI::GetDatapath returned null");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return ResultString{data_path};
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
struct CommandSetOutputName {
|
|
272
|
+
std::string output_name;
|
|
273
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
274
|
+
const std::atomic<bool> &initialized) const {
|
|
275
|
+
RequireInitialized(initialized, "setOutputName");
|
|
276
|
+
if (output_name.empty()) {
|
|
277
|
+
throw_runtime("setOutputName: output name is empty");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
api.SetOutputName(output_name.c_str());
|
|
281
|
+
return ResultVoid{};
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
struct CommandClearPersistentCache {
|
|
286
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
287
|
+
const std::atomic<bool> &initialized) const {
|
|
288
|
+
RequireInitialized(initialized, "clearPersistentCache");
|
|
289
|
+
api.ClearPersistentCache();
|
|
290
|
+
return ResultVoid{};
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
struct CommandClearAdaptiveClassifier {
|
|
295
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
296
|
+
const std::atomic<bool> &initialized) const {
|
|
297
|
+
RequireInitialized(initialized, "clearAdaptiveClassifier");
|
|
298
|
+
api.ClearAdaptiveClassifier();
|
|
299
|
+
return ResultVoid{};
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
struct CommandGetThresholdedImage {
|
|
304
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
305
|
+
const std::atomic<bool> &initialized) const {
|
|
306
|
+
RequireInitialized(initialized, "getThresholdedImage");
|
|
307
|
+
Pix *pix = api.GetThresholdedImage();
|
|
308
|
+
|
|
309
|
+
if (pix == nullptr) {
|
|
310
|
+
throw_runtime("getThresholdedImage: TessBaseAPI::GetThresholdedImage "
|
|
311
|
+
"returned null");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
l_uint32 *data = pixGetData(pix);
|
|
315
|
+
l_int32 wpl = pixGetWpl(pix);
|
|
316
|
+
l_int32 h = pixGetHeight(pix);
|
|
317
|
+
|
|
318
|
+
size_t bytecount = wpl * 4 * h;
|
|
319
|
+
const uint8_t *start = reinterpret_cast<const uint8_t *>(data);
|
|
320
|
+
std::vector<uint8_t> buffer(start, start + bytecount);
|
|
321
|
+
pixDestroy(&pix);
|
|
322
|
+
|
|
323
|
+
return ResultBuffer{buffer};
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
struct CommandGetThresholdedImageScaleFactor {
|
|
328
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
329
|
+
const std::atomic<bool> &initialized) const {
|
|
330
|
+
RequireInitialized(initialized, "getThresholdedImageScaleFactor");
|
|
331
|
+
int scale_factor = api.GetThresholdedImageScaleFactor();
|
|
332
|
+
return ResultInt{scale_factor};
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
140
336
|
struct CommandInit {
|
|
141
337
|
std::string data_path, language;
|
|
142
338
|
tesseract::OcrEngineMode oem{tesseract::OEM_DEFAULT};
|
|
@@ -146,15 +342,17 @@ struct CommandInit {
|
|
|
146
342
|
std::vector<std::string> vars_values;
|
|
147
343
|
bool set_only_non_debug_params{false};
|
|
148
344
|
|
|
149
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
345
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
346
|
+
std::atomic<bool> &initialized) const {
|
|
150
347
|
const std::vector<std::string> *vv = vars_vec.empty() ? nullptr : &vars_vec;
|
|
151
348
|
const std::vector<std::string> *vval =
|
|
152
349
|
vars_values.empty() ? nullptr : &vars_values;
|
|
153
350
|
|
|
154
351
|
if ((vv == nullptr) != (vval == nullptr) ||
|
|
155
352
|
(vv && vv->size() != vval->size())) {
|
|
156
|
-
|
|
157
|
-
"vars_vec and vars_values must both be
|
|
353
|
+
throw_runtime(
|
|
354
|
+
"init: vars_vec and vars_values must either both be empty or have "
|
|
355
|
+
"the same length");
|
|
158
356
|
}
|
|
159
357
|
|
|
160
358
|
if (api.Init(data_path.empty() ? nullptr : data_path.c_str(),
|
|
@@ -163,15 +361,18 @@ struct CommandInit {
|
|
|
163
361
|
: const_cast<char **>(configs.data()),
|
|
164
362
|
static_cast<int>(configs.size()), vv, vval,
|
|
165
363
|
set_only_non_debug_params) != 0) {
|
|
166
|
-
|
|
364
|
+
throw_runtime("init: TessBaseAPI::Init returned non-zero status");
|
|
167
365
|
}
|
|
168
366
|
|
|
367
|
+
initialized.store(true, std::memory_order_release);
|
|
169
368
|
return ResultVoid{};
|
|
170
369
|
}
|
|
171
370
|
};
|
|
172
371
|
|
|
173
372
|
struct CommandInitForAnalysePage {
|
|
174
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
373
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
374
|
+
const std::atomic<bool> &initialized) const {
|
|
375
|
+
RequireInitialized(initialized, "initForAnalysePage");
|
|
175
376
|
api.InitForAnalysePage();
|
|
176
377
|
return ResultVoid{};
|
|
177
378
|
}
|
|
@@ -179,13 +380,15 @@ struct CommandInitForAnalysePage {
|
|
|
179
380
|
|
|
180
381
|
struct CommandAnalyseLayout {
|
|
181
382
|
bool merge_similar_words = false;
|
|
182
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
383
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
384
|
+
const std::atomic<bool> &initialized) const {
|
|
385
|
+
RequireInitialized(initialized, "analyseLayout");
|
|
183
386
|
|
|
184
387
|
tesseract::PageIterator *p_iter = api.AnalyseLayout(merge_similar_words);
|
|
185
388
|
|
|
186
389
|
// returns nullptr on error or empty page
|
|
187
390
|
if (p_iter == nullptr) {
|
|
188
|
-
|
|
391
|
+
throw_runtime("analyseLayout: TessBaseAPI::AnalyseLayout returned null");
|
|
189
392
|
}
|
|
190
393
|
|
|
191
394
|
// Convert PageIterator to a feasible object here
|
|
@@ -194,21 +397,273 @@ struct CommandAnalyseLayout {
|
|
|
194
397
|
}
|
|
195
398
|
};
|
|
196
399
|
|
|
400
|
+
struct EncodedImageBuffer {
|
|
401
|
+
std::vector<uint8_t> bytes;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
struct ProcessPagesSession {
|
|
405
|
+
std::unique_ptr<tesseract::TessPDFRenderer> renderer;
|
|
406
|
+
std::string output_base;
|
|
407
|
+
int timeout_millisec{0};
|
|
408
|
+
bool textonly{false};
|
|
409
|
+
int next_page_index{0};
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
struct CommandBeginProcessPages {
|
|
413
|
+
std::string output_base;
|
|
414
|
+
std::string title;
|
|
415
|
+
int timeout_millisec{0}; // 0 = unlimited timeout
|
|
416
|
+
bool textonly{false};
|
|
417
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
418
|
+
std::optional<ProcessPagesSession> &session,
|
|
419
|
+
const std::atomic<bool> &initialized) const {
|
|
420
|
+
RequireInitialized(initialized, "beginProcessPages");
|
|
421
|
+
if (session.has_value()) {
|
|
422
|
+
throw_runtime(
|
|
423
|
+
"beginProcessPages called while a session is already active");
|
|
424
|
+
}
|
|
425
|
+
if (title.empty()) {
|
|
426
|
+
throw_runtime("beginProcessPages: title cannot be empty");
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const char *input_name = api.GetInputName();
|
|
430
|
+
std::string effective_output_base = output_base;
|
|
431
|
+
if (effective_output_base.empty()) {
|
|
432
|
+
if (input_name == nullptr || *input_name == '\0') {
|
|
433
|
+
throw_runtime("beginProcessPages: output_base is empty and "
|
|
434
|
+
"TessBaseAPI::GetInputName() returned null/empty");
|
|
435
|
+
}
|
|
436
|
+
effective_output_base = input_name;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
auto renderer = std::make_unique<tesseract::TessPDFRenderer>(
|
|
440
|
+
effective_output_base.c_str(), api.GetDatapath(), textonly);
|
|
441
|
+
if (!renderer->happy()) {
|
|
442
|
+
throw_runtime("beginProcessPages: renderer is not healthy");
|
|
443
|
+
}
|
|
444
|
+
if (!renderer->BeginDocument(title.c_str())) {
|
|
445
|
+
throw_runtime("beginProcessPages: could not begin document");
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
session.emplace();
|
|
449
|
+
session->renderer = std::move(renderer);
|
|
450
|
+
session->output_base = std::move(effective_output_base);
|
|
451
|
+
session->timeout_millisec = timeout_millisec;
|
|
452
|
+
session->textonly = textonly;
|
|
453
|
+
session->next_page_index = 0;
|
|
454
|
+
return ResultVoid{};
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
struct CommandAddProcessPage {
|
|
459
|
+
EncodedImageBuffer page;
|
|
460
|
+
std::string filename;
|
|
461
|
+
std::shared_ptr<MonitorContext> monitor_context;
|
|
462
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
463
|
+
std::optional<ProcessPagesSession> &session,
|
|
464
|
+
const std::atomic<bool> &initialized) const {
|
|
465
|
+
RequireInitialized(initialized, "addProcessPage");
|
|
466
|
+
if (!session.has_value()) {
|
|
467
|
+
throw_runtime("addProcessPage: called without an active session");
|
|
468
|
+
}
|
|
469
|
+
if (!session->renderer->happy()) {
|
|
470
|
+
throw_runtime("addProcessPage: renderer is not healthy");
|
|
471
|
+
}
|
|
472
|
+
if (page.bytes.empty()) {
|
|
473
|
+
throw_runtime("addProcessPage: buffer is empty");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
Pix *pix = pixReadMem(page.bytes.data(), page.bytes.size());
|
|
477
|
+
if (pix == nullptr) {
|
|
478
|
+
throw_runtime("addProcessPage: failed to decode image buffer");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (pixGetColormap(pix) != nullptr) {
|
|
482
|
+
Pix *no_cmap = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
|
|
483
|
+
if (no_cmap == nullptr) {
|
|
484
|
+
pixDestroy(&pix);
|
|
485
|
+
throw_runtime("addProcessPage: failed to remove image colormap");
|
|
486
|
+
}
|
|
487
|
+
if (no_cmap != pix) {
|
|
488
|
+
pixDestroy(&pix);
|
|
489
|
+
pix = no_cmap;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (pixGetSpp(pix) == 4) {
|
|
494
|
+
Pix *no_alpha = pixRemoveAlpha(pix);
|
|
495
|
+
if (no_alpha == nullptr) {
|
|
496
|
+
pixDestroy(&pix);
|
|
497
|
+
throw_runtime("addProcessPage: failed to remove alpha channel");
|
|
498
|
+
}
|
|
499
|
+
if (no_alpha != pix) {
|
|
500
|
+
pixDestroy(&pix);
|
|
501
|
+
pix = no_alpha;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const int depth = pixGetDepth(pix);
|
|
506
|
+
if (depth > 0 && depth < 8) {
|
|
507
|
+
Pix *normalized = pixConvertTo8(pix, false);
|
|
508
|
+
if (normalized == nullptr) {
|
|
509
|
+
pixDestroy(&pix);
|
|
510
|
+
throw_runtime(
|
|
511
|
+
"addProcessPage: failed to normalize low-bit-depth image");
|
|
512
|
+
}
|
|
513
|
+
if (normalized != pix) {
|
|
514
|
+
pixDestroy(&pix);
|
|
515
|
+
pix = normalized;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const int x_res = pixGetXRes(pix);
|
|
520
|
+
const int y_res = pixGetYRes(pix);
|
|
521
|
+
if (x_res <= 0 || y_res <= 0) {
|
|
522
|
+
pixSetResolution(pix, 300, 300);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const char *effective_filename =
|
|
526
|
+
filename.empty() ? nullptr : filename.c_str();
|
|
527
|
+
api.SetInputName(effective_filename);
|
|
528
|
+
api.SetImage(pix);
|
|
529
|
+
|
|
530
|
+
bool failed = false;
|
|
531
|
+
MonitorHandle handle{monitor_context};
|
|
532
|
+
auto *monitor = monitor_context ? &handle.monitor : nullptr;
|
|
533
|
+
|
|
534
|
+
if (session->timeout_millisec > 0) {
|
|
535
|
+
tesseract::ETEXT_DESC timeout_only_monitor{};
|
|
536
|
+
if (monitor != nullptr) {
|
|
537
|
+
monitor->set_deadline_msecs(session->timeout_millisec);
|
|
538
|
+
} else {
|
|
539
|
+
timeout_only_monitor.cancel = nullptr;
|
|
540
|
+
timeout_only_monitor.cancel_this = nullptr;
|
|
541
|
+
timeout_only_monitor.set_deadline_msecs(session->timeout_millisec);
|
|
542
|
+
monitor = &timeout_only_monitor;
|
|
543
|
+
}
|
|
544
|
+
failed = api.Recognize(monitor) < 0;
|
|
545
|
+
} else if (api.GetPageSegMode() == tesseract::PSM_OSD_ONLY ||
|
|
546
|
+
api.GetPageSegMode() == tesseract::PSM_AUTO_ONLY) {
|
|
547
|
+
tesseract::PageIterator *it = api.AnalyseLayout();
|
|
548
|
+
if (it == nullptr) {
|
|
549
|
+
failed = true;
|
|
550
|
+
} else {
|
|
551
|
+
delete it;
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
failed = api.Recognize(monitor) < 0;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (session->renderer && !failed) {
|
|
558
|
+
failed = !session->renderer->AddImage(&api);
|
|
559
|
+
}
|
|
560
|
+
pixDestroy(&pix);
|
|
561
|
+
|
|
562
|
+
if (!failed) {
|
|
563
|
+
session->next_page_index++;
|
|
564
|
+
return ResultVoid{};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
throw_runtime("addProcessPage: ProcessPage failed at page {}",
|
|
568
|
+
session->next_page_index);
|
|
569
|
+
return ResultVoid{};
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
struct CommandFinishProcessPages {
|
|
574
|
+
Result invoke(tesseract::TessBaseAPI &,
|
|
575
|
+
std::optional<ProcessPagesSession> &session,
|
|
576
|
+
const std::atomic<bool> &initialized) const {
|
|
577
|
+
RequireInitialized(initialized, "finishProcessPages");
|
|
578
|
+
if (!session.has_value()) {
|
|
579
|
+
throw_runtime("finishProcessPages: called without an active session");
|
|
580
|
+
}
|
|
581
|
+
if (!session->renderer->happy()) {
|
|
582
|
+
throw_runtime("finishProcessPages: renderer is not healthy");
|
|
583
|
+
}
|
|
584
|
+
if (!session->renderer->EndDocument()) {
|
|
585
|
+
throw_runtime("finishProcessPages: could not finalize document");
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
std::string output_filepath = session->output_base + ".pdf";
|
|
589
|
+
session.reset();
|
|
590
|
+
return ResultString{std::move(output_filepath)};
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
struct CommandAbortProcessPages {
|
|
595
|
+
std::string reason;
|
|
596
|
+
Result invoke(tesseract::TessBaseAPI &,
|
|
597
|
+
std::optional<ProcessPagesSession> &session) const {
|
|
598
|
+
session.reset();
|
|
599
|
+
return ResultVoid{};
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
struct CommandGetProcessPagesStatus {
|
|
604
|
+
Result invoke(tesseract::TessBaseAPI &,
|
|
605
|
+
std::optional<ProcessPagesSession> &session) const {
|
|
606
|
+
if (!session.has_value()) {
|
|
607
|
+
return ResultObject{{
|
|
608
|
+
{"active", false},
|
|
609
|
+
{"healthy", false},
|
|
610
|
+
{"processedPages", 0},
|
|
611
|
+
{"nextPageIndex", 0},
|
|
612
|
+
{"outputBase", std::string{}},
|
|
613
|
+
{"timeoutMillisec", 0},
|
|
614
|
+
{"textonly", false},
|
|
615
|
+
}};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return ResultObject{{
|
|
619
|
+
{"active", true},
|
|
620
|
+
{"healthy", session->renderer->happy()},
|
|
621
|
+
{"processedPages", session->next_page_index},
|
|
622
|
+
{"nextPageIndex", session->next_page_index},
|
|
623
|
+
{"outputBase", session->output_base},
|
|
624
|
+
{"timeoutMillisec", session->timeout_millisec},
|
|
625
|
+
{"textonly", session->textonly},
|
|
626
|
+
}};
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
struct CommandSetDebugVariable {
|
|
631
|
+
std::string name, value;
|
|
632
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
633
|
+
const std::atomic<bool> &initialized) const {
|
|
634
|
+
RequireInitialized(initialized, "setDebugVariable");
|
|
635
|
+
if (name.empty()) {
|
|
636
|
+
throw_runtime("setDebugVariable: variable name is empty");
|
|
637
|
+
} else if (value.empty()) {
|
|
638
|
+
throw_runtime("setDebugVariable: variable value is empty");
|
|
639
|
+
}
|
|
640
|
+
return ResultBool{api.SetDebugVariable(name.c_str(), value.c_str())};
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
|
|
197
644
|
struct CommandSetVariable {
|
|
198
645
|
std::string name, value;
|
|
199
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
646
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
647
|
+
const std::atomic<bool> &initialized) const {
|
|
648
|
+
RequireInitialized(initialized, "setVariable");
|
|
649
|
+
if (name.empty()) {
|
|
650
|
+
throw_runtime("setVariable: variable name is empty");
|
|
651
|
+
} else if (value.empty()) {
|
|
652
|
+
throw_runtime("setVariable: variable value is empty");
|
|
653
|
+
}
|
|
200
654
|
return ResultBool{api.SetVariable(name.c_str(), value.c_str())};
|
|
201
655
|
}
|
|
202
656
|
};
|
|
203
657
|
|
|
204
658
|
struct CommandGetIntVariable {
|
|
205
659
|
std::string name;
|
|
206
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
660
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
661
|
+
const std::atomic<bool> &initialized) const {
|
|
662
|
+
RequireInitialized(initialized, "getIntVariable");
|
|
207
663
|
int value;
|
|
208
664
|
if (!api.GetIntVariable(name.c_str(), &value)) {
|
|
209
|
-
throw_runtime(
|
|
210
|
-
|
|
211
|
-
name.c_str());
|
|
665
|
+
throw_runtime("getIntVariable: variable '{}' was not found",
|
|
666
|
+
name.c_str());
|
|
212
667
|
}
|
|
213
668
|
|
|
214
669
|
return ResultInt{value};
|
|
@@ -217,11 +672,12 @@ struct CommandGetIntVariable {
|
|
|
217
672
|
|
|
218
673
|
struct CommandGetBoolVariable {
|
|
219
674
|
std::string name;
|
|
220
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
675
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
676
|
+
const std::atomic<bool> &initialized) const {
|
|
677
|
+
RequireInitialized(initialized, "getBoolVariable");
|
|
221
678
|
bool value;
|
|
222
679
|
if (!api.GetBoolVariable(name.c_str(), &value)) {
|
|
223
|
-
throw_runtime("
|
|
224
|
-
"was not found",
|
|
680
|
+
throw_runtime("getBoolVariable: variable '{}' was not found",
|
|
225
681
|
name.c_str());
|
|
226
682
|
}
|
|
227
683
|
return ResultBool{value};
|
|
@@ -230,11 +686,12 @@ struct CommandGetBoolVariable {
|
|
|
230
686
|
|
|
231
687
|
struct CommandGetDoubleVariable {
|
|
232
688
|
std::string name;
|
|
233
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
689
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
690
|
+
const std::atomic<bool> &initialized) const {
|
|
691
|
+
RequireInitialized(initialized, "getDoubleVariable");
|
|
234
692
|
double value;
|
|
235
693
|
if (!api.GetDoubleVariable(name.c_str(), &value)) {
|
|
236
|
-
throw_runtime("
|
|
237
|
-
"was not found",
|
|
694
|
+
throw_runtime("getDoubleVariable: variable '{}' was not found",
|
|
238
695
|
name.c_str());
|
|
239
696
|
}
|
|
240
697
|
return ResultDouble{value};
|
|
@@ -243,30 +700,27 @@ struct CommandGetDoubleVariable {
|
|
|
243
700
|
|
|
244
701
|
struct CommandGetStringVariable {
|
|
245
702
|
std::string name;
|
|
246
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
703
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
704
|
+
const std::atomic<bool> &initialized) const {
|
|
705
|
+
RequireInitialized(initialized, "getStringVariable");
|
|
247
706
|
auto value = api.GetStringVariable(name.c_str());
|
|
248
707
|
if (value == nullptr) {
|
|
249
|
-
throw_runtime("
|
|
250
|
-
"was not found",
|
|
708
|
+
throw_runtime("getStringVariable: variable '{}' was not found",
|
|
251
709
|
name.c_str());
|
|
252
710
|
}
|
|
253
711
|
return ResultString{value};
|
|
254
712
|
}
|
|
255
713
|
};
|
|
256
714
|
|
|
257
|
-
// struct CommandPrintVariables {
|
|
258
|
-
// Result invoke(tesseract::TessBaseAPI &api) const {
|
|
259
|
-
// api.PrintVariables(FILE *fp);
|
|
260
|
-
// }
|
|
261
|
-
// };
|
|
262
|
-
|
|
263
715
|
struct CommandSetImage {
|
|
264
716
|
std::vector<uint8_t> bytes;
|
|
265
717
|
int width = 0;
|
|
266
718
|
int height = 0;
|
|
267
719
|
int bytes_per_pixel = 0; // bpp/8
|
|
268
720
|
int bytes_per_line = 0;
|
|
269
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
721
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
722
|
+
const std::atomic<bool> &initialized) const {
|
|
723
|
+
RequireInitialized(initialized, "setImage");
|
|
270
724
|
api.SetImage(bytes.data(), width, height, bytes_per_pixel, bytes_per_line);
|
|
271
725
|
return ResultVoid{};
|
|
272
726
|
}
|
|
@@ -274,12 +728,14 @@ struct CommandSetImage {
|
|
|
274
728
|
|
|
275
729
|
struct CommandSetPageMode {
|
|
276
730
|
tesseract::PageSegMode psm;
|
|
277
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
731
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
732
|
+
const std::atomic<bool> &initialized) const {
|
|
733
|
+
RequireInitialized(initialized, "setPageMode");
|
|
278
734
|
if (psm < 0 || psm >= tesseract::PageSegMode::PSM_COUNT) {
|
|
279
735
|
|
|
280
|
-
throw_runtime(
|
|
281
|
-
|
|
282
|
-
|
|
736
|
+
throw_runtime("setPageMode: page segmentation mode is out of range; "
|
|
737
|
+
"received {}",
|
|
738
|
+
static_cast<int>(psm));
|
|
283
739
|
}
|
|
284
740
|
api.SetPageSegMode(psm);
|
|
285
741
|
return ResultVoid{};
|
|
@@ -288,7 +744,9 @@ struct CommandSetPageMode {
|
|
|
288
744
|
|
|
289
745
|
struct CommandSetRectangle {
|
|
290
746
|
int left, top, width, height;
|
|
291
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
747
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
748
|
+
const std::atomic<bool> &initialized) const {
|
|
749
|
+
RequireInitialized(initialized, "setRectangle");
|
|
292
750
|
api.SetRectangle(left, top, width, height);
|
|
293
751
|
return ResultVoid{};
|
|
294
752
|
}
|
|
@@ -296,7 +754,9 @@ struct CommandSetRectangle {
|
|
|
296
754
|
|
|
297
755
|
struct CommandSetSourceResolution {
|
|
298
756
|
int ppi;
|
|
299
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
757
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
758
|
+
const std::atomic<bool> &initialized) const {
|
|
759
|
+
RequireInitialized(initialized, "setSourceResolution");
|
|
300
760
|
api.SetSourceResolution(ppi);
|
|
301
761
|
return ResultVoid{};
|
|
302
762
|
}
|
|
@@ -304,11 +764,14 @@ struct CommandSetSourceResolution {
|
|
|
304
764
|
|
|
305
765
|
struct CommandRecognize {
|
|
306
766
|
std::shared_ptr<MonitorContext> monitor_context;
|
|
307
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
767
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
768
|
+
const std::atomic<bool> &initialized) const {
|
|
769
|
+
RequireInitialized(initialized, "recognize");
|
|
308
770
|
MonitorHandle handle{monitor_context};
|
|
309
771
|
auto *monitor = monitor_context ? &handle.monitor : nullptr;
|
|
310
772
|
if (api.Recognize(monitor) != 0) {
|
|
311
|
-
|
|
773
|
+
throw_runtime(
|
|
774
|
+
"recognize: TessBaseAPI::Recognize returned non-zero status");
|
|
312
775
|
}
|
|
313
776
|
return ResultVoid{};
|
|
314
777
|
}
|
|
@@ -321,7 +784,9 @@ struct CommandRecognize {
|
|
|
321
784
|
// };
|
|
322
785
|
|
|
323
786
|
struct CommandDetectOrientationScript {
|
|
324
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
787
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
788
|
+
const std::atomic<bool> &initialized) const {
|
|
789
|
+
RequireInitialized(initialized, "detectOrientationScript");
|
|
325
790
|
int orient_deg;
|
|
326
791
|
float orient_conf;
|
|
327
792
|
const char *script_name;
|
|
@@ -329,8 +794,9 @@ struct CommandDetectOrientationScript {
|
|
|
329
794
|
|
|
330
795
|
if (!api.DetectOrientationScript(&orient_deg, &orient_conf, &script_name,
|
|
331
796
|
&script_conf)) {
|
|
332
|
-
|
|
333
|
-
"
|
|
797
|
+
throw_runtime(
|
|
798
|
+
"detectOrientationScript: TessBaseAPI::DetectOrientationScript "
|
|
799
|
+
"returned false");
|
|
334
800
|
}
|
|
335
801
|
|
|
336
802
|
return ResultObject{{
|
|
@@ -343,16 +809,122 @@ struct CommandDetectOrientationScript {
|
|
|
343
809
|
};
|
|
344
810
|
|
|
345
811
|
struct CommandMeanTextConf {
|
|
346
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
812
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
813
|
+
const std::atomic<bool> &initialized) const {
|
|
814
|
+
RequireInitialized(initialized, "meanTextConf");
|
|
347
815
|
return ResultInt{api.MeanTextConf()};
|
|
348
816
|
}
|
|
349
817
|
};
|
|
350
818
|
|
|
819
|
+
struct CommandGetPAGEText {
|
|
820
|
+
int page_number;
|
|
821
|
+
std::shared_ptr<MonitorContext> monitor_context;
|
|
822
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
823
|
+
const std::atomic<bool> &initialized) const {
|
|
824
|
+
RequireInitialized(initialized, "getPAGEText");
|
|
825
|
+
MonitorHandle handle{monitor_context};
|
|
826
|
+
auto *monitor = monitor_context ? &handle.monitor : nullptr;
|
|
827
|
+
char *page_text = api.GetPAGEText(monitor, page_number);
|
|
828
|
+
if (!page_text) {
|
|
829
|
+
throw_runtime("getPAGEText: TessBaseAPI::GetPAGEText returned null");
|
|
830
|
+
}
|
|
831
|
+
std::string text = std::string{page_text};
|
|
832
|
+
|
|
833
|
+
delete[] page_text;
|
|
834
|
+
return ResultString(text);
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
struct CommandGetLSTMBoxText {
|
|
839
|
+
int page_number;
|
|
840
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
841
|
+
const std::atomic<bool> &initialized) const {
|
|
842
|
+
RequireInitialized(initialized, "getLSTMBoxText");
|
|
843
|
+
char *lstm_box_text = api.GetLSTMBoxText(page_number);
|
|
844
|
+
if (!lstm_box_text) {
|
|
845
|
+
throw_runtime(
|
|
846
|
+
"getLSTMBoxText: TessBaseAPI::GetLSTMBoxText returned null");
|
|
847
|
+
}
|
|
848
|
+
std::string text = std::string{lstm_box_text};
|
|
849
|
+
|
|
850
|
+
delete[] lstm_box_text;
|
|
851
|
+
return ResultString(text);
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
struct CommandGetBoxText {
|
|
856
|
+
int page_number;
|
|
857
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
858
|
+
const std::atomic<bool> &initialized) const {
|
|
859
|
+
RequireInitialized(initialized, "getBoxText");
|
|
860
|
+
char *box_text = api.GetBoxText(page_number);
|
|
861
|
+
if (!box_text) {
|
|
862
|
+
throw_runtime("getBoxText: TessBaseAPI::GetBoxText returned null");
|
|
863
|
+
}
|
|
864
|
+
std::string text = std::string{box_text};
|
|
865
|
+
|
|
866
|
+
delete[] box_text;
|
|
867
|
+
return ResultString(text);
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
struct CommandGetWordStrBoxText {
|
|
872
|
+
int page_number;
|
|
873
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
874
|
+
const std::atomic<bool> &initialized) const {
|
|
875
|
+
RequireInitialized(initialized, "getWordStrBoxText");
|
|
876
|
+
char *word_str_box_text = api.GetWordStrBoxText(page_number);
|
|
877
|
+
if (!word_str_box_text) {
|
|
878
|
+
throw_runtime(
|
|
879
|
+
"getWordStrBoxText: TessBaseAPI::GetWordStrBoxText returned null");
|
|
880
|
+
}
|
|
881
|
+
std::string text = std::string{word_str_box_text};
|
|
882
|
+
|
|
883
|
+
delete[] word_str_box_text;
|
|
884
|
+
return ResultString(text);
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
struct CommandGetOSDText {
|
|
889
|
+
int page_number;
|
|
890
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
891
|
+
const std::atomic<bool> &initialized) const {
|
|
892
|
+
RequireInitialized(initialized, "getOSDText");
|
|
893
|
+
char *ost_text = api.GetOsdText(page_number);
|
|
894
|
+
if (!ost_text) {
|
|
895
|
+
throw_runtime("getOSDText: TessBaseAPI::GetOsdText returned null");
|
|
896
|
+
}
|
|
897
|
+
std::string text = std::string{ost_text};
|
|
898
|
+
|
|
899
|
+
delete[] ost_text;
|
|
900
|
+
return ResultString(text);
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
struct CommandAllWordConfidences {
|
|
905
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
906
|
+
const std::atomic<bool> &initialized) const {
|
|
907
|
+
RequireInitialized(initialized, "allWordConfidences");
|
|
908
|
+
int *all_word_confidences = api.AllWordConfidences();
|
|
909
|
+
|
|
910
|
+
std::vector<int> confidences;
|
|
911
|
+
if (all_word_confidences != nullptr) {
|
|
912
|
+
for (int i = 0; all_word_confidences[i] != -1; ++i) {
|
|
913
|
+
confidences.push_back(all_word_confidences[i]);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
delete[] all_word_confidences;
|
|
917
|
+
return ResultArray{confidences};
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
|
|
351
921
|
struct CommandGetUTF8Text {
|
|
352
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
922
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
923
|
+
const std::atomic<bool> &initialized) const {
|
|
924
|
+
RequireInitialized(initialized, "getUTF8Text");
|
|
353
925
|
char *utf8_text = api.GetUTF8Text();
|
|
354
926
|
if (!utf8_text) {
|
|
355
|
-
throw_runtime("GetUTF8Text returned null");
|
|
927
|
+
throw_runtime("getUTF8Text: TessBaseAPI::GetUTF8Text returned null");
|
|
356
928
|
}
|
|
357
929
|
std::string text = std::string{utf8_text};
|
|
358
930
|
|
|
@@ -364,13 +936,15 @@ struct CommandGetUTF8Text {
|
|
|
364
936
|
struct CommandGetHOCRText {
|
|
365
937
|
int page_number;
|
|
366
938
|
std::shared_ptr<MonitorContext> monitor_context;
|
|
367
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
939
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
940
|
+
const std::atomic<bool> &initialized) const {
|
|
941
|
+
RequireInitialized(initialized, "getHOCRText");
|
|
368
942
|
|
|
369
943
|
MonitorHandle handle{monitor_context};
|
|
370
944
|
auto *monitor = monitor_context ? &handle.monitor : nullptr;
|
|
371
945
|
char *hocr_text = api.GetHOCRText(monitor, page_number);
|
|
372
946
|
if (!hocr_text) {
|
|
373
|
-
throw_runtime("GetHOCRText returned null");
|
|
947
|
+
throw_runtime("getHOCRText: TessBaseAPI::GetHOCRText returned null");
|
|
374
948
|
}
|
|
375
949
|
|
|
376
950
|
std::string text = std::string{hocr_text};
|
|
@@ -382,10 +956,12 @@ struct CommandGetHOCRText {
|
|
|
382
956
|
|
|
383
957
|
struct CommandGetTSVText {
|
|
384
958
|
int page_number;
|
|
385
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
959
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
960
|
+
const std::atomic<bool> &initialized) const {
|
|
961
|
+
RequireInitialized(initialized, "getTSVText");
|
|
386
962
|
char *tsv_text = api.GetTSVText(page_number);
|
|
387
963
|
if (!tsv_text) {
|
|
388
|
-
throw_runtime("GetTSVText returned null");
|
|
964
|
+
throw_runtime("getTSVText: TessBaseAPI::GetTSVText returned null");
|
|
389
965
|
}
|
|
390
966
|
std::string text = std::string{tsv_text};
|
|
391
967
|
|
|
@@ -395,10 +971,12 @@ struct CommandGetTSVText {
|
|
|
395
971
|
};
|
|
396
972
|
|
|
397
973
|
struct CommandGetUNLVText {
|
|
398
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
974
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
975
|
+
const std::atomic<bool> &initialized) const {
|
|
976
|
+
RequireInitialized(initialized, "getUNLVText");
|
|
399
977
|
char *unlv_text = api.GetUNLVText();
|
|
400
978
|
if (!unlv_text) {
|
|
401
|
-
throw_runtime("GetUNLVText returned null");
|
|
979
|
+
throw_runtime("getUNLVText: TessBaseAPI::GetUNLVText returned null");
|
|
402
980
|
}
|
|
403
981
|
std::string text = std::string{unlv_text};
|
|
404
982
|
delete[] unlv_text;
|
|
@@ -409,12 +987,14 @@ struct CommandGetUNLVText {
|
|
|
409
987
|
struct CommandGetALTOText {
|
|
410
988
|
int page_number;
|
|
411
989
|
std::shared_ptr<MonitorContext> monitor_context;
|
|
412
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
990
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
991
|
+
const std::atomic<bool> &initialized) const {
|
|
992
|
+
RequireInitialized(initialized, "getALTOText");
|
|
413
993
|
MonitorHandle handle{monitor_context};
|
|
414
994
|
auto *monitor = monitor_context ? &handle.monitor : nullptr;
|
|
415
995
|
char *alto_text = api.GetAltoText(monitor, page_number);
|
|
416
996
|
if (!alto_text) {
|
|
417
|
-
throw_runtime("
|
|
997
|
+
throw_runtime("getALTOText: TessBaseAPI::GetAltoText returned null");
|
|
418
998
|
}
|
|
419
999
|
std::string text = std::string{alto_text};
|
|
420
1000
|
delete[] alto_text;
|
|
@@ -424,12 +1004,15 @@ struct CommandGetALTOText {
|
|
|
424
1004
|
};
|
|
425
1005
|
|
|
426
1006
|
struct CommandGetInitLanguages {
|
|
427
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
1007
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
1008
|
+
const std::atomic<bool> &initialized) const {
|
|
1009
|
+
RequireInitialized(initialized, "getInitLanguages");
|
|
428
1010
|
const char *p_init_languages = api.GetInitLanguagesAsString();
|
|
429
1011
|
|
|
430
1012
|
if (p_init_languages == nullptr) {
|
|
431
|
-
|
|
432
|
-
|
|
1013
|
+
throw_runtime("getInitLanguages: TessBaseAPI::GetInitLanguagesAsString "
|
|
1014
|
+
"returned null; call init(...) first with at least one "
|
|
1015
|
+
"valid language");
|
|
433
1016
|
}
|
|
434
1017
|
|
|
435
1018
|
std::string init_languages = std::string{p_init_languages};
|
|
@@ -439,7 +1022,9 @@ struct CommandGetInitLanguages {
|
|
|
439
1022
|
};
|
|
440
1023
|
|
|
441
1024
|
struct CommandGetLoadedLanguages {
|
|
442
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
1025
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
1026
|
+
const std::atomic<bool> &initialized) const {
|
|
1027
|
+
RequireInitialized(initialized, "getLoadedLanguages");
|
|
443
1028
|
std::vector<std::string> langs;
|
|
444
1029
|
api.GetLoadedLanguagesAsVector(&langs);
|
|
445
1030
|
return ResultArray{langs};
|
|
@@ -455,29 +1040,42 @@ struct CommandGetAvailableLanguages {
|
|
|
455
1040
|
};
|
|
456
1041
|
|
|
457
1042
|
struct CommandClear {
|
|
458
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
1043
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
1044
|
+
const std::atomic<bool> &initialized) const {
|
|
1045
|
+
RequireInitialized(initialized, "clear");
|
|
459
1046
|
api.Clear();
|
|
460
1047
|
return ResultVoid{};
|
|
461
1048
|
}
|
|
462
1049
|
};
|
|
463
1050
|
|
|
464
1051
|
struct CommandEnd {
|
|
465
|
-
Result invoke(tesseract::TessBaseAPI &api
|
|
1052
|
+
Result invoke(tesseract::TessBaseAPI &api,
|
|
1053
|
+
std::atomic<bool> &initialized) const {
|
|
466
1054
|
api.End();
|
|
1055
|
+
initialized.store(false, std::memory_order_release);
|
|
467
1056
|
return ResultVoid{};
|
|
468
1057
|
}
|
|
469
1058
|
};
|
|
470
1059
|
|
|
471
1060
|
using Command = std::variant<
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
1061
|
+
CommandVersion, CommandIsInitialized, CommandInit,
|
|
1062
|
+
CommandInitForAnalysePage, CommandSetVariable, CommandSetDebugVariable,
|
|
1063
|
+
CommandGetIntVariable, CommandGetBoolVariable, CommandGetDoubleVariable,
|
|
1064
|
+
CommandGetStringVariable, CommandSetInputName, CommandGetInputName,
|
|
1065
|
+
CommandSetOutputName, CommandGetDataPath, CommandSetInputImage,
|
|
1066
|
+
CommandGetInputImage, CommandSetPageMode, CommandSetRectangle,
|
|
1067
|
+
CommandSetSourceResolution, CommandGetSourceYResolution, CommandSetImage,
|
|
1068
|
+
CommandGetThresholdedImage, CommandGetThresholdedImageScaleFactor,
|
|
1069
|
+
CommandRecognize, CommandAnalyseLayout, CommandDetectOrientationScript,
|
|
1070
|
+
CommandMeanTextConf, CommandAllWordConfidences, CommandGetUTF8Text,
|
|
1071
|
+
CommandGetHOCRText, CommandGetTSVText, CommandGetUNLVText,
|
|
1072
|
+
CommandGetALTOText, CommandGetPAGEText, CommandGetLSTMBoxText,
|
|
1073
|
+
CommandGetBoxText, CommandGetWordStrBoxText, CommandGetOSDText,
|
|
1074
|
+
CommandBeginProcessPages, CommandAddProcessPage, CommandFinishProcessPages,
|
|
1075
|
+
CommandAbortProcessPages, CommandGetProcessPagesStatus,
|
|
1076
|
+
CommandGetInitLanguages, CommandGetLoadedLanguages,
|
|
1077
|
+
CommandGetAvailableLanguages, CommandClearPersistentCache,
|
|
1078
|
+
CommandClearAdaptiveClassifier, CommandClear, CommandEnd>;
|
|
481
1079
|
|
|
482
1080
|
struct Job {
|
|
483
1081
|
Command command;
|
|
@@ -485,4 +1083,6 @@ struct Job {
|
|
|
485
1083
|
|
|
486
1084
|
std::optional<Result> result;
|
|
487
1085
|
std::optional<std::string> error;
|
|
1086
|
+
std::optional<std::string> error_code;
|
|
1087
|
+
std::optional<std::string> error_method;
|
|
488
1088
|
};
|