21# include "config_auto.h"
30#include <allheaders.h>
39 "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;
62 segmentation_block_list_ =
nullptr;
64 perform_close_ =
false;
72 orig_pix_ = pix.
clone();
81 SplitStrategy split_strategy = split_for_pageseg ? pageseg_split_strategy_ : ocr_split_strategy_;
88 tprintf(
"Splitting shiro-rekha ...\n");
90 tprintf(
"Initial pageseg available = %s\n", segmentation_block_list_ ?
"yes" :
"no");
94 splitted_image_ = orig_pix_.
copy();
99 debug_image_ = pixConvertTo32(orig_pix_);
105 if (perform_close_ && global_xheight_ !=
kUnspecifiedXheight && !segmentation_block_list_) {
107 tprintf(
"Performing a global close operation..\n");
112 pix_for_ccs = orig_pix_.
copy();
113 PerformClose(pix_for_ccs, global_xheight_);
116 Boxa *tmp_boxa = pixConnComp(pix_for_ccs, &ccs, 8);
117 boxaDestroy(&tmp_boxa);
123 Boxa *regions_to_clear = boxaCreate(0);
125 if (ccs !=
nullptr) {
126 num_ccs = pixaGetCount(ccs);
128 for (
int i = 0;
i < num_ccs; ++
i) {
129 Box *box = pixaGetBox(ccs,
i, L_CLONE);
130 Image word_pix = pixClipRectangle(orig_pix_, box,
nullptr);
132 int xheight = GetXheightForCC(box);
134 pixRenderBoxArb(debug_image_, box, 1, 255, 0, 0);
141 boxGetGeometry(box, &
x, &
y, &w, &h);
143 SplitWordShiroRekha(split_strategy, word_pix, xheight,
x,
y, regions_to_clear);
145 tprintf(
"CC dropped from splitting: %d,%d (%d, %d)\n",
x,
y, w, h);
151 for (
int i = 0;
i < boxaGetCount(regions_to_clear); ++
i) {
152 Box *box = boxaGetBox(regions_to_clear,
i, L_CLONE);
153 pixClearInRect(splitted_image_, box);
156 boxaDestroy(®ions_to_clear);
159 pixa_debug->
AddPix(debug_image_, split_for_pageseg ?
"pageseg_split" :
"ocr_split");
166void ShiroRekhaSplitter::PerformClose(
Image pix,
int xheight_estimate) {
167 pixCloseBrick(pix, pix, xheight_estimate / 8, xheight_estimate / 3);
172int ShiroRekhaSplitter::GetXheightForCC(Box *cc_bbox) {
173 if (!segmentation_block_list_) {
174 return global_xheight_;
178 boxGetGeometry(cc_bbox, &
x, &
y, &w, &h);
179 TBOX bbox(
x, pixGetHeight(orig_pix_) -
y - h - 1,
180 x + w, pixGetHeight(orig_pix_) -
y - 1);
182 BLOCK_IT block_it(segmentation_block_list_);
183 for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) {
184 BLOCK *block = block_it.data();
186 ROW_IT row_it(block->row_list());
187 for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
188 ROW *row = row_it.data();
189 if (!row->bounding_box().major_overlap(bbox)) {
198 float box_middle = 0.5 * (bbox.left() + bbox.right());
199 int baseline =
static_cast<int>(row->base_line(box_middle) + 0.5);
200 TBOX test_box(box_middle - row->x_height() / 2,
baseline, box_middle + row->x_height() / 2,
201 static_cast<int>(
baseline + row->x_height()));
203 if (bbox.major_overlap(test_box)) {
204 return row->x_height();
228void ShiroRekhaSplitter::SplitWordShiroRekha(SplitStrategy split_strategy, Image pix,
int xheight,
229 int word_left,
int word_top, Boxa *regions_to_clear) {
233 int width = pixGetWidth(pix);
234 int height = pixGetHeight(pix);
236 int shirorekha_top, shirorekha_bottom, shirorekha_ylevel;
237 GetShiroRekhaYExtents(pix, &shirorekha_top, &shirorekha_bottom, &shirorekha_ylevel);
240 int stroke_width = shirorekha_bottom - shirorekha_top + 1;
245 if (shirorekha_ylevel > height / 2) {
248 tprintf(
"Skipping splitting CC at (%d, %d): shirorekha in lower half..\n", word_left,
253 if (stroke_width > height / 3) {
256 tprintf(
"Skipping splitting CC at (%d, %d): stroke width too huge..\n", word_left, word_top);
263 Box *box_to_clear = boxCreate(0, shirorekha_top - stroke_width / 3, width, 5 * stroke_width / 3);
264 Image word_in_xheight = pix.copy();
265 pixClearInRect(word_in_xheight, box_to_clear);
269 int leeway_to_keep = stroke_width * 3;
274 leeway_to_keep = xheight - stroke_width;
276 auto y = shirorekha_bottom + leeway_to_keep;
277 boxSetGeometry(box_to_clear, -1,
y, -1, height -
y);
278 pixClearInRect(word_in_xheight, box_to_clear);
279 boxDestroy(&box_to_clear);
281 PixelHistogram vert_hist;
282 vert_hist.ConstructVerticalCountHist(word_in_xheight);
283 word_in_xheight.destroy();
289 for (
int i = 0;
i < width; ++
i) {
290 if (vert_hist.hist()[
i] <= stroke_width / 4) {
291 vert_hist.hist()[
i] = 0;
293 vert_hist.hist()[
i] = 1;
299 int cur_component_width = 0;
301 if (!vert_hist.hist()[
i]) {
303 while (
i + j < width && !vert_hist.hist()[
i + j]) {
306 if (j >= stroke_width / 2 && cur_component_width >= stroke_width / 2) {
314 int split_width = minimal_split ? 1 : j;
315 int split_left = minimal_split ?
i + (j / 2) - (split_width / 2) :
i;
316 if (!minimal_split || (
i != 0 &&
i + j != width)) {
318 boxCreate(word_left + split_left, word_top + shirorekha_top - stroke_width / 3,
319 split_width, 5 * stroke_width / 3);
321 boxaAddBox(regions_to_clear, box_to_clear, L_CLONE);
324 pixRenderBoxArb(debug_image_, box_to_clear, 1, 128, 255, 128);
326 boxDestroy(&box_to_clear);
327 cur_component_width = 0;
334 ++cur_component_width;
346 tprintf(
"Before refreshing blobs:\n");
348 tprintf(
"New Blobs found: %d\n", new_blobs->length());
351 C_BLOB_LIST not_found_blobs;
353 segmentation_block_list_, new_blobs,
357 tprintf(
"After refreshing blobs:\n");
363 C_BLOB_IT not_found_it(¬_found_blobs);
364 for (not_found_it.mark_cycle_pt(); !not_found_it.cycled_list(); not_found_it.forward()) {
365 C_BLOB *not_found = not_found_it.data();
367 Box *box_to_plot = GetBoxForTBOX(not_found_box);
368 pixRenderBoxArb(debug_image_, box_to_plot, 1, 255, 0, 255);
369 boxDestroy(&box_to_plot);
373 C_BLOB_IT all_blobs_it(new_blobs);
374 for (all_blobs_it.mark_cycle_pt(); !all_blobs_it.cycled_list(); all_blobs_it.forward()) {
375 C_BLOB *a_blob = all_blobs_it.data();
376 Box *box_to_plot = GetBoxForTBOX(a_blob->
bounding_box());
377 pixRenderBoxArb(debug_image_, box_to_plot, 3, 0, 127, 0);
378 boxDestroy(&box_to_plot);
385Box *ShiroRekhaSplitter::GetBoxForTBOX(
const TBOX &tbox)
const {
386 return boxCreate(tbox.
left(), pixGetHeight(orig_pix_) - tbox.
top() - 1, tbox.
width(),
393 Boxa *boxa = pixConnComp(pix,
nullptr, 8);
394 STATS heights(0, pixGetHeight(pix) - 1);
396 for (
int i = 0;
i < boxaGetCount(boxa); ++
i) {
397 Box *box = boxaGetBox(boxa,
i, L_CLONE);
399 boxGetGeometry(box, &
x, &
y, &w, &h);
400 if (h >= 3 || w >= 3) {
406 return heights.
mode();
411void ShiroRekhaSplitter::GetShiroRekhaYExtents(
Image word_pix,
int *shirorekha_top,
412 int *shirorekha_bottom,
int *shirorekha_ylevel) {
418 int topline_onpixel_count = 0;
422 int thresh = (topline_onpixel_count * 70) / 100;
423 int ulimit = topline_ylevel;
424 int llimit = topline_ylevel;
425 while (ulimit > 0 && hist_horiz.
hist()[ulimit] >= thresh) {
428 while (llimit < pixGetHeight(word_pix) && hist_horiz.
hist()[llimit] >= thresh) {
432 if (shirorekha_top) {
433 *shirorekha_top = ulimit;
435 if (shirorekha_bottom) {
436 *shirorekha_bottom = llimit;
438 if (shirorekha_ylevel) {
439 *shirorekha_ylevel = topline_ylevel;
447 for (
int i = 0;
i < length_; ++
i) {
448 if (hist_[
i] > hist_[best_value]) {
453 *
count = hist_[best_value];
461 int width = pixGetWidth(pix);
462 int height = pixGetHeight(pix);
463 hist_ =
new int[width];
465 int wpl = pixGetWpl(pix);
466 l_uint32 *data = pixGetData(pix);
467 for (
int i = 0;
i < width; ++
i) {
470 for (
int i = 0;
i < height; ++
i) {
471 l_uint32 *line = data +
i * wpl;
472 for (
int j = 0; j < width; ++j) {
473 if (GET_DATA_BIT(line, j)) {
482 Numa *counts = pixCountPixelsByRow(pix,
nullptr);
483 length_ = numaGetCount(counts);
484 hist_ =
new int[length_];
485 for (
int i = 0;
i < length_; ++
i) {
487 numaGetIValue(counts,
i, &val);
490 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)