diff options
Diffstat (limited to 'tesseract/src/ccmain/pgedit.cpp')
-rw-r--r-- | tesseract/src/ccmain/pgedit.cpp | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/tesseract/src/ccmain/pgedit.cpp b/tesseract/src/ccmain/pgedit.cpp new file mode 100644 index 00000000..b00b5f64 --- /dev/null +++ b/tesseract/src/ccmain/pgedit.cpp @@ -0,0 +1,981 @@ +/********************************************************************** + * File: pgedit.cpp (Formerly pgeditor.c) + * Description: Page structure file editor + * Author: Phil Cheatle + * + *(C) Copyright 1991, Hewlett-Packard Ltd. + ** Licensed under the Apache License, Version 2.0(the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http:// www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +#include "config_auto.h" +#endif + +#include "pgedit.h" + +#include "blread.h" +#include "control.h" +#include "paramsd.h" +#include "pageres.h" +#include "tordmain.h" +#include "scrollview.h" +#include "svmnode.h" +#include "statistc.h" +#include "tesseractclass.h" +#include "werdit.h" + +#include <cctype> +#include <cmath> + +#ifndef GRAPHICS_DISABLED +namespace tesseract { +#define ASC_HEIGHT (2 * kBlnBaselineOffset + kBlnXHeight) +#define X_HEIGHT (kBlnBaselineOffset + kBlnXHeight) +#define BL_HEIGHT kBlnBaselineOffset +#define DESC_HEIGHT 0 + +enum CMD_EVENTS +{ + NULL_CMD_EVENT, + CHANGE_DISP_CMD_EVENT, + DUMP_WERD_CMD_EVENT, + SHOW_POINT_CMD_EVENT, + SHOW_BLN_WERD_CMD_EVENT, + DEBUG_WERD_CMD_EVENT, + BLAMER_CMD_EVENT, + BOUNDING_BOX_CMD_EVENT, + CORRECT_TEXT_CMD_EVENT, + POLYGONAL_CMD_EVENT, + BL_NORM_CMD_EVENT, + BITMAP_CMD_EVENT, + IMAGE_CMD_EVENT, + BLOCKS_CMD_EVENT, + BASELINES_CMD_EVENT, + UNIFORM_DISP_CMD_EVENT, + REFRESH_CMD_EVENT, + QUIT_CMD_EVENT, + RECOG_WERDS, + RECOG_PSEUDO, + SHOW_BLOB_FEATURES, + SHOW_SUBSCRIPT_CMD_EVENT, + SHOW_SUPERSCRIPT_CMD_EVENT, + SHOW_ITALIC_CMD_EVENT, + SHOW_BOLD_CMD_EVENT, + SHOW_UNDERLINE_CMD_EVENT, + SHOW_FIXEDPITCH_CMD_EVENT, + SHOW_SERIF_CMD_EVENT, + SHOW_SMALLCAPS_CMD_EVENT, + SHOW_DROPCAPS_CMD_EVENT, +}; + +enum ColorationMode { + CM_RAINBOW, + CM_SUBSCRIPT, + CM_SUPERSCRIPT, + CM_ITALIC, + CM_BOLD, + CM_UNDERLINE, + CM_FIXEDPITCH, + CM_SERIF, + CM_SMALLCAPS, + CM_DROPCAPS +}; + +/* + * + * Some global data + * + */ + +static ScrollView* image_win; +static ParamsEditor* pe; +static bool stillRunning = false; + +static ScrollView* bln_word_window = nullptr; // baseline norm words + +static CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT; // selected words op + +static bool recog_done = false; // recog_all_words was called + +// These variables should remain global, since they are only used for the +// debug mode (in which only a single Tesseract thread/instance will exist). +static BITS16 word_display_mode; +static ColorationMode color_mode = CM_RAINBOW; +static bool display_image = false; +static bool display_blocks = false; +static bool display_baselines = false; + +static PAGE_RES *current_page_res = nullptr; + +STRING_VAR(editor_image_win_name, "EditorImage", + "Editor image window name"); +INT_VAR(editor_image_xpos, 590, "Editor image X Pos"); +INT_VAR(editor_image_ypos, 10, "Editor image Y Pos"); +static INT_VAR(editor_image_menuheight, 50, "Add to image height for menu bar"); +INT_VAR(editor_image_word_bb_color, ScrollView::BLUE, + "Word bounding box colour"); +INT_VAR(editor_image_blob_bb_color, ScrollView::YELLOW, + "Blob bounding box colour"); +INT_VAR(editor_image_text_color, ScrollView::WHITE, + "Correct text colour"); + +STRING_VAR(editor_dbwin_name, "EditorDBWin", + "Editor debug window name"); +INT_VAR(editor_dbwin_xpos, 50, "Editor debug window X Pos"); +INT_VAR(editor_dbwin_ypos, 500, "Editor debug window Y Pos"); +INT_VAR(editor_dbwin_height, 24, "Editor debug window height"); +INT_VAR(editor_dbwin_width, 80, "Editor debug window width"); + +STRING_VAR(editor_word_name, "BlnWords", "BL normalized word window"); +INT_VAR(editor_word_xpos, 60, "Word window X Pos"); +INT_VAR(editor_word_ypos, 510, "Word window Y Pos"); +INT_VAR(editor_word_height, 240, "Word window height"); +INT_VAR(editor_word_width, 655, "Word window width"); + +/** + * show_point() + * + * Show coords of point, blob bounding box, word bounding box and offset from + * row baseline + */ + +static void show_point(PAGE_RES* page_res, float x, float y) { + FCOORD pt(x, y); + PAGE_RES_IT pr_it(page_res); + + const int kBufsize = 512; + char msg[kBufsize]; + char *msg_ptr = msg; + + msg_ptr += sprintf(msg_ptr, "Pt:(%0.3f, %0.3f) ", x, y); + + for (WERD_RES* word = pr_it.word(); word != nullptr; word = pr_it.forward()) { + if (pr_it.row() != pr_it.prev_row() && + pr_it.row()->row->bounding_box().contains(pt)) { + msg_ptr += sprintf(msg_ptr, "BL(x)=%0.3f ", + pr_it.row()->row->base_line(x)); + } + if (word->word->bounding_box().contains(pt)) { + TBOX box = word->word->bounding_box(); + msg_ptr += sprintf(msg_ptr, "Wd(%d, %d)/(%d, %d) ", + box.left(), box.bottom(), + box.right(), box.top()); + C_BLOB_IT cblob_it(word->word->cblob_list()); + for (cblob_it.mark_cycle_pt(); + !cblob_it.cycled_list(); + cblob_it.forward()) { + C_BLOB* cblob = cblob_it.data(); + box = cblob->bounding_box(); + if (box.contains(pt)) { + msg_ptr += sprintf(msg_ptr, + "CBlb(%d, %d)/(%d, %d) ", + box.left(), box.bottom(), + box.right(), box.top()); + } + } + } + } + image_win->AddMessage(msg); +} + +/** + * pgeditor_msg() + * + * Display a message - in the command window if there is one, or to stdout + */ + +static void pgeditor_msg( // message display + const char *msg) { + image_win->AddMessage(msg); +} + +class BlnEventHandler : public SVEventHandler { + public: + void Notify(const SVEvent* sv_event) override { + if (sv_event->type == SVET_DESTROY) + bln_word_window = nullptr; + else if (sv_event->type == SVET_CLICK) + show_point(current_page_res, sv_event->x, sv_event->y); + } +}; + +/** + * bln_word_window_handle() + * + * @return a WINDOW for the word window, creating it if necessary + */ +static ScrollView* bln_word_window_handle() { // return handle + // not opened yet + if (bln_word_window == nullptr) { + pgeditor_msg("Creating BLN word window..."); + bln_word_window = new ScrollView(editor_word_name.c_str(), + editor_word_xpos, editor_word_ypos, editor_word_width, + editor_word_height, 4000, 4000, true); + auto* a = new BlnEventHandler(); + bln_word_window->AddEventHandler(a); + pgeditor_msg("Creating BLN word window...Done"); + } + return bln_word_window; +} + +/** + * build_image_window() + * + * Destroy the existing image window if there is one. Work out how big the + * new window needs to be. Create it and re-display. + */ + +static void build_image_window(int width, int height) { + delete image_win; + image_win = new ScrollView(editor_image_win_name.c_str(), + editor_image_xpos, editor_image_ypos, + width + 1, + height + editor_image_menuheight + 1, + width, + height, + true); +} + +/** + * display_bln_lines() + * + * Display normalized baseline, x-height, ascender limit and descender limit + */ + +static void display_bln_lines(ScrollView* window, ScrollView::Color colour, + float scale_factor, float y_offset, + float minx, float maxx) { + window->Pen(colour); + window->Line(minx, y_offset + scale_factor * DESC_HEIGHT, + maxx, y_offset + scale_factor * DESC_HEIGHT); + window->Line(minx, y_offset + scale_factor * BL_HEIGHT, + maxx, y_offset + scale_factor * BL_HEIGHT); + window->Line(minx, y_offset + scale_factor * X_HEIGHT, + maxx, y_offset + scale_factor * X_HEIGHT); + window->Line(minx, y_offset + scale_factor * ASC_HEIGHT, + maxx, y_offset + scale_factor * ASC_HEIGHT); +} + +/** + * notify() + * + * Event handler that processes incoming events, either forwarding + * them to process_cmd_win_event or process_image_event. + * + */ + +void PGEventHandler::Notify(const SVEvent* event) { + char myval = '0'; + if (event->type == SVET_POPUP) { + pe->Notify(event); + } // These are handled by ParamsEditor + else if (event->type == SVET_EXIT) { stillRunning = false; } + else if (event->type == SVET_MENU) { + if (strcmp(event->parameter, "true") == 0) { myval = 'T'; } + else if (strcmp(event->parameter, "false") == 0) { myval = 'F'; } + tess_->process_cmd_win_event(event->command_id, &myval); + } + else { + tess_->process_image_event(*event); + } +} + +/** + * build_menu() + * + * Construct the menu tree used by the command window + */ +SVMenuNode *Tesseract::build_menu_new() { + SVMenuNode* parent_menu; + auto* root_menu_item = new SVMenuNode(); + + SVMenuNode* modes_menu_item = root_menu_item->AddChild("MODES"); + + modes_menu_item->AddChild("Change Display", CHANGE_DISP_CMD_EVENT); + modes_menu_item->AddChild("Dump Word", DUMP_WERD_CMD_EVENT); + modes_menu_item->AddChild("Show Point", SHOW_POINT_CMD_EVENT); + modes_menu_item->AddChild("Show BL Norm Word", SHOW_BLN_WERD_CMD_EVENT); + modes_menu_item->AddChild("Config Words", DEBUG_WERD_CMD_EVENT); + modes_menu_item->AddChild("Recog Words", RECOG_WERDS); + modes_menu_item->AddChild("Recog Blobs", RECOG_PSEUDO); + modes_menu_item->AddChild("Show Blob Features", SHOW_BLOB_FEATURES); + + parent_menu = root_menu_item->AddChild("DISPLAY"); + + parent_menu->AddChild("Blamer", BLAMER_CMD_EVENT, false); + parent_menu->AddChild("Bounding Boxes", BOUNDING_BOX_CMD_EVENT, false); + parent_menu->AddChild("Correct Text", CORRECT_TEXT_CMD_EVENT, false); + parent_menu->AddChild("Polygonal Approx", POLYGONAL_CMD_EVENT, false); + parent_menu->AddChild("Baseline Normalized", BL_NORM_CMD_EVENT, false); + parent_menu->AddChild("Edge Steps", BITMAP_CMD_EVENT, true); + parent_menu->AddChild("Subscripts", SHOW_SUBSCRIPT_CMD_EVENT); + parent_menu->AddChild("Superscripts", SHOW_SUPERSCRIPT_CMD_EVENT); + parent_menu->AddChild("Italics", SHOW_ITALIC_CMD_EVENT); + parent_menu->AddChild("Bold", SHOW_BOLD_CMD_EVENT); + parent_menu->AddChild("Underline", SHOW_UNDERLINE_CMD_EVENT); + parent_menu->AddChild("FixedPitch", SHOW_FIXEDPITCH_CMD_EVENT); + parent_menu->AddChild("Serifs", SHOW_SERIF_CMD_EVENT); + parent_menu->AddChild("SmallCaps", SHOW_SMALLCAPS_CMD_EVENT); + parent_menu->AddChild("DropCaps", SHOW_DROPCAPS_CMD_EVENT); + + + parent_menu = root_menu_item->AddChild("OTHER"); + + parent_menu->AddChild("Quit", QUIT_CMD_EVENT); + parent_menu->AddChild("Show Image", IMAGE_CMD_EVENT, false); + parent_menu->AddChild("ShowBlock Outlines", BLOCKS_CMD_EVENT, false); + parent_menu->AddChild("Show Baselines", BASELINES_CMD_EVENT, false); + parent_menu->AddChild("Uniform Display", UNIFORM_DISP_CMD_EVENT); + parent_menu->AddChild("Refresh Display", REFRESH_CMD_EVENT); + + return root_menu_item; +} + +/** + * do_re_display() + * + * Redisplay page + */ +void Tesseract::do_re_display( + bool (tesseract::Tesseract::* word_painter)(PAGE_RES_IT* pr_it)) { + int block_count = 1; + + image_win->Clear(); + if (display_image) { + image_win->Image(pix_binary_, 0, 0); + } + + image_win->Brush(ScrollView::NONE); + PAGE_RES_IT pr_it(current_page_res); + for (WERD_RES* word = pr_it.word(); word != nullptr; word = pr_it.forward()) { + (this->*word_painter)(&pr_it); + if (display_baselines && pr_it.row() != pr_it.prev_row()) + pr_it.row()->row->plot_baseline(image_win, ScrollView::GREEN); + if (display_blocks && pr_it.block() != pr_it.prev_block()) + pr_it.block()->block->pdblk.plot(image_win, block_count++, ScrollView::RED); + } + image_win->Update(); +} + +/** + * pgeditor_main() + * + * Top level editor operation: + * Setup a new window and an according event handler + * + */ + +void Tesseract::pgeditor_main(int width, int height, PAGE_RES *page_res) { + current_page_res = page_res; + if (current_page_res->block_res_list.empty()) + return; + + recog_done = false; + stillRunning = true; + + build_image_window(width, height); + word_display_mode.set(DF_EDGE_STEP); + do_re_display(&tesseract::Tesseract::word_set_display); +#ifndef GRAPHICS_DISABLED + pe = new ParamsEditor(this, image_win); +#endif + PGEventHandler pgEventHandler(this); + + image_win->AddEventHandler(&pgEventHandler); + image_win->AddMessageBox(); + + SVMenuNode* svMenuRoot = build_menu_new(); + + svMenuRoot->BuildMenu(image_win); + image_win->SetVisible(true); + + image_win->AwaitEvent(SVET_DESTROY); + image_win->AddEventHandler(nullptr); +} + +/** + * process_cmd_win_event() + * + * Process a command returned from the command window + * (Just call the appropriate command handler) + */ + +bool Tesseract::process_cmd_win_event( // UI command semantics + int32_t cmd_event, // which menu item? + char* new_value // any prompt data +) { + char msg[160]; + bool exit = false; + + color_mode = CM_RAINBOW; + + // Run recognition on the full page if needed. + switch (cmd_event) { + case BLAMER_CMD_EVENT: + case SHOW_SUBSCRIPT_CMD_EVENT: + case SHOW_SUPERSCRIPT_CMD_EVENT: + case SHOW_ITALIC_CMD_EVENT: + case SHOW_BOLD_CMD_EVENT: + case SHOW_UNDERLINE_CMD_EVENT: + case SHOW_FIXEDPITCH_CMD_EVENT: + case SHOW_SERIF_CMD_EVENT: + case SHOW_SMALLCAPS_CMD_EVENT: + case SHOW_DROPCAPS_CMD_EVENT: + if (!recog_done) { + recog_all_words(current_page_res, nullptr, nullptr, nullptr, 0); + recog_done = true; + } + break; + default: + break; + } + + char* parameter; + + switch (cmd_event) { + case NULL_CMD_EVENT: + break; + + case CHANGE_DISP_CMD_EVENT: + case DUMP_WERD_CMD_EVENT: + case SHOW_POINT_CMD_EVENT: + case SHOW_BLN_WERD_CMD_EVENT: + case RECOG_WERDS: + case RECOG_PSEUDO: + case SHOW_BLOB_FEATURES: + mode =static_cast<CMD_EVENTS>(cmd_event); + break; + case DEBUG_WERD_CMD_EVENT: + mode = DEBUG_WERD_CMD_EVENT; + parameter = image_win->ShowInputDialog("Config File Name"); + word_config_ = parameter; + delete[] parameter; + break; + case BOUNDING_BOX_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.set(DF_BOX); + else + word_display_mode.reset(DF_BOX); + mode = CHANGE_DISP_CMD_EVENT; + break; + case BLAMER_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.set(DF_BLAMER); + else + word_display_mode.reset(DF_BLAMER); + do_re_display(&tesseract::Tesseract::word_display); + mode = CHANGE_DISP_CMD_EVENT; + break; + case CORRECT_TEXT_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.set(DF_TEXT); + else + word_display_mode.reset(DF_TEXT); + mode = CHANGE_DISP_CMD_EVENT; + break; + case POLYGONAL_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.set(DF_POLYGONAL); + else + word_display_mode.reset(DF_POLYGONAL); + mode = CHANGE_DISP_CMD_EVENT; + break; + case BL_NORM_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.set(DF_BN_POLYGONAL); + else + word_display_mode.reset(DF_BN_POLYGONAL); + mode = CHANGE_DISP_CMD_EVENT; + break; + case BITMAP_CMD_EVENT: + if (new_value[0] == 'T') + word_display_mode.set(DF_EDGE_STEP); + else + word_display_mode.reset(DF_EDGE_STEP); + mode = CHANGE_DISP_CMD_EVENT; + break; + case UNIFORM_DISP_CMD_EVENT: + do_re_display(&tesseract::Tesseract::word_set_display); + break; + case IMAGE_CMD_EVENT: + display_image =(new_value[0] == 'T'); + do_re_display(&tesseract::Tesseract::word_display); + break; + case BLOCKS_CMD_EVENT: + display_blocks =(new_value[0] == 'T'); + do_re_display(&tesseract::Tesseract::word_display); + break; + case BASELINES_CMD_EVENT: + display_baselines =(new_value[0] == 'T'); + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_SUBSCRIPT_CMD_EVENT: + color_mode = CM_SUBSCRIPT; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_SUPERSCRIPT_CMD_EVENT: + color_mode = CM_SUPERSCRIPT; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_ITALIC_CMD_EVENT: + color_mode = CM_ITALIC; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_BOLD_CMD_EVENT: + color_mode = CM_BOLD; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_UNDERLINE_CMD_EVENT: + color_mode = CM_UNDERLINE; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_FIXEDPITCH_CMD_EVENT: + color_mode = CM_FIXEDPITCH; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_SERIF_CMD_EVENT: + color_mode = CM_SERIF; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_SMALLCAPS_CMD_EVENT: + color_mode = CM_SMALLCAPS; + do_re_display(&tesseract::Tesseract::word_display); + break; + case SHOW_DROPCAPS_CMD_EVENT: + color_mode = CM_DROPCAPS; + do_re_display(&tesseract::Tesseract::word_display); + break; + case REFRESH_CMD_EVENT: + do_re_display(&tesseract::Tesseract::word_display); + break; + case QUIT_CMD_EVENT: + exit = true; + ScrollView::Exit(); + break; + + default: + snprintf(msg, sizeof(msg), "Unrecognised event %" PRId32 "(%s)", + cmd_event, new_value); + image_win->AddMessage(msg); + break; + } + return exit; +} + + +/** + * process_image_event() + * + * User has done something in the image window - mouse down or up. Work out + * what it is and do something with it. + * If DOWN - just remember where it was. + * If UP - for each word in the selected area do the operation defined by + * the current mode. + */ +void Tesseract::process_image_event( // action in image win + const SVEvent &event) { + // The following variable should remain static, since it is used by + // debug editor, which uses a single Tesseract instance. + static ICOORD down; + ICOORD up; + TBOX selection_box; + char msg[80]; + + switch(event.type) { + + case SVET_SELECTION: + if (event.type == SVET_SELECTION) { + down.set_x(event.x + event.x_size); + down.set_y(event.y + event.y_size); + if (mode == SHOW_POINT_CMD_EVENT) + show_point(current_page_res, event.x, event.y); + } + + up.set_x(event.x); + up.set_y(event.y); + + selection_box = TBOX(down, up); + + switch(mode) { + case CHANGE_DISP_CMD_EVENT: + process_selected_words( + current_page_res, + selection_box, + &tesseract::Tesseract::word_blank_and_set_display); + break; + case DUMP_WERD_CMD_EVENT: + process_selected_words(current_page_res, + selection_box, + &tesseract::Tesseract::word_dumper); + break; + case SHOW_BLN_WERD_CMD_EVENT: + process_selected_words(current_page_res, + selection_box, + &tesseract::Tesseract::word_bln_display); + break; + case DEBUG_WERD_CMD_EVENT: + debug_word(current_page_res, selection_box); + break; + case SHOW_POINT_CMD_EVENT: + break; // ignore up event + + case RECOG_WERDS: + #ifndef DISABLED_LEGACY_ENGINE + image_win->AddMessage("Recogging selected words"); + this->process_selected_words(current_page_res, + selection_box, + &Tesseract::recog_interactive); + #endif // ndef DISABLED_LEGACY_ENGINE + break; + case RECOG_PSEUDO: + image_win->AddMessage("Recogging selected blobs"); + recog_pseudo_word(current_page_res, selection_box); + break; + case SHOW_BLOB_FEATURES: + blob_feature_display(current_page_res, selection_box); + break; + + default: + sprintf(msg, "Mode %d not yet implemented", mode); + image_win->AddMessage(msg); + break; + } + default: + break; + } +} + +/** + * debug_word + * + * Process the whole image, but load word_config_ for the selected word(s). + */ +void Tesseract::debug_word(PAGE_RES* page_res, const TBOX &selection_box) { +#ifndef DISABLED_LEGACY_ENGINE + ResetAdaptiveClassifier(); +#endif + recog_all_words(page_res, nullptr, &selection_box, word_config_.c_str(), 0); +} + + +/********************************************************************** + * WERD PROCESSOR FUNCTIONS + * ======================== + * + * These routines are invoked by one or more of: + * process_all_words() + * process_selected_words() + * or + * process_all_words_it() + * process_selected_words_it() + * for each word to be processed + **********************************************************************/ + +/** + * word_blank_and_set_display() Word processor + * + * Blank display of word then redisplay word according to current display mode + * settings + */ + +bool Tesseract::word_blank_and_set_display(PAGE_RES_IT* pr_it) { + pr_it->word()->word->bounding_box().plot(image_win, ScrollView::BLACK, + ScrollView::BLACK); + return word_set_display(pr_it); +} + + +/** + * word_bln_display() + * + * Normalize word and display in word window + */ +bool Tesseract::word_bln_display(PAGE_RES_IT* pr_it) { + WERD_RES* word_res = pr_it->word(); + if (word_res->chopped_word == nullptr) { + // Setup word normalization parameters. + word_res->SetupForRecognition(unicharset, this, BestPix(), + tessedit_ocr_engine_mode, nullptr, + classify_bln_numeric_mode, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, + pr_it->row()->row, pr_it->block()->block); + } + bln_word_window_handle()->Clear(); + display_bln_lines(bln_word_window_handle(), ScrollView::CYAN, + 1.0, 0.0f, -1000.0f, 1000.0f); + C_BLOB_IT it(word_res->word->cblob_list()); + ScrollView::Color color = WERD::NextColor(ScrollView::BLACK); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + it.data()->plot_normed(word_res->denorm, color, ScrollView::BROWN, + bln_word_window_handle()); + color = WERD::NextColor(color); + } + bln_word_window_handle()->Update(); + return true; +} + + + +/** + * word_display() Word Processor + * + * Display a word according to its display modes + */ +bool Tesseract::word_display(PAGE_RES_IT* pr_it) { + WERD_RES* word_res = pr_it->word(); + WERD* word = word_res->word; + TBOX word_bb; // word bounding box + int word_height; // ht of word BB + bool displayed_something = false; + float shift; // from bot left + + if (color_mode != CM_RAINBOW && word_res->box_word != nullptr) { + #ifndef DISABLED_LEGACY_ENGINE + BoxWord* box_word = word_res->box_word; + WERD_CHOICE* best_choice = word_res->best_choice; + int length = box_word->length(); + if (word_res->fontinfo == nullptr) return false; + const FontInfo& font_info = *word_res->fontinfo; + for (int i = 0; i < length; ++i) { + ScrollView::Color color = ScrollView::GREEN; + switch (color_mode) { + case CM_SUBSCRIPT: + if (best_choice->BlobPosition(i) == SP_SUBSCRIPT) + color = ScrollView::RED; + break; + case CM_SUPERSCRIPT: + if (best_choice->BlobPosition(i) == SP_SUPERSCRIPT) + color = ScrollView::RED; + break; + case CM_ITALIC: + if (font_info.is_italic()) + color = ScrollView::RED; + break; + case CM_BOLD: + if (font_info.is_bold()) + color = ScrollView::RED; + break; + case CM_FIXEDPITCH: + if (font_info.is_fixed_pitch()) + color = ScrollView::RED; + break; + case CM_SERIF: + if (font_info.is_serif()) + color = ScrollView::RED; + break; + case CM_SMALLCAPS: + if (word_res->small_caps) + color = ScrollView::RED; + break; + case CM_DROPCAPS: + if (best_choice->BlobPosition(i) == SP_DROPCAP) + color = ScrollView::RED; + break; + // TODO(rays) underline is currently completely unsupported. + case CM_UNDERLINE: + default: + break; + } + image_win->Pen(color); + TBOX box = box_word->BlobBox(i); + image_win->Rectangle(box.left(), box.bottom(), box.right(), box.top()); + } + return true; + #else + return false; + #endif // ndef DISABLED_LEGACY_ENGINE + } + /* + Note the double coercions of(COLOUR)((int32_t)editor_image_word_bb_color) + etc. are to keep the compiler happy. + */ + // display bounding box + if (word->display_flag(DF_BOX)) { + word->bounding_box().plot(image_win, + static_cast<ScrollView::Color>((int32_t) + editor_image_word_bb_color), + static_cast<ScrollView::Color>((int32_t) + editor_image_word_bb_color)); + + auto c = static_cast<ScrollView::Color>((int32_t) editor_image_blob_bb_color); + image_win->Pen(c); + // cblob iterator + C_BLOB_IT c_it(word->cblob_list()); + for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) + c_it.data()->bounding_box().plot(image_win); + displayed_something = true; + } + + // display edge steps + if (word->display_flag(DF_EDGE_STEP)) { // edgesteps available + word->plot(image_win); // rainbow colors + displayed_something = true; + } + + // display poly approx + if (word->display_flag(DF_POLYGONAL)) { + // need to convert + TWERD* tword = TWERD::PolygonalCopy(poly_allow_detailed_fx, word); + tword->plot(image_win); + delete tword; + displayed_something = true; + } + + // Display correct text and blamer information. + STRING text; + STRING blame; + if (word->display_flag(DF_TEXT) && word->text() != nullptr) { + text = word->text(); + } + if (word->display_flag(DF_BLAMER) && + !(word_res->blamer_bundle != nullptr && + word_res->blamer_bundle->incorrect_result_reason() == IRR_CORRECT)) { + text = ""; + const BlamerBundle *blamer_bundle = word_res->blamer_bundle; + if (blamer_bundle == nullptr) { + text += "NULL"; + } else { + text = blamer_bundle->TruthString(); + } + text += " -> "; + STRING best_choice_str; + if (word_res->best_choice == nullptr) { + best_choice_str = "NULL"; + } else { + word_res->best_choice->string_and_lengths(&best_choice_str, nullptr); + } + text += best_choice_str; + IncorrectResultReason reason = (blamer_bundle == nullptr) ? + IRR_PAGE_LAYOUT : blamer_bundle->incorrect_result_reason(); + ASSERT_HOST(reason < IRR_NUM_REASONS); + blame += " ["; + blame += BlamerBundle::IncorrectReasonName(reason); + blame += "]"; + } + if (text.length() > 0) { + word_bb = word->bounding_box(); + image_win->Pen(ScrollView::RED); + word_height = word_bb.height(); + int text_height = 0.50 * word_height; + if (text_height > 20) text_height = 20; + image_win->TextAttributes("Arial", text_height, false, false, false); + shift = (word_height < word_bb.width()) ? 0.25 * word_height : 0.0f; + image_win->Text(word_bb.left() + shift, + word_bb.bottom() + 0.25 * word_height, text.c_str()); + if (blame.length() > 0) { + image_win->Text(word_bb.left() + shift, + word_bb.bottom() + 0.25 * word_height - text_height, + blame.c_str()); + } + + displayed_something = true; + } + + if (!displayed_something) // display BBox anyway + word->bounding_box().plot(image_win, + static_cast<ScrollView::Color>((int32_t) editor_image_word_bb_color), + static_cast<ScrollView::Color>((int32_t) + editor_image_word_bb_color)); + return true; +} +} // namespace tesseract +#endif // !GRAPHICS_DISABLED + +namespace tesseract { +/** + * word_dumper() + * + * Dump members to the debug window + */ +bool Tesseract::word_dumper(PAGE_RES_IT* pr_it) { + if (pr_it->block()->block != nullptr) { + tprintf("\nBlock data...\n"); + pr_it->block()->block->print(nullptr, false); + } + tprintf("\nRow data...\n"); + pr_it->row()->row->print(nullptr); + tprintf("\nWord data...\n"); + WERD_RES* word_res = pr_it->word(); + word_res->word->print(); + if (word_res->blamer_bundle != nullptr && wordrec_debug_blamer && + word_res->blamer_bundle->incorrect_result_reason() != IRR_CORRECT) { + tprintf("Current blamer debug: %s\n", + word_res->blamer_bundle->debug().c_str()); + } + return true; +} + +#ifndef GRAPHICS_DISABLED +/** + * word_set_display() Word processor + * + * Display word according to current display mode settings + */ +bool Tesseract::word_set_display(PAGE_RES_IT* pr_it) { + WERD* word = pr_it->word()->word; + word->set_display_flag(DF_BOX, word_display_mode[DF_BOX]); + word->set_display_flag(DF_TEXT, word_display_mode[DF_TEXT]); + word->set_display_flag(DF_POLYGONAL, word_display_mode[DF_POLYGONAL]); + word->set_display_flag(DF_EDGE_STEP, word_display_mode[DF_EDGE_STEP]); + word->set_display_flag(DF_BN_POLYGONAL, + word_display_mode[DF_BN_POLYGONAL]); + word->set_display_flag(DF_BLAMER, word_display_mode[DF_BLAMER]); + return word_display(pr_it); +} + + +// page_res is non-const because the iterator doesn't know if you are going +// to change the items it points to! Really a const here though. +void Tesseract::blob_feature_display(PAGE_RES* page_res, + const TBOX& selection_box) { +#ifndef DISABLED_LEGACY_ENGINE + PAGE_RES_IT* it = make_pseudo_word(page_res, selection_box); + if (it != nullptr) { + WERD_RES* word_res = it->word(); + word_res->x_height = it->row()->row->x_height(); + word_res->SetupForRecognition(unicharset, this, BestPix(), + tessedit_ocr_engine_mode, nullptr, + classify_bln_numeric_mode, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, + it->row()->row, it->block()->block); + TWERD* bln_word = word_res->chopped_word; + TBLOB* bln_blob = bln_word->blobs[0]; + INT_FX_RESULT_STRUCT fx_info; + std::vector<INT_FEATURE_STRUCT> bl_features; + std::vector<INT_FEATURE_STRUCT> cn_features; + Classify::ExtractFeatures(*bln_blob, classify_nonlinear_norm, &bl_features, + &cn_features, &fx_info, nullptr); + // Display baseline features. + ScrollView* bl_win = CreateFeatureSpaceWindow("BL Features", 512, 0); + ClearFeatureSpaceWindow(baseline, bl_win); + for (int f = 0; f < bl_features.size(); ++f) + RenderIntFeature(bl_win, &bl_features[f], ScrollView::GREEN); + bl_win->Update(); + // Display cn features. + ScrollView* cn_win = CreateFeatureSpaceWindow("CN Features", 512, 0); + ClearFeatureSpaceWindow(character, cn_win); + for (int f = 0; f < cn_features.size(); ++f) + RenderIntFeature(cn_win, &cn_features[f], ScrollView::GREEN); + cn_win->Update(); + + it->DeleteCurrentWord(); + delete it; + } +#endif // ndef DISABLED_LEGACY_ENGINE +} + +#endif // !GRAPHICS_DISABLED + +} // namespace tesseract |