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