22 #include "config_auto.h"
25 #include "allheaders.h"
34 "Debug level for split shiro-rekha process.");
37 "Whether to create a debug image for split shiro-rekha process.");
43 segmentation_block_list_ =
nullptr;
44 splitted_image_ =
nullptr;
46 perform_close_ =
false;
47 debug_image_ =
nullptr;
57 pixDestroy(&orig_pix_);
58 pixDestroy(&splitted_image_);
61 pixDestroy(&debug_image_);
62 segmentation_block_list_ =
nullptr;
64 perform_close_ =
false;
70 pixDestroy(&orig_pix_);
72 orig_pix_ = pixClone(pix);
81 SplitStrategy split_strategy = split_for_pageseg ? pageseg_split_strategy_ :
90 tprintf(
"Splitting shiro-rekha ...\n");
91 tprintf(
"Split strategy = %s\n",
93 tprintf(
"Initial pageseg available = %s\n",
94 segmentation_block_list_ ?
"yes" :
"no");
97 pixDestroy(&splitted_image_);
98 splitted_image_ = pixCopy(
nullptr, orig_pix_);
102 pixDestroy(&debug_image_);
103 debug_image_ = pixConvertTo32(orig_pix_);
108 Pix* pix_for_ccs = pixClone(orig_pix_);
110 !segmentation_block_list_) {
112 tprintf(
"Performing a global close operation..\n");
116 pixDestroy(&pix_for_ccs);
117 pix_for_ccs = pixCopy(
nullptr, orig_pix_);
118 PerformClose(pix_for_ccs, global_xheight_);
121 Boxa* tmp_boxa = pixConnComp(pix_for_ccs, &ccs, 8);
122 boxaDestroy(&tmp_boxa);
123 pixDestroy(&pix_for_ccs);
128 Boxa* regions_to_clear = boxaCreate(0);
130 if (ccs !=
nullptr) num_ccs = pixaGetCount(ccs);
131 for (
int i = 0; i < num_ccs; ++i) {
132 Box* box = ccs->boxa->box[i];
133 Pix* word_pix = pixClipRectangle(orig_pix_, box,
nullptr);
135 int xheight = GetXheightForCC(box);
138 pixRenderBoxArb(debug_image_, box, 1, 255, 0, 0);
145 (box->w > xheight / 3 && box->h > xheight / 2)) {
146 SplitWordShiroRekha(split_strategy, word_pix, xheight,
147 box->x, box->y, regions_to_clear);
149 tprintf(
"CC dropped from splitting: %d,%d (%d, %d)\n",
150 box->x, box->y, box->w, box->h);
152 pixDestroy(&word_pix);
155 for (
int i = 0; i < boxaGetCount(regions_to_clear); ++i) {
156 Box* box = boxaGetBox(regions_to_clear, i, L_CLONE);
157 pixClearInRect(splitted_image_, box);
160 boxaDestroy(®ions_to_clear);
163 pixa_debug->
AddPix(debug_image_,
164 split_for_pageseg ?
"pageseg_split" :
"ocr_split");
171 void ShiroRekhaSplitter::PerformClose(Pix* pix,
int xheight_estimate) {
172 pixCloseBrick(pix, pix, xheight_estimate / 8, xheight_estimate / 3);
177 int ShiroRekhaSplitter::GetXheightForCC(Box* cc_bbox) {
178 if (!segmentation_block_list_) {
179 return global_xheight_;
182 TBOX bbox(cc_bbox->x,
183 pixGetHeight(orig_pix_) - cc_bbox->y - cc_bbox->h - 1,
184 cc_bbox->x + cc_bbox->w,
185 pixGetHeight(orig_pix_) - cc_bbox->y - 1);
187 BLOCK_IT block_it(segmentation_block_list_);
188 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
189 BLOCK* block = block_it.data();
192 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
193 ROW* row = row_it.data();
203 float box_middle = 0.5 * (bbox.left() + bbox.right());
210 if (bbox.major_overlap(test_box)) {
235 void ShiroRekhaSplitter::SplitWordShiroRekha(SplitStrategy split_strategy,
240 Boxa* regions_to_clear) {
244 int width = pixGetWidth(pix);
245 int height = pixGetHeight(pix);
247 int shirorekha_top, shirorekha_bottom, shirorekha_ylevel;
248 GetShiroRekhaYExtents(pix, &shirorekha_top, &shirorekha_bottom,
252 int stroke_width = shirorekha_bottom - shirorekha_top + 1;
257 if (shirorekha_ylevel > height / 2) {
260 tprintf(
"Skipping splitting CC at (%d, %d): shirorekha in lower half..\n",
261 word_left, word_top);
265 if (stroke_width > height / 3) {
268 tprintf(
"Skipping splitting CC at (%d, %d): stroke width too huge..\n",
269 word_left, word_top);
276 Box* box_to_clear = boxCreate(0, shirorekha_top - stroke_width / 3,
277 width, 5 * stroke_width / 3);
278 Pix* word_in_xheight = pixCopy(
nullptr, pix);
279 pixClearInRect(word_in_xheight, box_to_clear);
283 int leeway_to_keep = stroke_width * 3;
288 leeway_to_keep = xheight - stroke_width;
290 box_to_clear->y = shirorekha_bottom + leeway_to_keep;
291 box_to_clear->h = height - box_to_clear->y;
292 pixClearInRect(word_in_xheight, box_to_clear);
293 boxDestroy(&box_to_clear);
295 PixelHistogram vert_hist;
296 vert_hist.ConstructVerticalCountHist(word_in_xheight);
297 pixDestroy(&word_in_xheight);
303 for (
int i = 0; i < width; ++i) {
304 if (vert_hist.hist()[i] <= stroke_width / 4)
305 vert_hist.hist()[i] = 0;
307 vert_hist.hist()[i] = 1;
312 int cur_component_width = 0;
314 if (!vert_hist.hist()[i]) {
316 while (i + j < width && !vert_hist.hist()[i+j])
318 if (j >= stroke_width / 2 && cur_component_width >= stroke_width / 2) {
326 int split_width = minimal_split ? 1 : j;
327 int split_left = minimal_split ? i + (j / 2) - (split_width / 2) : i;
328 if (!minimal_split || (i != 0 && i + j != width)) {
330 boxCreate(word_left + split_left,
331 word_top + shirorekha_top - stroke_width / 3,
333 5 * stroke_width / 3);
335 boxaAddBox(regions_to_clear, box_to_clear, L_CLONE);
338 pixRenderBoxArb(debug_image_, box_to_clear, 1, 128, 255, 128);
340 boxDestroy(&box_to_clear);
341 cur_component_width = 0;
348 ++cur_component_width;
357 C_BLOB_LIST* new_blobs) {
361 tprintf(
"Before refreshing blobs:\n");
363 tprintf(
"New Blobs found: %d\n", new_blobs->length());
366 C_BLOB_LIST not_found_blobs;
370 ¬_found_blobs :
nullptr));
373 tprintf(
"After refreshing blobs:\n");
379 C_BLOB_IT not_found_it(¬_found_blobs);
380 for (not_found_it.mark_cycle_pt(); !not_found_it.cycled_list();
381 not_found_it.forward()) {
382 C_BLOB* not_found = not_found_it.data();
384 Box* box_to_plot = GetBoxForTBOX(not_found_box);
385 pixRenderBoxArb(debug_image_, box_to_plot, 1, 255, 0, 255);
386 boxDestroy(&box_to_plot);
390 C_BLOB_IT all_blobs_it(new_blobs);
391 for (all_blobs_it.mark_cycle_pt(); !all_blobs_it.cycled_list();
392 all_blobs_it.forward()) {
393 C_BLOB* a_blob = all_blobs_it.data();
394 Box* box_to_plot = GetBoxForTBOX(a_blob->
bounding_box());
395 pixRenderBoxArb(debug_image_, box_to_plot, 3, 0, 127, 0);
396 boxDestroy(&box_to_plot);
403 Box* ShiroRekhaSplitter::GetBoxForTBOX(
const TBOX& tbox)
const {
404 return boxCreate(tbox.
left(), pixGetHeight(orig_pix_) - tbox.
top() - 1,
411 Boxa* boxa = pixConnComp(pix,
nullptr, 8);
412 STATS heights(0, pixGetHeight(pix));
414 for (
int i = 0; i < boxaGetCount(boxa); ++i) {
415 Box* box = boxaGetBox(boxa, i, L_CLONE);
416 if (box->h >= 3 || box->w >= 3) {
417 heights.
add(box->h, 1);
422 return heights.
mode();
427 void ShiroRekhaSplitter::GetShiroRekhaYExtents(Pix* word_pix,
429 int* shirorekha_bottom,
430 int* shirorekha_ylevel) {
436 int topline_onpixel_count = 0;
440 int thresh = (topline_onpixel_count * 70) / 100;
441 int ulimit = topline_ylevel;
442 int llimit = topline_ylevel;
443 while (ulimit > 0 && hist_horiz.
hist()[ulimit] >= thresh)
445 while (llimit < pixGetHeight(word_pix) && hist_horiz.
hist()[llimit] >= thresh)
448 if (shirorekha_top) *shirorekha_top = ulimit;
449 if (shirorekha_bottom) *shirorekha_bottom = llimit;
450 if (shirorekha_ylevel) *shirorekha_ylevel = topline_ylevel;
457 for (
int i = 0; i < length_; ++i) {
458 if (hist_[i] > hist_[best_value]) {
463 *
count = hist_[best_value];
471 int width = pixGetWidth(pix);
472 int height = pixGetHeight(pix);
473 hist_ =
new int[width];
475 int wpl = pixGetWpl(pix);
476 l_uint32 *data = pixGetData(pix);
477 for (
int i = 0; i < width; ++i)
479 for (
int i = 0; i < height; ++i) {
480 l_uint32 *line = data + i * wpl;
481 for (
int j = 0; j < width; ++j)
482 if (GET_DATA_BIT(line, j))
489 Numa* counts = pixCountPixelsByRow(pix,
nullptr);
490 length_ = numaGetCount(counts);
491 hist_ =
new int[length_];
492 for (
int i = 0; i < length_; ++i) {
494 numaGetIValue(counts, i, &val);
497 numaDestroy(&counts);