22 #include "allheaders.h"
30 "Debug level for split shiro-rekha process.");
33 "Whether to create a debug image for split shiro-rekha process.");
39 segmentation_block_list_ =
NULL;
40 splitted_image_ =
NULL;
42 perform_close_ =
false;
53 pixDestroy(&orig_pix_);
54 pixDestroy(&splitted_image_);
57 pixDestroy(&debug_image_);
58 segmentation_block_list_ =
NULL;
60 perform_close_ =
false;
65 pixWrite(filename, debug_image_, IFF_PNG);
71 pixDestroy(&orig_pix_);
73 orig_pix_ = pixClone(pix);
82 SplitStrategy split_strategy = split_for_pageseg ? pageseg_split_strategy_ :
91 tprintf(
"Splitting shiro-rekha ...\n");
92 tprintf(
"Split strategy = %s\n",
94 tprintf(
"Initial pageseg available = %s\n",
95 segmentation_block_list_ ?
"yes" :
"no");
98 pixDestroy(&splitted_image_);
99 splitted_image_ = pixCopy(
NULL, orig_pix_);
103 pixDestroy(&debug_image_);
104 debug_image_ = pixConvertTo32(orig_pix_);
109 Pix* pix_for_ccs = pixClone(orig_pix_);
111 !segmentation_block_list_) {
113 tprintf(
"Performing a global close operation..\n");
117 pixDestroy(&pix_for_ccs);
118 pix_for_ccs = pixCopy(
NULL, orig_pix_);
119 PerformClose(pix_for_ccs, global_xheight_);
122 Boxa* tmp_boxa = pixConnComp(pix_for_ccs, &ccs, 8);
123 boxaDestroy(&tmp_boxa);
124 pixDestroy(&pix_for_ccs);
129 Boxa* regions_to_clear = boxaCreate(0);
130 for (
int i = 0; i < pixaGetCount(ccs); ++i) {
131 Box* box = ccs->boxa->box[i];
132 Pix* word_pix = pixClipRectangle(orig_pix_, box,
NULL);
134 int xheight = GetXheightForCC(box);
137 pixRenderBoxArb(debug_image_, box, 1, 255, 0, 0);
144 (box->w > xheight / 3 && box->h > xheight / 2)) {
145 SplitWordShiroRekha(split_strategy, word_pix, xheight,
146 box->x, box->y, regions_to_clear);
148 tprintf(
"CC dropped from splitting: %d,%d (%d, %d)\n",
149 box->x, box->y, box->w, box->h);
151 pixDestroy(&word_pix);
154 for (
int i = 0; i < boxaGetCount(regions_to_clear); ++i) {
155 Box* box = boxaGetBox(regions_to_clear, i, L_CLONE);
156 pixClearInRect(splitted_image_, box);
159 boxaDestroy(®ions_to_clear);
163 "ocr_split_debug.png");
170 void ShiroRekhaSplitter::PerformClose(Pix* pix,
int xheight_estimate) {
171 pixCloseBrick(pix, pix, xheight_estimate / 8, xheight_estimate / 3);
176 int ShiroRekhaSplitter::GetXheightForCC(Box* cc_bbox) {
177 if (!segmentation_block_list_) {
178 return global_xheight_;
181 TBOX bbox(cc_bbox->x,
182 pixGetHeight(orig_pix_) - cc_bbox->y - cc_bbox->h - 1,
183 cc_bbox->x + cc_bbox->w,
184 pixGetHeight(orig_pix_) - cc_bbox->y - 1);
186 BLOCK_IT block_it(segmentation_block_list_);
187 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
188 BLOCK* block = block_it.data();
191 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
192 ROW* row = row_it.data();
202 float box_middle = 0.5 * (bbox.left() + bbox.right());
207 static_cast<int>(baseline + row->
x_height()));
209 if (bbox.major_overlap(test_box)) {
234 void ShiroRekhaSplitter::SplitWordShiroRekha(SplitStrategy split_strategy,
239 Boxa* regions_to_clear) {
243 int width = pixGetWidth(pix);
244 int height = pixGetHeight(pix);
246 int shirorekha_top, shirorekha_bottom, shirorekha_ylevel;
247 GetShiroRekhaYExtents(pix, &shirorekha_top, &shirorekha_bottom,
251 int stroke_width = shirorekha_bottom - shirorekha_top + 1;
256 if (shirorekha_ylevel > height / 2) {
259 tprintf(
"Skipping splitting CC at (%d, %d): shirorekha in lower half..\n",
260 word_left, word_top);
264 if (stroke_width > height / 3) {
267 tprintf(
"Skipping splitting CC at (%d, %d): stroke width too huge..\n",
268 word_left, word_top);
275 Box* box_to_clear = boxCreate(0, shirorekha_top - stroke_width / 3,
276 width, 5 * stroke_width / 3);
277 Pix* word_in_xheight = pixCopy(
NULL, pix);
278 pixClearInRect(word_in_xheight, box_to_clear);
282 int leeway_to_keep = stroke_width * 3;
287 leeway_to_keep = xheight - stroke_width;
289 box_to_clear->y = shirorekha_bottom + leeway_to_keep;
290 box_to_clear->h = height - box_to_clear->y;
291 pixClearInRect(word_in_xheight, box_to_clear);
292 boxDestroy(&box_to_clear);
294 PixelHistogram vert_hist;
295 vert_hist.ConstructVerticalCountHist(word_in_xheight);
296 pixDestroy(&word_in_xheight);
302 for (
int i = 0; i < width; ++i) {
303 if (vert_hist.hist()[i] <= stroke_width / 4)
304 vert_hist.hist()[i] = 0;
306 vert_hist.hist()[i] = 1;
311 int cur_component_width = 0;
313 if (!vert_hist.hist()[i]) {
315 while (i + j < width && !vert_hist.hist()[i+j])
317 if (j >= stroke_width / 2 && cur_component_width >= stroke_width / 2) {
325 int split_width = minimal_split ? 1 : j;
326 int split_left = minimal_split ? i + (j / 2) - (split_width / 2) : i;
327 if (!minimal_split || (i != 0 && i + j != width)) {
329 boxCreate(word_left + split_left,
330 word_top + shirorekha_top - stroke_width / 3,
332 5 * stroke_width / 3);
334 boxaAddBox(regions_to_clear, box_to_clear, L_CLONE);
337 pixRenderBoxArb(debug_image_, box_to_clear, 1, 128, 255, 128);
339 boxDestroy(&box_to_clear);
340 cur_component_width = 0;
347 ++cur_component_width;
356 C_BLOB_LIST* new_blobs) {
360 tprintf(
"Before refreshing blobs:\n");
362 tprintf(
"New Blobs found: %d\n", new_blobs->length());
365 C_BLOB_LIST not_found_blobs;
369 ¬_found_blobs :
NULL));
372 tprintf(
"After refreshing blobs:\n");
378 C_BLOB_IT not_found_it(¬_found_blobs);
379 for (not_found_it.mark_cycle_pt(); !not_found_it.cycled_list();
380 not_found_it.forward()) {
381 C_BLOB* not_found = not_found_it.data();
383 Box* box_to_plot = GetBoxForTBOX(not_found_box);
384 pixRenderBoxArb(debug_image_, box_to_plot, 1, 255, 0, 255);
385 boxDestroy(&box_to_plot);
389 C_BLOB_IT all_blobs_it(new_blobs);
390 for (all_blobs_it.mark_cycle_pt(); !all_blobs_it.cycled_list();
391 all_blobs_it.forward()) {
392 C_BLOB* a_blob = all_blobs_it.data();
393 Box* box_to_plot = GetBoxForTBOX(a_blob->
bounding_box());
394 pixRenderBoxArb(debug_image_, box_to_plot, 3, 0, 127, 0);
395 boxDestroy(&box_to_plot);
402 Box* ShiroRekhaSplitter::GetBoxForTBOX(
const TBOX& tbox)
const {
403 return boxCreate(tbox.
left(), pixGetHeight(orig_pix_) - tbox.
top() - 1,
410 Boxa* boxa = pixConnComp(pix,
NULL, 8);
411 STATS heights(0, pixGetHeight(pix));
413 for (
int i = 0; i < boxaGetCount(boxa); ++i) {
414 Box* box = boxaGetBox(boxa, i, L_CLONE);
415 if (box->h >= 3 || box->w >= 3) {
416 heights.
add(box->h, 1);
421 return heights.
mode();
426 void ShiroRekhaSplitter::GetShiroRekhaYExtents(Pix* word_pix,
428 int* shirorekha_bottom,
429 int* shirorekha_ylevel) {
435 int topline_onpixel_count = 0;
439 int thresh = (topline_onpixel_count * 70) / 100;
440 int ulimit = topline_ylevel;
441 int llimit = topline_ylevel;
442 while (ulimit > 0 && hist_horiz.
hist()[ulimit] >= thresh)
444 while (llimit < word_pix->h && hist_horiz.
hist()[llimit] >= thresh)
447 if (shirorekha_top) *shirorekha_top = ulimit;
448 if (shirorekha_bottom) *shirorekha_bottom = llimit;
449 if (shirorekha_ylevel) *shirorekha_ylevel = topline_ylevel;
456 for (
int i = 0; i < length_; ++i) {
457 if (hist_[i] > hist_[best_value]) {
462 *count = hist_[best_value];
470 int width = pixGetWidth(pix);
471 int height = pixGetHeight(pix);
472 hist_ =
new int[width];
474 int wpl = pixGetWpl(pix);
475 l_uint32 *data = pixGetData(pix);
476 for (
int i = 0; i < width; ++i)
478 for (
int i = 0; i < height; ++i) {
479 l_uint32 *line = data + i * wpl;
480 for (
int j = 0; j < width; ++j)
481 if (GET_DATA_BIT(line, j))
488 Numa* counts = pixCountPixelsByRow(pix,
NULL);
489 length_ = numaGetCount(counts);
490 hist_ =
new int[length_];
491 for (
int i = 0; i < length_; ++i) {
493 numaGetIValue(counts, i, &val);
496 numaDestroy(&counts);