26 #include "allheaders.h" 33 "Debug level for split shiro-rekha process.");
36 "Whether to create a debug image for split shiro-rekha process.");
42 segmentation_block_list_ = NULL;
43 splitted_image_ = NULL;
45 perform_close_ =
false;
56 pixDestroy(&orig_pix_);
57 pixDestroy(&splitted_image_);
60 pixDestroy(&debug_image_);
61 segmentation_block_list_ = NULL;
63 perform_close_ =
false;
68 pixWrite(
filename, debug_image_, IFF_PNG);
74 pixDestroy(&orig_pix_);
76 orig_pix_ = pixClone(pix);
85 SplitStrategy split_strategy = split_for_pageseg ? pageseg_split_strategy_ :
94 tprintf(
"Splitting shiro-rekha ...\n");
95 tprintf(
"Split strategy = %s\n",
97 tprintf(
"Initial pageseg available = %s\n",
98 segmentation_block_list_ ?
"yes" :
"no");
101 pixDestroy(&splitted_image_);
102 splitted_image_ = pixCopy(NULL, orig_pix_);
106 pixDestroy(&debug_image_);
107 debug_image_ = pixConvertTo32(orig_pix_);
112 Pix* pix_for_ccs = pixClone(orig_pix_);
114 !segmentation_block_list_) {
116 tprintf(
"Performing a global close operation..\n");
120 pixDestroy(&pix_for_ccs);
121 pix_for_ccs = pixCopy(NULL, orig_pix_);
122 PerformClose(pix_for_ccs, global_xheight_);
125 Boxa* tmp_boxa = pixConnComp(pix_for_ccs, &ccs, 8);
126 boxaDestroy(&tmp_boxa);
127 pixDestroy(&pix_for_ccs);
132 Boxa* regions_to_clear = boxaCreate(0);
133 for (
int i = 0; i < pixaGetCount(ccs); ++i) {
134 Box* box = ccs->boxa->box[i];
135 Pix* word_pix = pixClipRectangle(orig_pix_, box, NULL);
137 int xheight = GetXheightForCC(box);
140 pixRenderBoxArb(debug_image_, box, 1, 255, 0, 0);
147 (box->w > xheight / 3 && box->h > xheight / 2)) {
148 SplitWordShiroRekha(split_strategy, word_pix, xheight,
149 box->x, box->y, regions_to_clear);
151 tprintf(
"CC dropped from splitting: %d,%d (%d, %d)\n",
152 box->x, box->y, box->w, box->h);
154 pixDestroy(&word_pix);
157 for (
int i = 0; i < boxaGetCount(regions_to_clear); ++i) {
158 Box* box = boxaGetBox(regions_to_clear, i, L_CLONE);
159 pixClearInRect(splitted_image_, box);
162 boxaDestroy(®ions_to_clear);
166 "ocr_split_debug.png");
173 void ShiroRekhaSplitter::PerformClose(Pix* pix,
int xheight_estimate) {
174 pixCloseBrick(pix, pix, xheight_estimate / 8, xheight_estimate / 3);
179 int ShiroRekhaSplitter::GetXheightForCC(Box* cc_bbox) {
180 if (!segmentation_block_list_) {
181 return global_xheight_;
184 TBOX bbox(cc_bbox->x,
185 pixGetHeight(orig_pix_) - cc_bbox->y - cc_bbox->h - 1,
186 cc_bbox->x + cc_bbox->w,
187 pixGetHeight(orig_pix_) - cc_bbox->y - 1);
189 BLOCK_IT block_it(segmentation_block_list_);
190 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
191 BLOCK* block = block_it.data();
194 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
195 ROW* row = row_it.data();
205 float box_middle = 0.5 * (bbox.left() + bbox.right());
212 if (bbox.major_overlap(test_box)) {
237 void ShiroRekhaSplitter::SplitWordShiroRekha(SplitStrategy split_strategy,
242 Boxa* regions_to_clear) {
246 int width = pixGetWidth(pix);
247 int height = pixGetHeight(pix);
249 int shirorekha_top, shirorekha_bottom, shirorekha_ylevel;
250 GetShiroRekhaYExtents(pix, &shirorekha_top, &shirorekha_bottom,
254 int stroke_width = shirorekha_bottom - shirorekha_top + 1;
259 if (shirorekha_ylevel > height / 2) {
262 tprintf(
"Skipping splitting CC at (%d, %d): shirorekha in lower half..\n",
263 word_left, word_top);
267 if (stroke_width > height / 3) {
270 tprintf(
"Skipping splitting CC at (%d, %d): stroke width too huge..\n",
271 word_left, word_top);
278 Box* box_to_clear = boxCreate(0, shirorekha_top - stroke_width / 3,
279 width, 5 * stroke_width / 3);
280 Pix* word_in_xheight = pixCopy(NULL, pix);
281 pixClearInRect(word_in_xheight, box_to_clear);
285 int leeway_to_keep = stroke_width * 3;
290 leeway_to_keep = xheight - stroke_width;
292 box_to_clear->y = shirorekha_bottom + leeway_to_keep;
293 box_to_clear->h = height - box_to_clear->y;
294 pixClearInRect(word_in_xheight, box_to_clear);
295 boxDestroy(&box_to_clear);
297 PixelHistogram vert_hist;
298 vert_hist.ConstructVerticalCountHist(word_in_xheight);
299 pixDestroy(&word_in_xheight);
305 for (
int i = 0; i < width; ++i) {
306 if (vert_hist.hist()[i] <= stroke_width / 4)
307 vert_hist.hist()[i] = 0;
309 vert_hist.hist()[i] = 1;
314 int cur_component_width = 0;
316 if (!vert_hist.hist()[i]) {
318 while (i + j < width && !vert_hist.hist()[i+j])
320 if (j >= stroke_width / 2 && cur_component_width >= stroke_width / 2) {
328 int split_width = minimal_split ? 1 : j;
329 int split_left = minimal_split ? i + (j / 2) - (split_width / 2) : i;
330 if (!minimal_split || (i != 0 && i + j != width)) {
332 boxCreate(word_left + split_left,
333 word_top + shirorekha_top - stroke_width / 3,
335 5 * stroke_width / 3);
337 boxaAddBox(regions_to_clear, box_to_clear, L_CLONE);
340 pixRenderBoxArb(debug_image_, box_to_clear, 1, 128, 255, 128);
342 boxDestroy(&box_to_clear);
343 cur_component_width = 0;
350 ++cur_component_width;
359 C_BLOB_LIST* new_blobs) {
363 tprintf(
"Before refreshing blobs:\n");
365 tprintf(
"New Blobs found: %d\n", new_blobs->length());
368 C_BLOB_LIST not_found_blobs;
372 ¬_found_blobs : NULL));
375 tprintf(
"After refreshing blobs:\n");
381 C_BLOB_IT not_found_it(¬_found_blobs);
382 for (not_found_it.mark_cycle_pt(); !not_found_it.cycled_list();
383 not_found_it.forward()) {
384 C_BLOB* not_found = not_found_it.data();
386 Box* box_to_plot = GetBoxForTBOX(not_found_box);
387 pixRenderBoxArb(debug_image_, box_to_plot, 1, 255, 0, 255);
388 boxDestroy(&box_to_plot);
392 C_BLOB_IT all_blobs_it(new_blobs);
393 for (all_blobs_it.mark_cycle_pt(); !all_blobs_it.cycled_list();
394 all_blobs_it.forward()) {
395 C_BLOB* a_blob = all_blobs_it.data();
396 Box* box_to_plot = GetBoxForTBOX(a_blob->
bounding_box());
397 pixRenderBoxArb(debug_image_, box_to_plot, 3, 0, 127, 0);
398 boxDestroy(&box_to_plot);
405 Box* ShiroRekhaSplitter::GetBoxForTBOX(
const TBOX& tbox)
const {
406 return boxCreate(tbox.
left(), pixGetHeight(orig_pix_) - tbox.
top() - 1,
413 Boxa* boxa = pixConnComp(pix, NULL, 8);
414 STATS heights(0, pixGetHeight(pix));
416 for (
int i = 0; i < boxaGetCount(boxa); ++i) {
417 Box* box = boxaGetBox(boxa, i, L_CLONE);
418 if (box->h >= 3 || box->w >= 3) {
419 heights.
add(box->h, 1);
424 return heights.
mode();
429 void ShiroRekhaSplitter::GetShiroRekhaYExtents(Pix* word_pix,
431 int* shirorekha_bottom,
432 int* shirorekha_ylevel) {
438 int topline_onpixel_count = 0;
442 int thresh = (topline_onpixel_count * 70) / 100;
443 int ulimit = topline_ylevel;
444 int llimit = topline_ylevel;
445 while (ulimit > 0 && hist_horiz.
hist()[ulimit] >= thresh)
447 while (llimit < pixGetHeight(word_pix) && hist_horiz.
hist()[llimit] >= thresh)
450 if (shirorekha_top) *shirorekha_top = ulimit;
451 if (shirorekha_bottom) *shirorekha_bottom = llimit;
452 if (shirorekha_ylevel) *shirorekha_ylevel = topline_ylevel;
459 for (
int i = 0; i < length_; ++i) {
460 if (hist_[i] > hist_[best_value]) {
465 *
count = hist_[best_value];
473 int width = pixGetWidth(pix);
474 int height = pixGetHeight(pix);
475 hist_ =
new int[width];
477 int wpl = pixGetWpl(pix);
478 l_uint32 *data = pixGetData(pix);
479 for (
int i = 0; i < width; ++i)
481 for (
int i = 0; i < height; ++i) {
482 l_uint32 *line = data + i * wpl;
483 for (
int j = 0; j < width; ++j)
484 if (GET_DATA_BIT(line, j))
491 Numa* counts = pixCountPixelsByRow(pix, NULL);
492 length_ = numaGetCount(counts);
493 hist_ =
new int[length_];
494 for (
int i = 0; i < length_; ++i) {
496 numaGetIValue(counts, i, &val);
499 numaDestroy(&counts);
void RefreshWordBlobsFromNewBlobs(BLOCK_LIST *block_list, C_BLOB_LIST *new_blobs, C_BLOB_LIST *not_found_blobs)
TBOX bounding_box() const
void set_orig_pix(Pix *pix)
float base_line(float xpos) const
void add(inT32 value, inT32 count)
void ConstructVerticalCountHist(Pix *pix)
TBOX bounding_box() const
ROW_LIST * row_list()
get rows
static const int kUnspecifiedXheight
virtual ~ShiroRekhaSplitter()
bool devanagari_split_debugimage
void PrintSegmentationStats(BLOCK_LIST *block_list)
int GetHistogramMaximum(int *count) const
static int GetModeHeight(Pix *pix)
void RefreshSegmentationWithNewBlobs(C_BLOB_LIST *new_blobs)
bool major_overlap(const TBOX &box) const
void DumpDebugImage(const char *filename) const
void ConstructHorizontalCountHist(Pix *pix)
#define BOOL_VAR(name, val, comment)
bool Split(bool split_for_pageseg)
#define INT_VAR(name, val, comment)
int devanagari_split_debuglevel