1 /**********************************************************************
2 * File: pgedit.cpp (Formerly pgeditor.c)
3 * Description: Page structure file editor
4 * Author: Phil Cheatle
5 *
6 *(C) Copyright 1991, Hewlett-Packard Ltd.
7 ** Licensed under the Apache License, Version 2.0(the "License");
8 ** you may not use this file except in compliance with the License.
9 ** You may obtain a copy of the License at
10 ** http:// www.apache.org/licenses/LICENSE-2.0
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 *
17 **********************************************************************/
18
19 // Include automatically generated configuration file if running autoconf.
20 #ifdef HAVE_CONFIG_H
21 # include "config_auto.h"
22 #endif
23
24 #include "pgedit.h"
25
26 #include "blread.h"
27 #include "control.h"
28 #include "pageres.h"
29 #include "paramsd.h"
30 #include "scrollview.h"
31 #include "statistc.h"
32 #include "svmnode.h"
33 #include "tesseractclass.h"
34 #include "tordmain.h"
35 #include "werdit.h"
36
37 #include <cctype>
38 #include <cmath>
39
40 #ifndef GRAPHICS_DISABLED
41 namespace tesseract {
42 # define ASC_HEIGHT (2 * kBlnBaselineOffset + kBlnXHeight)
43 # define X_HEIGHT (kBlnBaselineOffset + kBlnXHeight)
44 # define BL_HEIGHT kBlnBaselineOffset
45 # define DESC_HEIGHT 0
46
47 enum CMD_EVENTS {
48 NULL_CMD_EVENT,
49 CHANGE_DISP_CMD_EVENT,
50 DUMP_WERD_CMD_EVENT,
51 SHOW_POINT_CMD_EVENT,
52 SHOW_BLN_WERD_CMD_EVENT,
53 DEBUG_WERD_CMD_EVENT,
54 BLAMER_CMD_EVENT,
55 BOUNDING_BOX_CMD_EVENT,
56 CORRECT_TEXT_CMD_EVENT,
57 POLYGONAL_CMD_EVENT,
58 BL_NORM_CMD_EVENT,
59 BITMAP_CMD_EVENT,
60 IMAGE_CMD_EVENT,
61 BLOCKS_CMD_EVENT,
62 BASELINES_CMD_EVENT,
63 UNIFORM_DISP_CMD_EVENT,
64 REFRESH_CMD_EVENT,
65 QUIT_CMD_EVENT,
66 RECOG_WERDS,
67 RECOG_PSEUDO,
68 SHOW_BLOB_FEATURES,
69 SHOW_SUBSCRIPT_CMD_EVENT,
70 SHOW_SUPERSCRIPT_CMD_EVENT,
71 SHOW_ITALIC_CMD_EVENT,
72 SHOW_BOLD_CMD_EVENT,
73 SHOW_UNDERLINE_CMD_EVENT,
74 SHOW_FIXEDPITCH_CMD_EVENT,
75 SHOW_SERIF_CMD_EVENT,
76 SHOW_SMALLCAPS_CMD_EVENT,
77 SHOW_DROPCAPS_CMD_EVENT,
78 };
79
80 enum ColorationMode {
81 CM_RAINBOW,
82 CM_SUBSCRIPT,
83 CM_SUPERSCRIPT,
84 CM_ITALIC,
85 CM_BOLD,
86 CM_UNDERLINE,
87 CM_FIXEDPITCH,
88 CM_SERIF,
89 CM_SMALLCAPS,
90 CM_DROPCAPS
91 };
92
93 /*
94 *
95 * Some global data
96 *
97 */
98
99 static ScrollView *image_win;
100 static ParamsEditor *pe;
101 static bool stillRunning = false;
102
103 static ScrollView *bln_word_window = nullptr; // baseline norm words
104
105 static CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT; // selected words op
106
107 static bool recog_done = false; // recog_all_words was called
108
109 // These variables should remain global, since they are only used for the
110 // debug mode (in which only a single Tesseract thread/instance will exist).
111 static std::bitset<16> word_display_mode;
112 static ColorationMode color_mode = CM_RAINBOW;
113 static bool display_image = false;
114 static bool display_blocks = false;
115 static bool display_baselines = false;
116
117 static PAGE_RES *current_page_res = nullptr;
118
119 STRING_VAR(editor_image_win_name, "EditorImage", "Editor image window name");
120 INT_VAR(editor_image_xpos, 590, "Editor image X Pos");
121 INT_VAR(editor_image_ypos, 10, "Editor image Y Pos");
122 static INT_VAR(editor_image_menuheight, 50, "Add to image height for menu bar");
123 INT_VAR(editor_image_word_bb_color, ScrollView::BLUE, "Word bounding box colour");
124 INT_VAR(editor_image_blob_bb_color, ScrollView::YELLOW, "Blob bounding box colour");
125
126 STRING_VAR(editor_word_name, "BlnWords", "BL normalized word window");
127 INT_VAR(editor_word_xpos, 60, "Word window X Pos");
128 INT_VAR(editor_word_ypos, 510, "Word window Y Pos");
129 INT_VAR(editor_word_height, 240, "Word window height");
130 INT_VAR(editor_word_width, 655, "Word window width");
131
132 /**
133 * show_point()
134 *
135 * Show coords of point, blob bounding box, word bounding box and offset from
136 * row baseline
137 */
138
show_point(PAGE_RES * page_res,float x,float y)139 static void show_point(PAGE_RES *page_res, float x, float y) {
140 FCOORD pt(x, y);
141 PAGE_RES_IT pr_it(page_res);
142
143 const int kBufsize = 512;
144 char msg[kBufsize];
145 char *msg_ptr = msg;
146
147 msg_ptr += sprintf(msg_ptr, "Pt:(%0.3f, %0.3f) ", x, y);
148
149 for (WERD_RES *word = pr_it.word(); word != nullptr; word = pr_it.forward()) {
150 if (pr_it.row() != pr_it.prev_row() && pr_it.row()->row->bounding_box().contains(pt)) {
151 msg_ptr += sprintf(msg_ptr, "BL(x)=%0.3f ", pr_it.row()->row->base_line(x));
152 }
153 if (word->word->bounding_box().contains(pt)) {
154 TBOX box = word->word->bounding_box();
155 msg_ptr += sprintf(msg_ptr, "Wd(%d, %d)/(%d, %d) ", box.left(), box.bottom(), box.right(),
156 box.top());
157 C_BLOB_IT cblob_it(word->word->cblob_list());
158 for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list(); cblob_it.forward()) {
159 C_BLOB *cblob = cblob_it.data();
160 box = cblob->bounding_box();
161 if (box.contains(pt)) {
162 msg_ptr += sprintf(msg_ptr, "CBlb(%d, %d)/(%d, %d) ", box.left(), box.bottom(),
163 box.right(), box.top());
164 }
165 }
166 }
167 }
168 image_win->AddMessage(msg);
169 }
170
171 /**
172 * pgeditor_msg()
173 *
174 * Display a message - in the command window if there is one, or to stdout
175 */
176
pgeditor_msg(const char * msg)177 static void pgeditor_msg( // message display
178 const char *msg) {
179 image_win->AddMessage(msg);
180 }
181
182 class BlnEventHandler : public SVEventHandler {
183 public:
Notify(const SVEvent * sv_event)184 void Notify(const SVEvent *sv_event) override {
185 if (sv_event->type == SVET_DESTROY) {
186 bln_word_window = nullptr;
187 } else if (sv_event->type == SVET_CLICK) {
188 show_point(current_page_res, sv_event->x, sv_event->y);
189 }
190 }
191 };
192
193 /**
194 * bln_word_window_handle()
195 *
196 * @return a WINDOW for the word window, creating it if necessary
197 */
bln_word_window_handle()198 static ScrollView *bln_word_window_handle() { // return handle
199 // not opened yet
200 if (bln_word_window == nullptr) {
201 pgeditor_msg("Creating BLN word window...");
202 bln_word_window = new ScrollView(editor_word_name.c_str(), editor_word_xpos, editor_word_ypos,
203 editor_word_width, editor_word_height, 4000, 4000, true);
204 auto *a = new BlnEventHandler();
205 bln_word_window->AddEventHandler(a);
206 pgeditor_msg("Creating BLN word window...Done");
207 }
208 return bln_word_window;
209 }
210
211 /**
212 * build_image_window()
213 *
214 * Destroy the existing image window if there is one. Work out how big the
215 * new window needs to be. Create it and re-display.
216 */
217
build_image_window(int width,int height)218 static void build_image_window(int width, int height) {
219 delete image_win;
220 image_win = new ScrollView(editor_image_win_name.c_str(), editor_image_xpos, editor_image_ypos,
221 width + 1, height + editor_image_menuheight + 1, width, height, true);
222 }
223
224 /**
225 * display_bln_lines()
226 *
227 * Display normalized baseline, x-height, ascender limit and descender limit
228 */
229
display_bln_lines(ScrollView * window,ScrollView::Color colour,float scale_factor,float y_offset,float minx,float maxx)230 static void display_bln_lines(ScrollView *window, ScrollView::Color colour, float scale_factor,
231 float y_offset, float minx, float maxx) {
232 window->Pen(colour);
233 window->Line(minx, y_offset + scale_factor * DESC_HEIGHT, maxx,
234 y_offset + scale_factor * DESC_HEIGHT);
235 window->Line(minx, y_offset + scale_factor * BL_HEIGHT, maxx,
236 y_offset + scale_factor * BL_HEIGHT);
237 window->Line(minx, y_offset + scale_factor * X_HEIGHT, maxx, y_offset + scale_factor * X_HEIGHT);
238 window->Line(minx, y_offset + scale_factor * ASC_HEIGHT, maxx,
239 y_offset + scale_factor * ASC_HEIGHT);
240 }
241
242 /**
243 * notify()
244 *
245 * Event handler that processes incoming events, either forwarding
246 * them to process_cmd_win_event or process_image_event.
247 *
248 */
249
Notify(const SVEvent * event)250 void PGEventHandler::Notify(const SVEvent *event) {
251 char myval = '0';
252 if (event->type == SVET_POPUP) {
253 pe->Notify(event);
254 } // These are handled by ParamsEditor
255 else if (event->type == SVET_EXIT) {
256 stillRunning = false;
257 } else if (event->type == SVET_MENU) {
258 if (strcmp(event->parameter, "true") == 0) {
259 myval = 'T';
260 } else if (strcmp(event->parameter, "false") == 0) {
261 myval = 'F';
262 }
263 tess_->process_cmd_win_event(event->command_id, &myval);
264 } else {
265 tess_->process_image_event(*event);
266 }
267 }
268
269 /**
270 * build_menu()
271 *
272 * Construct the menu tree used by the command window
273 */
build_menu_new()274 SVMenuNode *Tesseract::build_menu_new() {
275 SVMenuNode *parent_menu;
276 auto *root_menu_item = new SVMenuNode();
277
278 SVMenuNode *modes_menu_item = root_menu_item->AddChild("MODES");
279
280 modes_menu_item->AddChild("Change Display", CHANGE_DISP_CMD_EVENT);
281 modes_menu_item->AddChild("Dump Word", DUMP_WERD_CMD_EVENT);
282 modes_menu_item->AddChild("Show Point", SHOW_POINT_CMD_EVENT);
283 modes_menu_item->AddChild("Show BL Norm Word", SHOW_BLN_WERD_CMD_EVENT);
284 modes_menu_item->AddChild("Config Words", DEBUG_WERD_CMD_EVENT);
285 modes_menu_item->AddChild("Recog Words", RECOG_WERDS);
286 modes_menu_item->AddChild("Recog Blobs", RECOG_PSEUDO);
287 modes_menu_item->AddChild("Show Blob Features", SHOW_BLOB_FEATURES);
288
289 parent_menu = root_menu_item->AddChild("DISPLAY");
290
291 parent_menu->AddChild("Blamer", BLAMER_CMD_EVENT, false);
292 parent_menu->AddChild("Bounding Boxes", BOUNDING_BOX_CMD_EVENT, false);
293 parent_menu->AddChild("Correct Text", CORRECT_TEXT_CMD_EVENT, false);
294 parent_menu->AddChild("Polygonal Approx", POLYGONAL_CMD_EVENT, false);
295 parent_menu->AddChild("Baseline Normalized", BL_NORM_CMD_EVENT, false);
296 parent_menu->AddChild("Edge Steps", BITMAP_CMD_EVENT, true);
297 parent_menu->AddChild("Subscripts", SHOW_SUBSCRIPT_CMD_EVENT);
298 parent_menu->AddChild("Superscripts", SHOW_SUPERSCRIPT_CMD_EVENT);
299 parent_menu->AddChild("Italics", SHOW_ITALIC_CMD_EVENT);
300 parent_menu->AddChild("Bold", SHOW_BOLD_CMD_EVENT);
301 parent_menu->AddChild("Underline", SHOW_UNDERLINE_CMD_EVENT);
302 parent_menu->AddChild("FixedPitch", SHOW_FIXEDPITCH_CMD_EVENT);
303 parent_menu->AddChild("Serifs", SHOW_SERIF_CMD_EVENT);
304 parent_menu->AddChild("SmallCaps", SHOW_SMALLCAPS_CMD_EVENT);
305 parent_menu->AddChild("DropCaps", SHOW_DROPCAPS_CMD_EVENT);
306
307 parent_menu = root_menu_item->AddChild("OTHER");
308
309 parent_menu->AddChild("Quit", QUIT_CMD_EVENT);
310 parent_menu->AddChild("Show Image", IMAGE_CMD_EVENT, false);
311 parent_menu->AddChild("ShowBlock Outlines", BLOCKS_CMD_EVENT, false);
312 parent_menu->AddChild("Show Baselines", BASELINES_CMD_EVENT, false);
313 parent_menu->AddChild("Uniform Display", UNIFORM_DISP_CMD_EVENT);
314 parent_menu->AddChild("Refresh Display", REFRESH_CMD_EVENT);
315
316 return root_menu_item;
317 }
318
319 /**
320 * do_re_display()
321 *
322 * Redisplay page
323 */
do_re_display(bool (tesseract::Tesseract::* word_painter)(PAGE_RES_IT * pr_it))324 void Tesseract::do_re_display(bool (tesseract::Tesseract::*word_painter)(PAGE_RES_IT *pr_it)) {
325 int block_count = 1;
326
327 image_win->Clear();
328 if (display_image) {
329 image_win->Draw(pix_binary_, 0, 0);
330 }
331
332 image_win->Brush(ScrollView::NONE);
333 PAGE_RES_IT pr_it(current_page_res);
334 for (WERD_RES *word = pr_it.word(); word != nullptr; word = pr_it.forward()) {
335 (this->*word_painter)(&pr_it);
336 if (display_baselines && pr_it.row() != pr_it.prev_row()) {
337 pr_it.row()->row->plot_baseline(image_win, ScrollView::GREEN);
338 }
339 if (display_blocks && pr_it.block() != pr_it.prev_block()) {
340 pr_it.block()->block->pdblk.plot(image_win, block_count++, ScrollView::RED);
341 }
342 }
343 image_win->Update();
344 }
345
346 /**
347 * pgeditor_main()
348 *
349 * Top level editor operation:
350 * Setup a new window and an according event handler
351 *
352 */
353
pgeditor_main(int width,int height,PAGE_RES * page_res)354 void Tesseract::pgeditor_main(int width, int height, PAGE_RES *page_res) {
355 current_page_res = page_res;
356 if (current_page_res->block_res_list.empty()) {
357 return;
358 }
359
360 recog_done = false;
361 stillRunning = true;
362
363 build_image_window(width, height);
364 word_display_mode.set(DF_EDGE_STEP);
365 do_re_display(&tesseract::Tesseract::word_set_display);
366 # ifndef GRAPHICS_DISABLED
367 pe = new ParamsEditor(this, image_win);
368 # endif
369 PGEventHandler pgEventHandler(this);
370
371 image_win->AddEventHandler(&pgEventHandler);
372 image_win->AddMessageBox();
373
374 SVMenuNode *svMenuRoot = build_menu_new();
375
376 svMenuRoot->BuildMenu(image_win);
377 image_win->SetVisible(true);
378
379 image_win->AwaitEvent(SVET_DESTROY);
380 image_win->AddEventHandler(nullptr);
381 }
382
383 /**
384 * process_cmd_win_event()
385 *
386 * Process a command returned from the command window
387 * (Just call the appropriate command handler)
388 */
389
process_cmd_win_event(int32_t cmd_event,char * new_value)390 bool Tesseract::process_cmd_win_event( // UI command semantics
391 int32_t cmd_event, // which menu item?
392 char *new_value // any prompt data
393 ) {
394 char msg[160];
395 bool exit = false;
396
397 color_mode = CM_RAINBOW;
398
399 // Run recognition on the full page if needed.
400 switch (cmd_event) {
401 case BLAMER_CMD_EVENT:
402 case SHOW_SUBSCRIPT_CMD_EVENT:
403 case SHOW_SUPERSCRIPT_CMD_EVENT:
404 case SHOW_ITALIC_CMD_EVENT:
405 case SHOW_BOLD_CMD_EVENT:
406 case SHOW_UNDERLINE_CMD_EVENT:
407 case SHOW_FIXEDPITCH_CMD_EVENT:
408 case SHOW_SERIF_CMD_EVENT:
409 case SHOW_SMALLCAPS_CMD_EVENT:
410 case SHOW_DROPCAPS_CMD_EVENT:
411 if (!recog_done) {
412 recog_all_words(current_page_res, nullptr, nullptr, nullptr, 0);
413 recog_done = true;
414 }
415 break;
416 default:
417 break;
418 }
419
420 char *parameter;
421
422 switch (cmd_event) {
423 case NULL_CMD_EVENT:
424 break;
425
426 case CHANGE_DISP_CMD_EVENT:
427 case DUMP_WERD_CMD_EVENT:
428 case SHOW_POINT_CMD_EVENT:
429 case SHOW_BLN_WERD_CMD_EVENT:
430 case RECOG_WERDS:
431 case RECOG_PSEUDO:
432 case SHOW_BLOB_FEATURES:
433 mode = static_cast<CMD_EVENTS>(cmd_event);
434 break;
435 case DEBUG_WERD_CMD_EVENT:
436 mode = DEBUG_WERD_CMD_EVENT;
437 parameter = image_win->ShowInputDialog("Config File Name");
438 word_config_ = parameter;
439 delete[] parameter;
440 break;
441 case BOUNDING_BOX_CMD_EVENT:
442 if (new_value[0] == 'T') {
443 word_display_mode.set(DF_BOX);
444 } else {
445 word_display_mode.reset(DF_BOX);
446 }
447 mode = CHANGE_DISP_CMD_EVENT;
448 break;
449 case BLAMER_CMD_EVENT:
450 if (new_value[0] == 'T') {
451 word_display_mode.set(DF_BLAMER);
452 } else {
453 word_display_mode.reset(DF_BLAMER);
454 }
455 do_re_display(&tesseract::Tesseract::word_display);
456 mode = CHANGE_DISP_CMD_EVENT;
457 break;
458 case CORRECT_TEXT_CMD_EVENT:
459 if (new_value[0] == 'T') {
460 word_display_mode.set(DF_TEXT);
461 } else {
462 word_display_mode.reset(DF_TEXT);
463 }
464 mode = CHANGE_DISP_CMD_EVENT;
465 break;
466 case POLYGONAL_CMD_EVENT:
467 if (new_value[0] == 'T') {
468 word_display_mode.set(DF_POLYGONAL);
469 } else {
470 word_display_mode.reset(DF_POLYGONAL);
471 }
472 mode = CHANGE_DISP_CMD_EVENT;
473 break;
474 case BL_NORM_CMD_EVENT:
475 if (new_value[0] == 'T') {
476 word_display_mode.set(DF_BN_POLYGONAL);
477 } else {
478 word_display_mode.reset(DF_BN_POLYGONAL);
479 }
480 mode = CHANGE_DISP_CMD_EVENT;
481 break;
482 case BITMAP_CMD_EVENT:
483 if (new_value[0] == 'T') {
484 word_display_mode.set(DF_EDGE_STEP);
485 } else {
486 word_display_mode.reset(DF_EDGE_STEP);
487 }
488 mode = CHANGE_DISP_CMD_EVENT;
489 break;
490 case UNIFORM_DISP_CMD_EVENT:
491 do_re_display(&tesseract::Tesseract::word_set_display);
492 break;
493 case IMAGE_CMD_EVENT:
494 display_image = (new_value[0] == 'T');
495 do_re_display(&tesseract::Tesseract::word_display);
496 break;
497 case BLOCKS_CMD_EVENT:
498 display_blocks = (new_value[0] == 'T');
499 do_re_display(&tesseract::Tesseract::word_display);
500 break;
501 case BASELINES_CMD_EVENT:
502 display_baselines = (new_value[0] == 'T');
503 do_re_display(&tesseract::Tesseract::word_display);
504 break;
505 case SHOW_SUBSCRIPT_CMD_EVENT:
506 color_mode = CM_SUBSCRIPT;
507 do_re_display(&tesseract::Tesseract::word_display);
508 break;
509 case SHOW_SUPERSCRIPT_CMD_EVENT:
510 color_mode = CM_SUPERSCRIPT;
511 do_re_display(&tesseract::Tesseract::word_display);
512 break;
513 case SHOW_ITALIC_CMD_EVENT:
514 color_mode = CM_ITALIC;
515 do_re_display(&tesseract::Tesseract::word_display);
516 break;
517 case SHOW_BOLD_CMD_EVENT:
518 color_mode = CM_BOLD;
519 do_re_display(&tesseract::Tesseract::word_display);
520 break;
521 case SHOW_UNDERLINE_CMD_EVENT:
522 color_mode = CM_UNDERLINE;
523 do_re_display(&tesseract::Tesseract::word_display);
524 break;
525 case SHOW_FIXEDPITCH_CMD_EVENT:
526 color_mode = CM_FIXEDPITCH;
527 do_re_display(&tesseract::Tesseract::word_display);
528 break;
529 case SHOW_SERIF_CMD_EVENT:
530 color_mode = CM_SERIF;
531 do_re_display(&tesseract::Tesseract::word_display);
532 break;
533 case SHOW_SMALLCAPS_CMD_EVENT:
534 color_mode = CM_SMALLCAPS;
535 do_re_display(&tesseract::Tesseract::word_display);
536 break;
537 case SHOW_DROPCAPS_CMD_EVENT:
538 color_mode = CM_DROPCAPS;
539 do_re_display(&tesseract::Tesseract::word_display);
540 break;
541 case REFRESH_CMD_EVENT:
542 do_re_display(&tesseract::Tesseract::word_display);
543 break;
544 case QUIT_CMD_EVENT:
545 exit = true;
546 ScrollView::Exit();
547 break;
548
549 default:
550 snprintf(msg, sizeof(msg), "Unrecognised event %" PRId32 "(%s)", cmd_event, new_value);
551 image_win->AddMessage(msg);
552 break;
553 }
554 return exit;
555 }
556
557 /**
558 * process_image_event()
559 *
560 * User has done something in the image window - mouse down or up. Work out
561 * what it is and do something with it.
562 * If DOWN - just remember where it was.
563 * If UP - for each word in the selected area do the operation defined by
564 * the current mode.
565 */
process_image_event(const SVEvent & event)566 void Tesseract::process_image_event( // action in image win
567 const SVEvent &event) {
568 // The following variable should remain static, since it is used by
569 // debug editor, which uses a single Tesseract instance.
570 static ICOORD down;
571 ICOORD up;
572 TBOX selection_box;
573 char msg[80];
574
575 switch (event.type) {
576 case SVET_SELECTION:
577 if (event.type == SVET_SELECTION) {
578 down.set_x(event.x + event.x_size);
579 down.set_y(event.y + event.y_size);
580 if (mode == SHOW_POINT_CMD_EVENT) {
581 show_point(current_page_res, event.x, event.y);
582 }
583 }
584
585 up.set_x(event.x);
586 up.set_y(event.y);
587
588 selection_box = TBOX(down, up);
589
590 switch (mode) {
591 case CHANGE_DISP_CMD_EVENT:
592 process_selected_words(current_page_res, selection_box,
593 &tesseract::Tesseract::word_blank_and_set_display);
594 break;
595 case DUMP_WERD_CMD_EVENT:
596 process_selected_words(current_page_res, selection_box,
597 &tesseract::Tesseract::word_dumper);
598 break;
599 case SHOW_BLN_WERD_CMD_EVENT:
600 process_selected_words(current_page_res, selection_box,
601 &tesseract::Tesseract::word_bln_display);
602 break;
603 case DEBUG_WERD_CMD_EVENT:
604 debug_word(current_page_res, selection_box);
605 break;
606 case SHOW_POINT_CMD_EVENT:
607 break; // ignore up event
608
609 case RECOG_WERDS:
610 # ifndef DISABLED_LEGACY_ENGINE
611 image_win->AddMessage("Recogging selected words");
612 this->process_selected_words(current_page_res, selection_box,
613 &Tesseract::recog_interactive);
614 # endif // ndef DISABLED_LEGACY_ENGINE
615 break;
616 case RECOG_PSEUDO:
617 image_win->AddMessage("Recogging selected blobs");
618 recog_pseudo_word(current_page_res, selection_box);
619 break;
620 case SHOW_BLOB_FEATURES:
621 blob_feature_display(current_page_res, selection_box);
622 break;
623
624 default:
625 sprintf(msg, "Mode %d not yet implemented", mode);
626 image_win->AddMessage(msg);
627 break;
628 }
629 default:
630 break;
631 }
632 }
633
634 /**
635 * debug_word
636 *
637 * Process the whole image, but load word_config_ for the selected word(s).
638 */
debug_word(PAGE_RES * page_res,const TBOX & selection_box)639 void Tesseract::debug_word(PAGE_RES *page_res, const TBOX &selection_box) {
640 # ifndef DISABLED_LEGACY_ENGINE
641 ResetAdaptiveClassifier();
642 # endif
643 recog_all_words(page_res, nullptr, &selection_box, word_config_.c_str(), 0);
644 }
645
646 /**********************************************************************
647 * WERD PROCESSOR FUNCTIONS
648 * ========================
649 *
650 * These routines are invoked by one or more of:
651 * process_all_words()
652 * process_selected_words()
653 * or
654 * process_all_words_it()
655 * process_selected_words_it()
656 * for each word to be processed
657 **********************************************************************/
658
659 /**
660 * word_blank_and_set_display() Word processor
661 *
662 * Blank display of word then redisplay word according to current display mode
663 * settings
664 */
665
word_blank_and_set_display(PAGE_RES_IT * pr_it)666 bool Tesseract::word_blank_and_set_display(PAGE_RES_IT *pr_it) {
667 pr_it->word()->word->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK);
668 return word_set_display(pr_it);
669 }
670
671 /**
672 * word_bln_display()
673 *
674 * Normalize word and display in word window
675 */
word_bln_display(PAGE_RES_IT * pr_it)676 bool Tesseract::word_bln_display(PAGE_RES_IT *pr_it) {
677 WERD_RES *word_res = pr_it->word();
678 if (word_res->chopped_word == nullptr) {
679 // Setup word normalization parameters.
680 word_res->SetupForRecognition(unicharset, this, BestPix(), tessedit_ocr_engine_mode, nullptr,
681 classify_bln_numeric_mode, textord_use_cjk_fp_model,
682 poly_allow_detailed_fx, pr_it->row()->row, pr_it->block()->block);
683 }
684 bln_word_window_handle()->Clear();
685 display_bln_lines(bln_word_window_handle(), ScrollView::CYAN, 1.0, 0.0f, -1000.0f, 1000.0f);
686 C_BLOB_IT it(word_res->word->cblob_list());
687 ScrollView::Color color = WERD::NextColor(ScrollView::BLACK);
688 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
689 it.data()->plot_normed(word_res->denorm, color, ScrollView::BROWN, bln_word_window_handle());
690 color = WERD::NextColor(color);
691 }
692 bln_word_window_handle()->Update();
693 return true;
694 }
695
696 /**
697 * word_display() Word Processor
698 *
699 * Display a word according to its display modes
700 */
word_display(PAGE_RES_IT * pr_it)701 bool Tesseract::word_display(PAGE_RES_IT *pr_it) {
702 WERD_RES *word_res = pr_it->word();
703 WERD *word = word_res->word;
704 TBOX word_bb; // word bounding box
705 int word_height; // ht of word BB
706 bool displayed_something = false;
707 float shift; // from bot left
708
709 if (color_mode != CM_RAINBOW && word_res->box_word != nullptr) {
710 # ifndef DISABLED_LEGACY_ENGINE
711 BoxWord *box_word = word_res->box_word;
712 WERD_CHOICE *best_choice = word_res->best_choice;
713 int length = box_word->length();
714 if (word_res->fontinfo == nullptr) {
715 return false;
716 }
717 const FontInfo &font_info = *word_res->fontinfo;
718 for (int i = 0; i < length; ++i) {
719 ScrollView::Color color = ScrollView::GREEN;
720 switch (color_mode) {
721 case CM_SUBSCRIPT:
722 if (best_choice->BlobPosition(i) == SP_SUBSCRIPT) {
723 color = ScrollView::RED;
724 }
725 break;
726 case CM_SUPERSCRIPT:
727 if (best_choice->BlobPosition(i) == SP_SUPERSCRIPT) {
728 color = ScrollView::RED;
729 }
730 break;
731 case CM_ITALIC:
732 if (font_info.is_italic()) {
733 color = ScrollView::RED;
734 }
735 break;
736 case CM_BOLD:
737 if (font_info.is_bold()) {
738 color = ScrollView::RED;
739 }
740 break;
741 case CM_FIXEDPITCH:
742 if (font_info.is_fixed_pitch()) {
743 color = ScrollView::RED;
744 }
745 break;
746 case CM_SERIF:
747 if (font_info.is_serif()) {
748 color = ScrollView::RED;
749 }
750 break;
751 case CM_SMALLCAPS:
752 if (word_res->small_caps) {
753 color = ScrollView::RED;
754 }
755 break;
756 case CM_DROPCAPS:
757 if (best_choice->BlobPosition(i) == SP_DROPCAP) {
758 color = ScrollView::RED;
759 }
760 break;
761 // TODO(rays) underline is currently completely unsupported.
762 case CM_UNDERLINE:
763 default:
764 break;
765 }
766 image_win->Pen(color);
767 TBOX box = box_word->BlobBox(i);
768 image_win->Rectangle(box.left(), box.bottom(), box.right(), box.top());
769 }
770 return true;
771 # else
772 return false;
773 # endif // ndef DISABLED_LEGACY_ENGINE
774 }
775 /*
776 Note the double coercions of(COLOUR)((int32_t)editor_image_word_bb_color)
777 etc. are to keep the compiler happy.
778 */
779 // display bounding box
780 if (word->display_flag(DF_BOX)) {
781 word->bounding_box().plot(image_win,
782 static_cast<ScrollView::Color>((int32_t)editor_image_word_bb_color),
783 static_cast<ScrollView::Color>((int32_t)editor_image_word_bb_color));
784
785 auto c = static_cast<ScrollView::Color>((int32_t)editor_image_blob_bb_color);
786 image_win->Pen(c);
787 // cblob iterator
788 C_BLOB_IT c_it(word->cblob_list());
789 for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) {
790 c_it.data()->bounding_box().plot(image_win);
791 }
792 displayed_something = true;
793 }
794
795 // display edge steps
796 if (word->display_flag(DF_EDGE_STEP)) { // edgesteps available
797 word->plot(image_win); // rainbow colors
798 displayed_something = true;
799 }
800
801 // display poly approx
802 if (word->display_flag(DF_POLYGONAL)) {
803 // need to convert
804 TWERD *tword = TWERD::PolygonalCopy(poly_allow_detailed_fx, word);
805 tword->plot(image_win);
806 delete tword;
807 displayed_something = true;
808 }
809
810 // Display correct text and blamer information.
811 std::string text;
812 std::string blame;
813 if (word->display_flag(DF_TEXT) && word->text() != nullptr) {
814 text = word->text();
815 }
816 if (word->display_flag(DF_BLAMER) &&
817 !(word_res->blamer_bundle != nullptr &&
818 word_res->blamer_bundle->incorrect_result_reason() == IRR_CORRECT)) {
819 text = "";
820 const BlamerBundle *blamer_bundle = word_res->blamer_bundle;
821 if (blamer_bundle == nullptr) {
822 text += "NULL";
823 } else {
824 text = blamer_bundle->TruthString();
825 }
826 text += " -> ";
827 std::string best_choice_str;
828 if (word_res->best_choice == nullptr) {
829 best_choice_str = "NULL";
830 } else {
831 word_res->best_choice->string_and_lengths(&best_choice_str, nullptr);
832 }
833 text += best_choice_str;
834 IncorrectResultReason reason =
835 (blamer_bundle == nullptr) ? IRR_PAGE_LAYOUT : blamer_bundle->incorrect_result_reason();
836 ASSERT_HOST(reason < IRR_NUM_REASONS);
837 blame += " [";
838 blame += BlamerBundle::IncorrectReasonName(reason);
839 blame += "]";
840 }
841 if (text.length() > 0) {
842 word_bb = word->bounding_box();
843 image_win->Pen(ScrollView::RED);
844 word_height = word_bb.height();
845 int text_height = 0.50 * word_height;
846 if (text_height > 20) {
847 text_height = 20;
848 }
849 image_win->TextAttributes("Arial", text_height, false, false, false);
850 shift = (word_height < word_bb.width()) ? 0.25 * word_height : 0.0f;
851 image_win->Text(word_bb.left() + shift, word_bb.bottom() + 0.25 * word_height, text.c_str());
852 if (blame.length() > 0) {
853 image_win->Text(word_bb.left() + shift, word_bb.bottom() + 0.25 * word_height - text_height,
854 blame.c_str());
855 }
856
857 displayed_something = true;
858 }
859
860 if (!displayed_something) { // display BBox anyway
861 word->bounding_box().plot(image_win,
862 static_cast<ScrollView::Color>((int32_t)editor_image_word_bb_color),
863 static_cast<ScrollView::Color>((int32_t)editor_image_word_bb_color));
864 }
865 return true;
866 }
867 } // namespace tesseract
868 #endif // !GRAPHICS_DISABLED
869
870 namespace tesseract {
871 /**
872 * word_dumper()
873 *
874 * Dump members to the debug window
875 */
word_dumper(PAGE_RES_IT * pr_it)876 bool Tesseract::word_dumper(PAGE_RES_IT *pr_it) {
877 if (pr_it->block()->block != nullptr) {
878 tprintf("\nBlock data...\n");
879 pr_it->block()->block->print(nullptr, false);
880 }
881 tprintf("\nRow data...\n");
882 pr_it->row()->row->print(nullptr);
883 tprintf("\nWord data...\n");
884 WERD_RES *word_res = pr_it->word();
885 word_res->word->print();
886 if (word_res->blamer_bundle != nullptr && wordrec_debug_blamer &&
887 word_res->blamer_bundle->incorrect_result_reason() != IRR_CORRECT) {
888 tprintf("Current blamer debug: %s\n", word_res->blamer_bundle->debug().c_str());
889 }
890 return true;
891 }
892
893 #ifndef GRAPHICS_DISABLED
894 /**
895 * word_set_display() Word processor
896 *
897 * Display word according to current display mode settings
898 */
word_set_display(PAGE_RES_IT * pr_it)899 bool Tesseract::word_set_display(PAGE_RES_IT *pr_it) {
900 WERD *word = pr_it->word()->word;
901 word->set_display_flag(DF_BOX, word_display_mode[DF_BOX]);
902 word->set_display_flag(DF_TEXT, word_display_mode[DF_TEXT]);
903 word->set_display_flag(DF_POLYGONAL, word_display_mode[DF_POLYGONAL]);
904 word->set_display_flag(DF_EDGE_STEP, word_display_mode[DF_EDGE_STEP]);
905 word->set_display_flag(DF_BN_POLYGONAL, word_display_mode[DF_BN_POLYGONAL]);
906 word->set_display_flag(DF_BLAMER, word_display_mode[DF_BLAMER]);
907 return word_display(pr_it);
908 }
909
910 // page_res is non-const because the iterator doesn't know if you are going
911 // to change the items it points to! Really a const here though.
blob_feature_display(PAGE_RES * page_res,const TBOX & selection_box)912 void Tesseract::blob_feature_display(PAGE_RES *page_res, const TBOX &selection_box) {
913 # ifndef DISABLED_LEGACY_ENGINE
914 PAGE_RES_IT *it = make_pseudo_word(page_res, selection_box);
915 if (it != nullptr) {
916 WERD_RES *word_res = it->word();
917 word_res->x_height = it->row()->row->x_height();
918 word_res->SetupForRecognition(unicharset, this, BestPix(), tessedit_ocr_engine_mode, nullptr,
919 classify_bln_numeric_mode, textord_use_cjk_fp_model,
920 poly_allow_detailed_fx, it->row()->row, it->block()->block);
921 TWERD *bln_word = word_res->chopped_word;
922 TBLOB *bln_blob = bln_word->blobs[0];
923 INT_FX_RESULT_STRUCT fx_info;
924 std::vector<INT_FEATURE_STRUCT> bl_features;
925 std::vector<INT_FEATURE_STRUCT> cn_features;
926 Classify::ExtractFeatures(*bln_blob, classify_nonlinear_norm, &bl_features, &cn_features,
927 &fx_info, nullptr);
928 // Display baseline features.
929 ScrollView *bl_win = CreateFeatureSpaceWindow("BL Features", 512, 0);
930 ClearFeatureSpaceWindow(baseline, bl_win);
931 for (auto &bl_feature : bl_features) {
932 RenderIntFeature(bl_win, &bl_feature, ScrollView::GREEN);
933 }
934 bl_win->Update();
935 // Display cn features.
936 ScrollView *cn_win = CreateFeatureSpaceWindow("CN Features", 512, 0);
937 ClearFeatureSpaceWindow(character, cn_win);
938 for (auto &cn_feature : cn_features) {
939 RenderIntFeature(cn_win, &cn_feature, ScrollView::GREEN);
940 }
941 cn_win->Update();
942
943 it->DeleteCurrentWord();
944 delete it;
945 }
946 # endif // ndef DISABLED_LEGACY_ENGINE
947 }
948
949 #endif // !GRAPHICS_DISABLED
950
951 } // namespace tesseract
952