22 #pragma warning(disable:4244) // Conversion warnings 26 #include "config_auto.h" 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;
204 if (box_width >= kMinThickLineWidth && box_height >= kMinThickLineWidth &&
205 box_width < min_thick_length && box_height < min_thick_length &&
206 max_width > kMinThickLineWidth) {
211 (intersection_pix == NULL ||
212 NumTouchingIntersections(box, intersection_pix) < 2)) {
214 int nonline_count = CountPixelsAdjacentToLine(max_width, box,
216 if (nonline_count > box_height * box_width * kMaxNonLineDensity)
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);
446 AlignedBlob blob_grid(kLineFindGridSize, bleft, tright);
447 for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) {
463 TabVector_IT vector_it(vectors);
471 tprintf(
"Finding line vector starting at bbox (%d,%d)\n",
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);
538 if (music_pixels < kMinMusicPixelFraction * all_pixels) {
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);
733 for (
int y = kCrackSpacing; y < height; y +=
kCrackSpacing) {
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 double kMaxStaveHeight
const double kMaxNonLineDensity
#define PERF_COUNT_START(FUNCT_NAME)
inT16 x() const
access function
void set_bounding_box(const TBOX &new_box)
static void ConvertBoxaToBlobs(int image_width, int image_height, Boxa **boxes, C_BLOB_LIST *blobs)
void set_left_rule(int new_left)
void set_right_crossing_rule(int new_right)
const int kMinLineLengthFraction
Denominator of resolution makes min pixels to demand line lengths to be.
TabType left_tab_type() const
void InsertBBox(bool h_spread, bool v_spread, BBC *bbox)
C_BLOB_LIST * blob_list()
get blobs
static bool WithinTestRegion(int detail_level, int x, int y)
const double kThickLengthMultiple
void set_line_crossings(int value)
void set_left_crossing_rule(int new_left)
const int kMinThickLineWidth
const int kLineFindGridSize
Grid size used by line finder. Not very critical.
void set_with_shrink(int x, int y)
Set from the given x,y, shrinking the vector to fit if needed.
void set_right_rule(int new_right)
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)
const int kCrackSpacing
Spacing of cracks across the page to break up tall vertical lines.
void set_left_tab_type(TabType new_type)
const double kMinMusicPixelFraction
const int kMaxLineResidue
const TBOX & bounding_box() const
static void MergeSimilarTabVectors(const ICOORD &vertical, TabVector_LIST *vectors, BlobGrid *grid)
void outlines_to_blobs(BLOCK *block, ICOORD bleft, ICOORD tright, C_OUTLINE_LIST *outlines)
TabVector * FindVerticalAlignment(AlignedBlobParams align_params, BLOBNBOX *bbox, int *vertical_x, int *vertical_y)
const int kThinLineFraction
Denominator of resolution makes max pixel width to allow thin lines.