22 #pragma warning(disable:4244) // Conversion warnings 36 #include "allheaders.h" 66 static void RemoveUnusedLineSegments(
bool horizontal_lines,
67 BLOBNBOX_LIST* line_bblobs,
69 int height = pixGetHeight(line_pix);
70 BLOBNBOX_IT bbox_it(line_bblobs);
71 for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) {
76 if (horizontal_lines) {
81 pixbox = boxCreate(box.
bottom(), height - box.
right(),
87 pixbox = boxCreate(box.
left(), height - box.
top(),
90 pixClearInRect(line_pix, pixbox);
100 static void SubtractLinesAndResidue(Pix* line_pix, Pix* non_line_pix,
101 int resolution, Pix* src_pix) {
103 pixSubtract(src_pix, src_pix, line_pix);
105 Pix* residue_pix = pixSubtract(NULL, src_pix, non_line_pix);
107 Pix* fat_line_pix = pixDilateBrick(NULL, line_pix, 3, 3);
109 pixSeedfillBinary(fat_line_pix, fat_line_pix, residue_pix, 8);
111 pixSubtract(src_pix, src_pix, fat_line_pix);
112 pixDestroy(&fat_line_pix);
113 pixDestroy(&residue_pix);
118 static int MaxStrokeWidth(Pix* pix) {
119 Pix* dist_pix = pixDistanceFunction(pix, 4, 8, L_BOUNDARY_BG);
120 int width = pixGetWidth(dist_pix);
121 int height = pixGetHeight(dist_pix);
122 int wpl = pixGetWpl(dist_pix);
123 l_uint32* data = pixGetData(dist_pix);
126 for (
int y = 0; y < height; ++y) {
127 for (
int x = 0; x < width; ++x) {
128 int pixel = GET_DATA_BYTE(data, x);
129 if (pixel > max_dist)
134 pixDestroy(&dist_pix);
139 static int NumTouchingIntersections(Box* line_box, Pix* intersection_pix) {
140 if (intersection_pix == NULL)
return 0;
141 Pix* rect_pix = pixClipRectangle(intersection_pix, line_box, NULL);
142 Boxa* boxa = pixConnComp(rect_pix, NULL, 8);
143 pixDestroy(&rect_pix);
144 if (boxa == NULL)
return false;
145 int result = boxaGetCount(boxa);
153 static int CountPixelsAdjacentToLine(
int line_width, Box* line_box,
155 l_int32 x, y, box_width, box_height;
156 boxGetGeometry(line_box, &x, &y, &box_width, &box_height);
157 if (box_width > box_height) {
159 int bottom =
MIN(pixGetHeight(nonline_pix), y + box_height + line_width);
160 y =
MAX(0, y - line_width);
161 box_height = bottom - y;
164 int right =
MIN(pixGetWidth(nonline_pix), x + box_width + line_width);
165 x =
MAX(0, x - line_width);
166 box_width = right - x;
168 Box* box = boxCreate(x, y, box_width, box_height);
169 Pix* rect_pix = pixClipRectangle(nonline_pix, box, NULL);
172 pixCountPixels(rect_pix, &result, NULL);
173 pixDestroy(&rect_pix);
186 static int FilterFalsePositives(
int resolution, Pix* nonline_pix,
187 Pix* intersection_pix, Pix* line_pix) {
190 Boxa* boxa = pixConnComp(line_pix, &pixa, 8);
192 int nboxes = boxaGetCount(boxa);
193 int remaining_boxes = nboxes;
194 for (
int i = 0; i < nboxes; ++i) {
195 Box* box = boxaGetBox(boxa, i, L_CLONE);
196 l_int32 x, y, box_width, box_height;
197 boxGetGeometry(box, &x, &y, &box_width, &box_height);
198 Pix* comp_pix = pixaGetPix(pixa, i, L_CLONE);
199 int max_width = MaxStrokeWidth(comp_pix);
200 pixDestroy(&comp_pix);
201 bool bad_line =
false;
205 box_width < min_thick_length && box_height < min_thick_length &&
211 (intersection_pix == NULL ||
212 NumTouchingIntersections(box, intersection_pix) < 2)) {
214 int nonline_count = CountPixelsAdjacentToLine(max_width, box,
221 pixClearInRect(line_pix, box);
228 return remaining_boxes;
244 int* vertical_x,
int* vertical_y,
245 Pix** pix_music_mask,
246 TabVector_LIST* v_lines,
247 TabVector_LIST* h_lines) {
249 if (pix == NULL || vertical_x == NULL || vertical_y == NULL) {
250 tprintf(
"Error in parameters for LineFinder::FindAndRemoveLines\n");
253 Pix* pix_vline = NULL;
254 Pix* pix_non_vline = NULL;
255 Pix* pix_hline = NULL;
256 Pix* pix_non_hline = NULL;
257 Pix* pix_intersections = NULL;
258 Pixa* pixa_display = debug ? pixaCreate(0) : NULL;
259 GetLineMasks(resolution, pix, &pix_vline, &pix_non_vline, &pix_hline,
260 &pix_non_hline, &pix_intersections, pix_music_mask,
263 FindAndRemoveVLines(resolution, pix_intersections, vertical_x, vertical_y,
264 &pix_vline, pix_non_vline, pix, v_lines);
265 if (pix_hline != NULL) {
267 if (pix_vline != NULL)
268 pixAnd(pix_intersections, pix_vline, pix_hline);
270 pixDestroy(&pix_intersections);
271 if (!FilterFalsePositives(resolution, pix_non_hline, pix_intersections,
273 pixDestroy(&pix_hline);
276 FindAndRemoveHLines(resolution, pix_intersections, *vertical_x, *vertical_y,
277 &pix_hline, pix_non_hline, pix, h_lines);
278 if (pixa_display != NULL && pix_vline != NULL)
279 pixaAddPix(pixa_display, pix_vline, L_CLONE);
280 if (pixa_display != NULL && pix_hline != NULL)
281 pixaAddPix(pixa_display, pix_hline, L_CLONE);
282 if (pix_vline != NULL && pix_hline != NULL) {
285 pixAnd(pix_intersections, pix_vline, pix_hline);
288 Pix* pix_join_residue = pixDilateBrick(NULL, pix_intersections, 5, 5);
289 pixSeedfillBinary(pix_join_residue, pix_join_residue, pix, 8);
291 pixSubtract(pix, pix, pix_join_residue);
292 pixDestroy(&pix_join_residue);
295 if (pix_music_mask != NULL && *pix_music_mask != NULL) {
296 if (pixa_display != NULL)
297 pixaAddPix(pixa_display, *pix_music_mask, L_CLONE);
298 pixSubtract(pix, pix, *pix_music_mask);
300 if (pixa_display != NULL)
301 pixaAddPix(pixa_display, pix, L_CLONE);
303 pixDestroy(&pix_vline);
304 pixDestroy(&pix_non_vline);
305 pixDestroy(&pix_hline);
306 pixDestroy(&pix_non_hline);
307 pixDestroy(&pix_intersections);
308 if (pixa_display != NULL) {
309 pixaConvertToPdf(pixa_display, resolution, 1.0f, 0, 0,
"LineFinding",
310 "vhlinefinding.pdf");
311 pixaDestroy(&pixa_display);
322 Boxa** boxes, C_BLOB_LIST* blobs) {
323 C_OUTLINE_LIST outlines;
324 C_OUTLINE_IT ol_it = &outlines;
326 int nboxes = boxaGetCount(*boxes);
327 for (
int i = 0; i < nboxes; ++i) {
328 l_int32 x, y, width, height;
329 boxaGetBoxGeometry(*boxes, i, &x, &y, &width, &height);
334 ICOORD bot_right(x + width, y + height);
336 startpt.
pos = top_left;
338 ol_it.add_after_then_move(outline);
345 ICOORD page_br(image_width, image_height);
348 C_BLOB_IT blob_it(blobs);
349 blob_it.add_list_after(block.
blob_list());
364 void LineFinder::FindAndRemoveVLines(
int resolution,
365 Pix* pix_intersections,
366 int* vertical_x,
int* vertical_y,
367 Pix** pix_vline, Pix* pix_non_vline,
368 Pix* src_pix, TabVector_LIST* vectors) {
369 if (pix_vline == NULL || *pix_vline == NULL)
return;
370 C_BLOB_LIST line_cblobs;
371 BLOBNBOX_LIST line_bblobs;
372 GetLineBoxes(
false, *pix_vline, pix_intersections,
373 &line_cblobs, &line_bblobs);
374 int width = pixGetWidth(src_pix);
375 int height = pixGetHeight(src_pix);
377 ICOORD tright(width, height);
378 FindLineVectors(bleft, tright, &line_bblobs, vertical_x, vertical_y, vectors);
379 if (!vectors->empty()) {
380 RemoveUnusedLineSegments(
false, &line_bblobs, *pix_vline);
381 SubtractLinesAndResidue(*pix_vline, pix_non_vline, resolution, src_pix);
386 pixDestroy(pix_vline);
400 void LineFinder::FindAndRemoveHLines(
int resolution,
401 Pix* pix_intersections,
402 int vertical_x,
int vertical_y,
403 Pix** pix_hline, Pix* pix_non_hline,
404 Pix* src_pix, TabVector_LIST* vectors) {
405 if (pix_hline == NULL || *pix_hline == NULL)
return;
406 C_BLOB_LIST line_cblobs;
407 BLOBNBOX_LIST line_bblobs;
408 GetLineBoxes(
true, *pix_hline, pix_intersections, &line_cblobs, &line_bblobs);
409 int width = pixGetWidth(src_pix);
410 int height = pixGetHeight(src_pix);
412 ICOORD tright(height, width);
413 FindLineVectors(bleft, tright, &line_bblobs, &vertical_x, &vertical_y,
415 if (!vectors->empty()) {
416 RemoveUnusedLineSegments(
true, &line_bblobs, *pix_hline);
417 SubtractLinesAndResidue(*pix_hline, pix_non_hline, resolution, src_pix);
424 TabVector_IT h_it(vectors);
425 for (h_it.mark_cycle_pt(); !h_it.cycled_list(); h_it.forward()) {
426 h_it.data()->XYFlip();
429 pixDestroy(pix_hline);
438 void LineFinder::FindLineVectors(
const ICOORD& bleft,
const ICOORD& tright,
439 BLOBNBOX_LIST* line_bblobs,
440 int* vertical_x,
int* vertical_y,
441 TabVector_LIST* vectors) {
442 BLOBNBOX_IT bbox_it(line_bblobs);
447 for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) {
454 blob_grid.InsertBBox(
false,
true, bblob);
463 TabVector_IT vector_it(vectors);
466 lsearch.StartFullSearch();
467 while ((bbox = lsearch.NextFullSearch()) != NULL) {
471 tprintf(
"Finding line vector starting at bbox (%d,%d)\n",
473 AlignedBlobParams align_params(*vertical_x, *vertical_y, box.
width());
474 TabVector* vector = blob_grid.FindVerticalAlignment(align_params, bbox,
477 if (vector != NULL) {
479 vector_it.add_to_end(vector);
490 static Pix* FilterMusic(
int resolution, Pix* pix_closed,
491 Pix* pix_vline, Pix* pix_hline,
492 l_int32* v_empty, l_int32* h_empty) {
494 Pix* intersection_pix = pixAnd(NULL, pix_vline, pix_hline);
495 Boxa* boxa = pixConnComp(pix_vline, NULL, 8);
497 int nboxes = boxaGetCount(boxa);
498 Pix* music_mask = NULL;
499 for (
int i = 0; i < nboxes; ++i) {
500 Box* box = boxaGetBox(boxa, i, L_CLONE);
501 l_int32 x, y, box_width, box_height;
502 boxGetGeometry(box, &x, &y, &box_width, &box_height);
503 int joins = NumTouchingIntersections(box, intersection_pix);
506 if (joins >= 5 && (joins - 1) * max_stave_height >= 4 * box_height) {
508 if (music_mask == NULL)
509 music_mask = pixCreate(pixGetWidth(pix_vline), pixGetHeight(pix_vline),
511 pixSetInRect(music_mask, box);
516 pixDestroy(&intersection_pix);
517 if (music_mask != NULL) {
521 pixSeedfillBinary(music_mask, music_mask, pix_closed, 8);
525 Boxa* boxa = pixConnComp(music_mask, NULL, 8);
527 int nboxes = boxaGetCount(boxa);
528 for (
int i = 0; i < nboxes; ++i) {
529 Box* box = boxaGetBox(boxa, i, L_CLONE);
530 Pix* rect_pix = pixClipRectangle(music_mask, box, NULL);
531 l_int32 music_pixels;
532 pixCountPixels(rect_pix, &music_pixels, NULL);
533 pixDestroy(&rect_pix);
534 rect_pix = pixClipRectangle(pix_closed, box, NULL);
536 pixCountPixels(rect_pix, &all_pixels, NULL);
537 pixDestroy(&rect_pix);
540 pixClearInRect(music_mask, box);
544 l_int32 no_remaining_music;
546 pixZero(music_mask, &no_remaining_music);
547 if (no_remaining_music) {
548 pixDestroy(&music_mask);
550 pixSubtract(pix_vline, pix_vline, music_mask);
551 pixSubtract(pix_hline, pix_hline, music_mask);
553 pixZero(pix_vline, v_empty);
554 pixZero(pix_hline, h_empty);
572 void LineFinder::GetLineMasks(
int resolution, Pix* src_pix,
573 Pix** pix_vline, Pix** pix_non_vline,
574 Pix** pix_hline, Pix** pix_non_hline,
575 Pix** pix_intersections, Pix** pix_music_mask,
576 Pixa* pixa_display) {
577 Pix* pix_closed = NULL;
578 Pix* pix_hollow = NULL;
582 if (pixa_display != NULL) {
583 tprintf(
"Image resolution = %d, max line width = %d, min length=%d\n",
584 resolution, max_line_width, min_line_length);
586 int closing_brick = max_line_width / 3;
591 if (OpenclDevice::selectedDeviceIsOpenCL()) {
593 int clStatus = OpenclDevice::initMorphCLAllocations(pixGetWpl(src_pix),
594 pixGetHeight(src_pix),
596 bool getpixclosed = pix_music_mask != NULL ? true :
false;
597 OpenclDevice::pixGetLinesCL(NULL, src_pix, pix_vline, pix_hline,
598 &pix_closed, getpixclosed, closing_brick,
599 closing_brick, max_line_width, max_line_width,
600 min_line_length, min_line_length);
606 pix_closed = pixCloseBrick(NULL, src_pix, closing_brick, closing_brick);
607 if (pixa_display != NULL)
608 pixaAddPix(pixa_display, pix_closed, L_CLONE);
611 Pix* pix_solid = pixOpenBrick(NULL, pix_closed, max_line_width,
613 if (pixa_display != NULL)
614 pixaAddPix(pixa_display, pix_solid, L_CLONE);
615 pix_hollow = pixSubtract(NULL, pix_closed, pix_solid);
617 pixDestroy(&pix_solid);
621 if (pixa_display != NULL)
622 pixaAddPix(pixa_display, pix_hollow, L_CLONE);
623 *pix_vline = pixOpenBrick(NULL, pix_hollow, 1, min_line_length);
624 *pix_hline = pixOpenBrick(NULL, pix_hollow, min_line_length, 1);
626 pixDestroy(&pix_hollow);
635 pixZero(*pix_vline, &v_empty);
636 pixZero(*pix_hline, &h_empty);
637 if (pix_music_mask != NULL) {
638 if (!v_empty && !h_empty) {
639 *pix_music_mask = FilterMusic(resolution, pix_closed,
640 *pix_vline, *pix_hline,
643 *pix_music_mask = NULL;
646 pixDestroy(&pix_closed);
647 Pix* pix_nonlines = NULL;
648 *pix_intersections = NULL;
649 Pix* extra_non_hlines = NULL;
652 pix_nonlines = pixSubtract(NULL, src_pix, *pix_vline);
654 pixSubtract(pix_nonlines, pix_nonlines, *pix_hline);
656 *pix_intersections = pixAnd(NULL, *pix_vline, *pix_hline);
659 extra_non_hlines = pixSubtract(NULL, *pix_vline, *pix_intersections);
661 *pix_non_vline = pixErodeBrick(NULL, pix_nonlines,
kMaxLineResidue, 1);
662 pixSeedfillBinary(*pix_non_vline, *pix_non_vline, pix_nonlines, 8);
665 pixOr(*pix_non_vline, *pix_non_vline, *pix_hline);
666 pixSubtract(*pix_non_vline, *pix_non_vline, *pix_intersections);
668 if (!FilterFalsePositives(resolution, *pix_non_vline, *pix_intersections,
670 pixDestroy(pix_vline);
673 pixDestroy(pix_vline);
674 *pix_non_vline = NULL;
676 pix_nonlines = pixSubtract(NULL, src_pix, *pix_hline);
680 pixDestroy(pix_hline);
681 *pix_non_hline = NULL;
686 *pix_non_hline = pixErodeBrick(NULL, pix_nonlines, 1,
kMaxLineResidue);
687 pixSeedfillBinary(*pix_non_hline, *pix_non_hline, pix_nonlines, 8);
688 if (extra_non_hlines != NULL) {
689 pixOr(*pix_non_hline, *pix_non_hline, extra_non_hlines);
690 pixDestroy(&extra_non_hlines);
692 if (!FilterFalsePositives(resolution, *pix_non_hline, *pix_intersections,
694 pixDestroy(pix_hline);
696 if (pixa_display != NULL) {
697 if (*pix_vline != NULL) pixaAddPix(pixa_display, *pix_vline, L_CLONE);
698 if (*pix_hline != NULL) pixaAddPix(pixa_display, *pix_hline, L_CLONE);
699 if (pix_nonlines != NULL) pixaAddPix(pixa_display, pix_nonlines, L_CLONE);
700 if (*pix_non_vline != NULL)
701 pixaAddPix(pixa_display, *pix_non_vline, L_CLONE);
702 if (*pix_non_hline != NULL)
703 pixaAddPix(pixa_display, *pix_non_hline, L_CLONE);
704 if (*pix_intersections != NULL)
705 pixaAddPix(pixa_display, *pix_intersections, L_CLONE);
706 if (pix_music_mask != NULL && *pix_music_mask != NULL)
707 pixaAddPix(pixa_display, *pix_music_mask, L_CLONE);
709 pixDestroy(&pix_nonlines);
715 void LineFinder::GetLineBoxes(
bool horizontal_lines,
716 Pix* pix_lines, Pix* pix_intersections,
717 C_BLOB_LIST* line_cblobs,
718 BLOBNBOX_LIST* line_bblobs) {
722 int wpl = pixGetWpl(pix_lines);
723 int width = pixGetWidth(pix_lines);
724 int height = pixGetHeight(pix_lines);
725 l_uint32* data = pixGetData(pix_lines);
726 if (horizontal_lines) {
727 for (
int y = 0; y < height; ++y, data += wpl) {
729 CLEAR_DATA_BIT(data, x);
734 memset(data + wpl * y, 0, wpl *
sizeof(*data));
738 Boxa* boxa = pixConnComp(pix_lines, NULL, 8);
741 C_BLOB_IT blob_it(line_cblobs);
742 BLOBNBOX_IT bbox_it(line_bblobs);
743 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
744 C_BLOB* cblob = blob_it.data();
746 bbox_it.add_to_end(bblob);
749 Box* box = boxCreate(bbox.
left(), bbox.
bottom(),
757 if (horizontal_lines) {
const TBOX & bounding_box() const
const int kMinThickLineWidth
static bool WithinTestRegion(int detail_level, int x, int y)
const int kMinLineLengthFraction
Denominator of resolution makes min pixels to demand line lengths to be.
void set_line_crossings(int value)
void outlines_to_blobs(BLOCK *block, ICOORD bleft, ICOORD tright, C_OUTLINE_LIST *outlines)
const double kThickLengthMultiple
const int kMaxLineResidue
void set_with_shrink(int x, int y)
Set from the given x,y, shrinking the vector to fit if needed.
const double kMaxNonLineDensity
void set_right_rule(int new_right)
static void MergeSimilarTabVectors(const ICOORD &vertical, TabVector_LIST *vectors, BlobGrid *grid)
void set_left_rule(int new_left)
const int kLineFindGridSize
Grid size used by line finder. Not very critical.
inT16 x() const
access function
const int kThinLineFraction
Denominator of resolution makes max pixel width to allow thin lines.
#define PERF_COUNT_START(FUNCT_NAME)
const int kCrackSpacing
Spacing of cracks across the page to break up tall vertical lines.
GridSearch< BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT > BlobGridSearch
void set_bounding_box(const TBOX &new_box)
void set_left_tab_type(TabType new_type)
const double kMaxStaveHeight
const double kMinMusicPixelFraction
static void ConvertBoxaToBlobs(int image_width, int image_height, Boxa **boxes, C_BLOB_LIST *blobs)
void set_right_crossing_rule(int new_right)
C_BLOB_LIST * blob_list()
get blobs
void set_left_crossing_rule(int new_left)
TabType left_tab_type() const
static void FindAndRemoveLines(int resolution, bool debug, Pix *pix, int *vertical_x, int *vertical_y, Pix **pix_music_mask, TabVector_LIST *v_lines, TabVector_LIST *h_lines)