39 #include "allheaders.h" 44 #define MAX_NEAREST_DIST 600 //for block skew stats 57 int pix_height = pixGetHeight(pix);
58 const TBOX& box = blob->bounding_box();
59 int width = box.
width();
61 Box* blob_pix_box = boxCreate(box.
left(), pix_height - box.
top(),
63 Pix* pix_blob = pixClipRectangle(pix, blob_pix_box, NULL);
64 boxDestroy(&blob_pix_box);
65 Pix* dist_pix = pixDistanceFunction(pix_blob, 4, 8, L_BOUNDARY_BG);
66 pixDestroy(&pix_blob);
68 uinT32* data = pixGetData(dist_pix);
69 int wpl = pixGetWpl(dist_pix);
71 STATS h_stats(0, width + 1);
72 for (
int y = 0; y < height; ++y) {
73 uinT32* pixels = data + y*wpl;
75 int pixel = GET_DATA_BYTE(pixels, 0);
76 for (
int x = 1; x < width; ++x) {
77 int next_pixel = GET_DATA_BYTE(pixels, x);
80 if (prev_pixel < pixel &&
81 (y == 0 || pixel == GET_DATA_BYTE(pixels - wpl, x - 1)) &&
82 (y == height - 1 || pixel == GET_DATA_BYTE(pixels + wpl, x - 1))) {
83 if (pixel > next_pixel) {
85 h_stats.
add(pixel * 2 - 1, 1);
86 }
else if (pixel == next_pixel && x + 1 < width &&
87 pixel > GET_DATA_BYTE(pixels, x + 1)) {
89 h_stats.
add(pixel * 2, 1);
97 STATS v_stats(0, height + 1);
98 for (
int x = 0; x < width; ++x) {
100 int pixel = GET_DATA_BYTE(data, x);
101 for (
int y = 1; y < height; ++y) {
102 uinT32* pixels = data + y*wpl;
103 int next_pixel = GET_DATA_BYTE(pixels, x);
106 if (prev_pixel < pixel &&
107 (x == 0 || pixel == GET_DATA_BYTE(pixels - wpl, x - 1)) &&
108 (x == width - 1 || pixel == GET_DATA_BYTE(pixels - wpl, x + 1))) {
109 if (pixel > next_pixel) {
111 v_stats.
add(pixel * 2 - 1, 1);
112 }
else if (pixel == next_pixel && y + 1 < height &&
113 pixel > GET_DATA_BYTE(pixels + wpl, x)) {
115 v_stats.
add(pixel * 2, 1);
122 pixDestroy(&dist_pix);
129 if (h_stats.
get_total() >= (width + height) / 4) {
130 blob->set_horz_stroke_width(h_stats.
ile(0.5f));
131 if (v_stats.
get_total() >= (width + height) / 4)
132 blob->set_vert_stroke_width(v_stats.
ile(0.5f));
134 blob->set_vert_stroke_width(0.0f);
136 if (v_stats.
get_total() >= (width + height) / 4 ||
138 blob->set_horz_stroke_width(0.0f);
139 blob->set_vert_stroke_width(v_stats.
ile(0.5f));
141 blob->set_horz_stroke_width(h_stats.
get_total() > 2 ? h_stats.
ile(0.5f)
143 blob->set_vert_stroke_width(0.0f);
156 TO_BLOCK_LIST *port_blocks) {
160 BLOCK_IT block_it = blocks;
162 BLOBNBOX_IT port_box_it;
164 TO_BLOCK_IT port_block_it = port_blocks;
167 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
168 block = block_it.data();
172 port_box_it.set_to_list(&port_block->
blobs);
174 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
175 blob = blob_it.extract();
178 port_box_it.add_after_then_move(newblob);
186 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
187 blob = blob_it.extract();
190 port_box_it.add_after_then_move(newblob);
193 port_block_it.add_after_then_move(port_block);
206 TO_BLOCK_LIST *to_blocks) {
207 int width = pixGetWidth(pix);
208 int height = pixGetHeight(pix);
210 tprintf(
"Input image too large! (%d, %d)\n", width, height);
216 BLOCK_IT block_it(blocks);
217 for (block_it.mark_cycle_pt(); !block_it.cycled_list();
218 block_it.forward()) {
219 BLOCK* block = block_it.data();
226 ICOORD page_tr(width, height);
237 TO_BLOCK_LIST *blocks,
239 TO_BLOCK_IT block_it = blocks;
242 #ifndef GRAPHICS_DISABLED 245 #endif // GRAPHICS_DISABLED 247 for (block_it.mark_cycle_pt(); !block_it.cycled_list();
248 block_it.forward()) {
249 block = block_it.data();
263 #ifndef GRAPHICS_DISABLED 277 #endif // GRAPHICS_DISABLED 287 float Textord::filter_noise_blobs(
288 BLOBNBOX_LIST *src_list,
289 BLOBNBOX_LIST *noise_list,
290 BLOBNBOX_LIST *small_list,
291 BLOBNBOX_LIST *large_list) {
296 BLOBNBOX_IT src_it = src_list;
297 BLOBNBOX_IT noise_it = noise_list;
298 BLOBNBOX_IT small_it = small_list;
299 BLOBNBOX_IT large_it = large_list;
307 for (src_it.mark_cycle_pt(); !src_it.cycled_list(); src_it.forward()) {
308 blob = src_it.data();
310 noise_it.add_after_then_move(src_it.extract());
313 small_it.add_after_then_move(src_it.extract());
315 for (src_it.mark_cycle_pt(); !src_it.cycled_list(); src_it.forward()) {
316 size_stats.add(src_it.data()->bounding_box().height(), 1);
319 max_y = ceil(initial_x *
324 min_y = floor (initial_x / 2);
326 small_it.move_to_first ();
327 for (small_it.mark_cycle_pt (); !small_it.cycled_list ();
328 small_it.forward ()) {
329 height = small_it.data()->bounding_box().height();
331 large_it.add_after_then_move(small_it.extract ());
332 else if (height >= min_y)
333 src_it.add_after_then_move(small_it.extract ());
336 for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) {
337 height = src_it.data ()->bounding_box ().height ();
338 width = src_it.data ()->bounding_box ().width ();
340 small_it.add_after_then_move (src_it.extract ());
341 else if (height > max_y || width > max_x)
342 large_it.add_after_then_move (src_it.extract ());
344 size_stats.add (height, 1);
350 if (max_height > initial_x)
351 initial_x = max_height;
360 void Textord::cleanup_nontext_block(
BLOCK* block) {
363 if (row_it.empty()) {
365 float height = box.
height();
367 double coeffs[3] = {0.0, 0.0,
static_cast<double>(box.
bottom())};
368 ROW* row =
new ROW(1, xstarts, coeffs, height / 2.0f, height / 4.0f,
369 height / 4.0f, 0, 1);
370 row_it.add_after_then_move(row);
373 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
374 ROW* row = row_it.data();
382 C_BLOB_IT blob_it(&blobs);
383 blob_it.add_after_then_move(blob);
384 WERD* word =
new WERD(&blobs, 0, NULL);
385 w_it.add_after_then_move(word);
388 for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) {
389 WERD* word = w_it.data();
404 void Textord::cleanup_blocks(
bool clean_noise, BLOCK_LIST* blocks) {
405 BLOCK_IT block_it = blocks;
409 int num_rows_all = 0;
411 int num_blocks_all = 0;
412 for (block_it.mark_cycle_pt(); !block_it.cycled_list();
413 block_it.forward()) {
414 BLOCK* block = block_it.data();
416 cleanup_nontext_block(block);
422 row_it.set_to_list(block->
row_list());
423 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
424 ROW* row = row_it.data();
426 clean_small_noise_from_words(row);
428 clean_noise_from_row(row)) ||
430 delete row_it.extract();
433 clean_noise_from_words(row_it.data());
442 delete block_it.extract();
448 tprintf(
"cleanup_blocks: # rows = %d / %d\n", num_rows, num_rows_all);
451 tprintf(
"cleanup_blocks: # blocks = %d / %d\n", num_blocks, num_blocks_all);
461 BOOL8 Textord::clean_noise_from_row(
470 inT32 trans_count = 0;
471 inT32 trans_threshold;
474 inT32 super_norm_count;
488 super_norm_count = 0;
489 for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) {
490 word = word_it.data ();
493 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
494 blob_it.forward ()) {
495 blob = blob_it.data ();
498 out_it.set_to_list (blob->
out_list ());
499 for (out_it.mark_cycle_pt (); !out_it.cycled_list ();
501 outline = out_it.data ();
507 if (blob_size < textord_noise_sizelimit * row->x_height ())
509 if (!outline->
child ()->empty ()
514 && blob_box.
width () <
516 && blob_box.
width () >
528 && blob_size < row->
x_height () * 2) {
535 && (!word_it.at_first () || !blob_it.at_first ()))
539 (
"Blob at (%d,%d) -> (%d,%d), ols=%d, tc=%d, bldiff=%g\n",
541 blob_box.
top (), blob->
out_list ()->length (), trans_count,
547 tprintf (
"Row ending at (%d,%g):",
549 tprintf (
" R=%g, dc=%d, nc=%d, %s\n",
550 norm_count > 0 ? (
float) dot_count / norm_count : 9999,
551 dot_count, norm_count,
553 && dot_count > 2 ?
"REJECTED" :
"ACCEPTED");
565 void Textord::clean_noise_from_words(
575 inT32 trans_threshold;
586 ok_words = word_it.length ();
593 for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) {
594 word = word_it.data ();
599 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
600 blob_it.forward ()) {
601 blob = blob_it.data ();
604 out_it.set_to_list (blob->
out_list ());
605 for (out_it.mark_cycle_pt (); !out_it.cycled_list ();
607 outline = out_it.data ();
613 if (blob_size < textord_noise_sizelimit * row->x_height ())
615 if (!outline->
child ()->empty ()
620 && blob_box.
width () <
622 && blob_box.
width () >
634 && blob_size < row->
x_height () * 2) {
641 && (!word_it.at_first () || !blob_it.at_first ()))
646 word_dud[word_index] = 2;
648 word_dud[word_index] = 1;
650 word_dud[word_index] = 0;
652 word_dud[word_index] = 0;
654 if (word_dud[word_index] == 2)
662 for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) {
663 if (word_dud[word_index] == 2
664 || (word_dud[word_index] == 1 && dud_words > ok_words)) {
665 word = word_it.data();
678 void Textord::clean_small_noise_from_words(
ROW *row) {
680 for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) {
681 WERD* word = word_it.data();
682 int min_size =
static_cast<int>(
685 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
686 C_BLOB* blob = blob_it.data();
687 C_OUTLINE_IT out_it(blob->
out_list());
688 for (out_it.mark_cycle_pt(); !out_it.cycled_list(); out_it.forward()) {
693 delete blob_it.extract();
697 if (!word_it.at_last()) {
700 WERD* next_word = word_it.data_relative(1);
705 delete word_it.extract();
735 void Textord::TransferDiacriticsToBlockGroups(BLOBNBOX_LIST* diacritic_blobs,
736 BLOCK_LIST* blocks) {
739 const double kMaxAngleDiff = 0.01;
741 BLOCK_IT bk_it(blocks);
742 for (bk_it.mark_cycle_pt(); !bk_it.cycled_list(); bk_it.forward()) {
743 BLOCK* block = bk_it.data();
751 for (
int g = 0; g < groups.
size(); ++g) {
752 double angle_diff = fabs(block_angle - groups[g]->angle);
753 if (angle_diff > M_PI) angle_diff = fabs(angle_diff - 2.0 * M_PI);
754 if (angle_diff < best_angle_diff) {
755 best_angle_diff = angle_diff;
759 if (best_angle_diff > kMaxAngleDiff) {
765 if (x_height < groups[best_g]->min_xheight)
766 groups[best_g]->min_xheight = x_height;
770 PointerVector<WordWithBox> word_ptrs;
771 for (
int g = 0; g < groups.
size(); ++g) {
772 const BlockGroup* group = groups[g];
773 if (group->bounding_box.null_box())
continue;
774 WordGrid word_grid(group->min_xheight, group->bounding_box.botleft(),
775 group->bounding_box.topright());
776 for (
int b = 0; b < group->blocks.size(); ++b) {
777 ROW_IT row_it(group->blocks[b]->row_list());
778 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
779 ROW* row = row_it.data();
782 for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) {
783 WERD* word = w_it.data();
784 WordWithBox* box_word =
new WordWithBox(word);
785 word_grid.InsertBBox(
true,
true, box_word);
787 word_ptrs.push_back(box_word);
791 FCOORD rotation = group->rotation;
793 rotation.
set_y(-rotation.
y());
794 TransferDiacriticsToWords(diacritic_blobs, rotation, &word_grid);
801 void Textord::TransferDiacriticsToWords(BLOBNBOX_LIST* diacritic_blobs,
805 BLOBNBOX_IT b_it(diacritic_blobs);
809 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
812 blob_box.
rotate(rotation);
813 ws.StartRectSearch(blob_box);
818 WordWithBox* best_above_word = NULL;
819 WordWithBox* best_below_word = NULL;
820 int best_above_distance = 0;
821 int best_below_distance = 0;
822 for (WordWithBox* word = ws.NextRectSearch(); word != NULL;
823 word = ws.NextRectSearch()) {
826 int x_distance = blob_box.
x_gap(word_box);
827 int y_distance = blob_box.
y_gap(word_box);
828 if (x_distance > 0) {
838 y_distance += x_distance;
841 (best_above_word == NULL || y_distance < best_above_distance)) {
842 best_above_word = word;
843 best_above_distance = y_distance;
846 (best_below_word == NULL || y_distance < best_below_distance)) {
847 best_below_word = word;
848 best_below_distance = y_distance;
852 best_above_word != NULL &&
853 (best_below_word == NULL ||
854 best_above_distance < best_below_distance + blob_box.
height());
856 best_below_word != NULL && best_below_word != best_above_word &&
857 (best_above_word == NULL ||
858 best_below_distance < best_above_distance + blob_box.
height());
861 copied_blob->
rotate(rotation);
863 C_BLOB_IT blob_it(best_below_word->RejBlobs());
864 blob_it.add_to_end(copied_blob);
868 copied_blob->
rotate(rotation);
870 C_BLOB_IT blob_it(best_above_word->RejBlobs());
871 blob_it.add_to_end(copied_blob);
886 double blshift_maxshift,
887 double blshift_xfraction) {
903 for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) {
904 word = word_it.data ();
914 (
double *)
alloc_mem ((blob_count + row->baseline.segments) * 3 *
919 xstarts[0] = row->baseline.xcoords[0];
920 for (word_it.mark_cycle_pt (); !word_it.cycled_list (); word_it.forward ()) {
921 word = word_it.data ();
924 for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
925 blob_it.forward ()) {
926 blob = blob_it.data ();
928 x_centre = (blob_box.
left () + blob_box.
right ()) / 2.0;
934 if (ydiff < blshift_maxshift
936 if (xstarts[dest_index] >= x_centre)
937 xstarts[dest_index] = blob_box.
left ();
938 coeffs[dest_index * 3] = 0;
939 coeffs[dest_index * 3 + 1] = 0;
940 coeffs[dest_index * 3 + 2] = blob_box.
bottom ();
943 xstarts[dest_index] = blob_box.
right () + 1;
946 if (xstarts[dest_index] <= x_centre) {
947 while (row->baseline.xcoords[src_index + 1] <= x_centre
948 && src_index < row->
baseline.segments - 1) {
949 if (row->baseline.xcoords[src_index + 1] >
950 xstarts[dest_index]) {
951 coeffs[dest_index * 3] =
952 row->baseline.quadratics[src_index].
a;
953 coeffs[dest_index * 3 + 1] =
954 row->baseline.quadratics[src_index].
b;
955 coeffs[dest_index * 3 + 2] =
956 row->baseline.quadratics[src_index].
c;
958 xstarts[dest_index] =
959 row->baseline.xcoords[src_index + 1];
963 coeffs[dest_index * 3] =
964 row->baseline.quadratics[src_index].
a;
965 coeffs[dest_index * 3 + 1] =
966 row->baseline.quadratics[src_index].
b;
967 coeffs[dest_index * 3 + 2] =
968 row->baseline.quadratics[src_index].
c;
970 xstarts[dest_index] = row->baseline.xcoords[src_index + 1];
975 while (src_index < row->
baseline.segments
976 && row->baseline.xcoords[src_index + 1] <= xstarts[dest_index])
978 while (src_index < row->
baseline.segments) {
979 coeffs[dest_index * 3] = row->baseline.quadratics[src_index].
a;
980 coeffs[dest_index * 3 + 1] = row->baseline.quadratics[src_index].
b;
981 coeffs[dest_index * 3 + 2] = row->baseline.quadratics[src_index].
c;
984 xstarts[dest_index] = row->baseline.xcoords[src_index];
987 row->baseline =
QSPLINE (dest_index, xstarts, coeffs);
int textord_max_noise_size
double textord_min_linesize
const TBOX & bounding_box() const
void rotate(const FCOORD &vec)
TBOX true_bounding_box() const
bool textord_test_landscape
ScrollView * create_to_win(ICOORD page_tr)
bool textord_noise_rejrows
void rotate(const FCOORD &rotation)
TBOX bounding_box() const
float base_line(float xpos) const
void SetBlobStrokeWidth(Pix *pix, BLOBNBOX *blob)
void CleanNoise(float size_threshold)
int textord_noise_translimit
BBGrid< WordWithBox, WordWithBox_CLIST, WordWithBox_C_IT > WordGrid
int textord_noise_sizefraction
void add(inT32 value, inT32 count)
inT32 count_transitions(inT32 threshold)
void set_global_loc_code(int loc_code)
TBOX bounding_box() const
ROW_LIST * row_list()
get rows
double textord_initialasc_ile
double textord_noise_syfract
void tweak_row_baseline(ROW *row, double blshift_maxshift, double blshift_xfraction)
inT32 enclosed_area() const
void find_components(Pix *pix, BLOCK_LIST *blocks, TO_BLOCK_LIST *to_blocks)
C_OUTLINE_LIST * out_list()
void recalc_bounding_box()
void plot_box_list(ScrollView *win, BLOBNBOX_LIST *list, ScrollView::Color body_colour)
C_BLOB_LIST * reject_blobs()
double textord_excess_blobsize
POLY_BLOCK * poly_block() const
int y_gap(const TBOX &box) const
FCOORD re_rotation() const
BLOBNBOX_LIST noise_blobs
void filter_blobs(ICOORD page_tr, TO_BLOCK_LIST *blocks, BOOL8 testing_on)
BOOL8 flag(WERD_FLAGS mask) const
void RemoveSmallRecursive(int min_size, C_OUTLINE_IT *it)
static const double kXHeightCapRatio
void * alloc_mem(inT32 count)
GridSearch< WordWithBox, WordWithBox_CLIST, WordWithBox_C_IT > WordSearch
BLOBNBOX_LIST large_blobs
void set_y(float yin)
rewrite function
void extract_edges(Pix *pix, BLOCK *block)
GenericVector< BLOCK * > blocks
bool textord_noise_rejwords
double textord_noise_sxfract
const TBOX & bounding_box() const
int x_gap(const TBOX &box) const
static const double kAscenderFraction
double textord_noise_normratio
double textord_blshift_xfraction
static C_BLOB * deep_copy(const C_BLOB *src)
double textord_noise_sizelimit
BLOBNBOX_LIST small_blobs
void plot_graded_blobs(ScrollView *to_win)
double textord_blshift_maxshift
static const double kXHeightFraction
double ile(double frac) const
inT32 x_height() const
return xheight
static C_BLOB * FakeBlob(const TBOX &box)
void assign_blobs_to_blocks2(Pix *pix, BLOCK_LIST *blocks, TO_BLOCK_LIST *port_blocks)
int textord_noise_sncount
static const double kDescenderFraction
double textord_width_limit
C_BLOB_LIST * blob_list()
get blobs
EXTERN ScrollView * to_win
void free_mem(void *oldchunk)
void set_flag(WERD_FLAGS mask, BOOL8 value)
double textord_noise_rowratio
#define CLISTIZE(CLASSNAME)
bool major_y_overlap(const TBOX &box) const
C_BLOB_LIST * cblob_list()
float angle() const
find angle
double textord_noise_area_ratio
double textord_noise_hfract
double textord_initialx_ile
TBOX bounding_box() const
void bounding_box(ICOORD &bottom_left, ICOORD &top_right) const
get box