* 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.
*
**********************************************************************/
#ifdef HAVE_CONFIG_H
# include "config_auto.h"
#endif
#include "pgedit.h"
#include "blread.h"
#include "control.h"
#include "pageres.h"
#include "paramsd.h"
#include "scrollview.h"
#include "statistc.h"
#include "svmnode.h"
#include "tesseractclass.h"
#include "tordmain.h"
#include "werdit.h"
#include <cctype>
#include <cmath>
#include <iomanip>
#include <locale>
#include <sstream>
#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;
static CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT;
static bool recog_done = false;
static std::bitset<16> 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");
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);
std::stringstream msg;
msg.imbue(std::locale::classic());
msg << std::fixed << std::setprecision(3) << "Pt:(" << 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 << "BL(x)=" << pr_it.row()->row->base_line(x) << ' ';
}
if (word->word->bounding_box().contains(pt)) {
TBOX box = word->word->bounding_box();
msg << "Wd(" << 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 << "CBlb(" << box.left() << ", " << box.bottom() << ")/("
<< box.right() << ", " << box.top() << ") ";
}
}
}
}
image_win->AddMessage(msg.str().c_str());
}
* pgeditor_msg()
*
* Display a message - in the command window if there is one, or to stdout
*/
static void pgeditor_msg(
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() {
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);
}
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->Draw(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(
int32_t cmd_event,
char *new_value
) {
char msg[160];
bool exit = false;
color_mode = CM_RAINBOW;
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(
const SVEvent &event) {
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;
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
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:
snprintf(msg, sizeof(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) {
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;
bool displayed_something = false;
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;
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
}
Note the double coercions of(COLOUR)((int32_t)editor_image_word_bb_color)
etc. are to keep the compiler happy.
*/
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);
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;
}
if (word->display_flag(DF_EDGE_STEP)) {
word->plot(image_win);
displayed_something = true;
}
if (word->display_flag(DF_POLYGONAL)) {
TWERD *tword = TWERD::PolygonalCopy(poly_allow_detailed_fx, word);
tword->plot(image_win);
delete tword;
displayed_something = true;
}
std::string text;
std::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 += " -> ";
std::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);
auto word_height = word_bb.height();
int text_height = word_height / 2;
if (text_height > 20) {
text_height = 20;
}
image_win->TextAttributes("Arial", text_height, false, false, false);
float shift = (word_height < word_bb.width()) ? 0.25f * 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) {
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;
}
}
#endif
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);
}
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);
ScrollView *bl_win = CreateFeatureSpaceWindow("BL Features", 512, 0);
ClearFeatureSpaceWindow(baseline, bl_win);
for (auto &bl_feature : bl_features) {
RenderIntFeature(bl_win, &bl_feature, ScrollView::GREEN);
}
bl_win->Update();
ScrollView *cn_win = CreateFeatureSpaceWindow("CN Features", 512, 0);
ClearFeatureSpaceWindow(character, cn_win);
for (auto &cn_feature : cn_features) {
RenderIntFeature(cn_win, &cn_feature, ScrollView::GREEN);
}
cn_win->Update();
it->DeleteCurrentWord();
delete it;
}
# endif
}
#endif
}