31 #include <allheaders.h>
40 "Whether to create a debug image for split shiro-rekha process.");
44 segmentation_block_list_ =
nullptr;
45 splitted_image_ =
nullptr;
47 perform_close_ =
false;
48 debug_image_ =
nullptr;
63 segmentation_block_list_ =
nullptr;
65 perform_close_ =
false;
73 orig_pix_ = pix.
clone();
82 SplitStrategy split_strategy = split_for_pageseg ? pageseg_split_strategy_ : ocr_split_strategy_;
89 tprintf(
"Splitting shiro-rekha ...\n");
91 tprintf(
"Initial pageseg available = %s\n", segmentation_block_list_ ?
"yes" :
"no");
95 splitted_image_ = orig_pix_.
copy();
100 debug_image_ = pixConvertTo32(orig_pix_);
106 if (perform_close_ && global_xheight_ !=
kUnspecifiedXheight && !segmentation_block_list_) {
108 tprintf(
"Performing a global close operation..\n");
113 pix_for_ccs = orig_pix_.
copy();
114 PerformClose(pix_for_ccs, global_xheight_);
117 Boxa *tmp_boxa = pixConnComp(pix_for_ccs, &ccs, 8);
118 boxaDestroy(&tmp_boxa);
124 Boxa *regions_to_clear = boxaCreate(0);
126 if (ccs !=
nullptr) {
127 num_ccs = pixaGetCount(ccs);
129 for (
int i = 0; i < num_ccs; ++i) {
130 Box *box = ccs->boxa->box[i];
131 Image word_pix = pixClipRectangle(orig_pix_, box,
nullptr);
133 int xheight = GetXheightForCC(box);
135 pixRenderBoxArb(debug_image_, box, 1, 255, 0, 0);
142 SplitWordShiroRekha(split_strategy, word_pix, xheight, box->x, box->y, regions_to_clear);
144 tprintf(
"CC dropped from splitting: %d,%d (%d, %d)\n", box->x, box->y, box->w, box->h);
149 for (
int i = 0; i < boxaGetCount(regions_to_clear); ++i) {
150 Box *box = boxaGetBox(regions_to_clear, i, L_CLONE);
151 pixClearInRect(splitted_image_, box);
154 boxaDestroy(®ions_to_clear);
157 pixa_debug->
AddPix(debug_image_, split_for_pageseg ?
"pageseg_split" :
"ocr_split");
164 void ShiroRekhaSplitter::PerformClose(
Image pix,
int xheight_estimate) {
165 pixCloseBrick(pix, pix, xheight_estimate / 8, xheight_estimate / 3);
170 int ShiroRekhaSplitter::GetXheightForCC(Box *cc_bbox) {
171 if (!segmentation_block_list_) {
172 return global_xheight_;
175 TBOX bbox(cc_bbox->x, pixGetHeight(orig_pix_) - cc_bbox->y - cc_bbox->h - 1,
176 cc_bbox->x + cc_bbox->w, pixGetHeight(orig_pix_) - cc_bbox->y - 1);
178 BLOCK_IT block_it(segmentation_block_list_);
179 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
180 BLOCK *block = block_it.data();
182 ROW_IT row_it(block->row_list());
183 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
184 ROW *row = row_it.data();
185 if (!row->bounding_box().major_overlap(bbox)) {
194 float box_middle = 0.5 * (bbox.left() + bbox.right());
195 int baseline =
static_cast<int>(row->base_line(box_middle) + 0.5);
196 TBOX test_box(box_middle - row->x_height() / 2,
baseline, box_middle + row->x_height() / 2,
197 static_cast<int>(
baseline + row->x_height()));
199 if (bbox.major_overlap(test_box)) {
200 return row->x_height();
224 void ShiroRekhaSplitter::SplitWordShiroRekha(SplitStrategy split_strategy, Image pix,
int xheight,
225 int word_left,
int word_top, Boxa *regions_to_clear) {
229 int width = pixGetWidth(pix);
230 int height = pixGetHeight(pix);
232 int shirorekha_top, shirorekha_bottom, shirorekha_ylevel;
233 GetShiroRekhaYExtents(pix, &shirorekha_top, &shirorekha_bottom, &shirorekha_ylevel);
236 int stroke_width = shirorekha_bottom - shirorekha_top + 1;
241 if (shirorekha_ylevel > height / 2) {
244 tprintf(
"Skipping splitting CC at (%d, %d): shirorekha in lower half..\n", word_left,
249 if (stroke_width > height / 3) {
252 tprintf(
"Skipping splitting CC at (%d, %d): stroke width too huge..\n", word_left, word_top);
259 Box *box_to_clear = boxCreate(0, shirorekha_top - stroke_width / 3, width, 5 * stroke_width / 3);
260 Image word_in_xheight = pix.copy();
261 pixClearInRect(word_in_xheight, box_to_clear);
265 int leeway_to_keep = stroke_width * 3;
270 leeway_to_keep = xheight - stroke_width;
272 box_to_clear->y = shirorekha_bottom + leeway_to_keep;
273 box_to_clear->h = height - box_to_clear->y;
274 pixClearInRect(word_in_xheight, box_to_clear);
275 boxDestroy(&box_to_clear);
277 PixelHistogram vert_hist;
278 vert_hist.ConstructVerticalCountHist(word_in_xheight);
279 word_in_xheight.destroy();
285 for (
int i = 0; i < width; ++i) {
286 if (vert_hist.hist()[i] <= stroke_width / 4) {
287 vert_hist.hist()[i] = 0;
289 vert_hist.hist()[i] = 1;
295 int cur_component_width = 0;
297 if (!vert_hist.hist()[i]) {
299 while (i + j < width && !vert_hist.hist()[i + j]) {
302 if (j >= stroke_width / 2 && cur_component_width >= stroke_width / 2) {
310 int split_width = minimal_split ? 1 : j;
311 int split_left = minimal_split ? i + (j / 2) - (split_width / 2) : i;
312 if (!minimal_split || (i != 0 && i + j != width)) {
314 boxCreate(word_left + split_left, word_top + shirorekha_top - stroke_width / 3,
315 split_width, 5 * stroke_width / 3);
317 boxaAddBox(regions_to_clear, box_to_clear, L_CLONE);
320 pixRenderBoxArb(debug_image_, box_to_clear, 1, 128, 255, 128);
322 boxDestroy(&box_to_clear);
323 cur_component_width = 0;
330 ++cur_component_width;
342 tprintf(
"Before refreshing blobs:\n");
344 tprintf(
"New Blobs found: %d\n", new_blobs->length());
347 C_BLOB_LIST not_found_blobs;
349 segmentation_block_list_, new_blobs,
353 tprintf(
"After refreshing blobs:\n");
359 C_BLOB_IT not_found_it(¬_found_blobs);
360 for (not_found_it.mark_cycle_pt(); !not_found_it.cycled_list(); not_found_it.forward()) {
361 C_BLOB *not_found = not_found_it.data();
363 Box *box_to_plot = GetBoxForTBOX(not_found_box);
364 pixRenderBoxArb(debug_image_, box_to_plot, 1, 255, 0, 255);
365 boxDestroy(&box_to_plot);
369 C_BLOB_IT all_blobs_it(new_blobs);
370 for (all_blobs_it.mark_cycle_pt(); !all_blobs_it.cycled_list(); all_blobs_it.forward()) {
371 C_BLOB *a_blob = all_blobs_it.data();
372 Box *box_to_plot = GetBoxForTBOX(a_blob->
bounding_box());
373 pixRenderBoxArb(debug_image_, box_to_plot, 3, 0, 127, 0);
374 boxDestroy(&box_to_plot);
381 Box *ShiroRekhaSplitter::GetBoxForTBOX(
const TBOX &tbox)
const {
382 return boxCreate(tbox.
left(), pixGetHeight(orig_pix_) - tbox.
top() - 1, tbox.
width(),
389 Boxa *boxa = pixConnComp(pix,
nullptr, 8);
390 STATS heights(0, pixGetHeight(pix));
392 for (
int i = 0; i < boxaGetCount(boxa); ++i) {
393 Box *box = boxaGetBox(boxa, i, L_CLONE);
394 if (box->h >= 3 || box->w >= 3) {
395 heights.
add(box->h, 1);
400 return heights.
mode();
405 void ShiroRekhaSplitter::GetShiroRekhaYExtents(
Image word_pix,
int *shirorekha_top,
406 int *shirorekha_bottom,
int *shirorekha_ylevel) {
412 int topline_onpixel_count = 0;
416 int thresh = (topline_onpixel_count * 70) / 100;
417 int ulimit = topline_ylevel;
418 int llimit = topline_ylevel;
419 while (ulimit > 0 && hist_horiz.
hist()[ulimit] >= thresh) {
422 while (llimit < pixGetHeight(word_pix) && hist_horiz.
hist()[llimit] >= thresh) {
426 if (shirorekha_top) {
427 *shirorekha_top = ulimit;
429 if (shirorekha_bottom) {
430 *shirorekha_bottom = llimit;
432 if (shirorekha_ylevel) {
433 *shirorekha_ylevel = topline_ylevel;
441 for (
int i = 0; i < length_; ++i) {
442 if (hist_[i] > hist_[best_value]) {
447 *count = hist_[best_value];
455 int width = pixGetWidth(pix);
456 int height = pixGetHeight(pix);
457 hist_ =
new int[width];
459 int wpl = pixGetWpl(pix);
460 l_uint32 *data = pixGetData(pix);
461 for (
int i = 0; i < width; ++i) {
464 for (
int i = 0; i < height; ++i) {
465 l_uint32 *line = data + i * wpl;
466 for (
int j = 0; j < width; ++j) {
467 if (GET_DATA_BIT(line, j)) {
476 Numa *counts = pixCountPixelsByRow(pix,
nullptr);
477 length_ = numaGetCount(counts);
478 hist_ =
new int[length_];
479 for (
int i = 0; i < length_; ++i) {
481 numaGetIValue(counts, i, &val);
484 numaDestroy(&counts);
#define BOOL_VAR(name, val, comment)
#define INT_VAR(name, val, comment)
bool devanagari_split_debugimage
void tprintf(const char *format,...)
void RefreshWordBlobsFromNewBlobs(BLOCK_LIST *block_list, C_BLOB_LIST *new_blobs, C_BLOB_LIST *not_found_blobs)
void PrintSegmentationStats(BLOCK_LIST *block_list)
int devanagari_split_debuglevel
void AddPix(const Image pix, const char *caption)
TDimension height() const
void add(int32_t value, int32_t count)
TBOX bounding_box() const
int GetHistogramMaximum(int *count) const
void ConstructHorizontalCountHist(Image pix)
void ConstructVerticalCountHist(Image pix)
virtual ~ShiroRekhaSplitter()
void set_orig_pix(Image pix)
void RefreshSegmentationWithNewBlobs(C_BLOB_LIST *new_blobs)
bool Split(bool split_for_pageseg, DebugPixa *pixa_debug)
static const int kUnspecifiedXheight
static int GetModeHeight(Image pix)