1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 - This software is distributed in the hope that it will be
4 - useful, but with NO WARRANTY OF ANY KIND.
5 - No author or distributor accepts responsibility to anyone for the
6 - consequences of using this software, or for whether it serves any
7 - particular purpose or works at all, unless he or she says so in
8 - writing. Everyone is granted permission to copy, modify and
9 - redistribute this source code, for commercial or non-commercial
10 - purposes, with the following restrictions: (1) the origin of this
11 - source code must not be misrepresented; (2) modified versions must
12 - be plainly marked as such; and (3) this notice may not be removed
13 - or altered from any source or modified source distribution.
14 *====================================================================*/
15
16 /*
17 * seedfill.c
18 *
19 * Binary seedfill (source: Luc Vincent)
20 * PIX *pixSeedfillBinary()
21 * PIX *pixSeedfillBinaryRestricted()
22 *
23 * Applications of binary seedfill to find and fill holes,
24 * and to remove c.c. touching the border:
25 * PIX *pixHolesByFilling()
26 * PIX *pixFillClosedBorders()
27 * PIX *pixExtractBorderConnComps()
28 * PIX *pixRemoveBorderConnComps()
29 *
30 * Hole-filling of components to bounding rectangle
31 * PIX *pixFillHolesToBoundingRect()
32 *
33 * Gray seedfill (source: Luc Vincent:fast-hybrid-grayscale-reconstruction)
34 * l_int32 pixSeedfillGray()
35 * l_int32 pixSeedfillGrayInv()
36 *
37 * Gray seedfill (source: Luc Vincent: sequential-reconstruction algorithm)
38 * l_int32 pixSeedfillGraySimple()
39 * l_int32 pixSeedfillGrayInvSimple()
40 *
41 * Gray seedfill variations
42 * PIX *pixSeedfillGrayBasin()
43 *
44 * Distance function (source: Luc Vincent)
45 * PIX *pixDistanceFunction()
46 *
47 * Seed spread (based on distance function)
48 * PIX *pixSeedspread()
49 *
50 * Local extrema:
51 * l_int32 pixLocalExtrema()
52 * static l_int32 pixQualifyLocalMinima()
53 * l_int32 pixSelectedLocalExtrema()
54 * PIX *pixFindEqualValues()
55 *
56 * Selection of minima in mask of connected components
57 * PTA *pixSelectMinInConnComp()
58 *
59 * Removal of seeded connected components from a mask
60 * PIX *pixRemoveSeededComponents()
61 *
62 *
63 * ITERATIVE RASTER-ORDER SEEDFILL
64 *
65 * The basic method in the Vincent seedfill (aka reconstruction)
66 * algorithm is simple. We describe here the situation for
67 * binary seedfill. Pixels are sampled in raster order in
68 * the seed image. If they are 4-connected to ON pixels
69 * either directly above or to the left, and are not masked
70 * out by the mask image, they are turned on (or remain on).
71 * (Ditto for 8-connected, except you need to check 3 pixels
72 * on the previous line as well as the pixel to the left
73 * on the current line. This is extra computational work
74 * for relatively little gain, so it is preferable
75 * in most situations to use the 4-connected version.)
76 * The algorithm proceeds from UR to LL of the image, and
77 * then reverses and sweeps up from LL to UR.
78 * These double sweeps are iterated until there is no change.
79 * At this point, the seed has entirely filled the region it
80 * is allowed to, as delimited by the mask image.
81 *
82 * The grayscale seedfill is a straightforward generalization
83 * of the binary seedfill, and is described in seedfillLowGray().
84 *
85 * For some applications, the filled seed will later be OR'd
86 * with the negative of the mask. This is used, for example,
87 * when you flood fill into a 4-connected region of OFF pixels
88 * and you want the result after those pixels are turned ON.
89 *
90 * Note carefully that the mask we use delineates which pixels
91 * are allowed to be ON as the seed is filled. We will call this
92 * a "filling mask". As the seed expands, it is repeatedly
93 * ANDed with the filling mask: s & fm. The process can equivalently
94 * be formulated using the inverse of the filling mask, which
95 * we will call a "blocking mask": bm = ~fm. As the seed
96 * expands, the blocking mask is repeatedly used to prevent
97 * the seed from expanding into the blocking mask. This is done
98 * by set subtracting the blocking mask from the expanded seed:
99 * s - bm. Set subtraction of the blocking mask is equivalent
100 * to ANDing with the inverse of the blocking mask: s & (~bm).
101 * But from the inverse relation between blocking and filling
102 * masks, this is equal to s & fm, which proves the equivalence.
103 *
104 * For efficiency, the pixels can be taken in larger units
105 * for processing, but still in raster order. It is natural
106 * to take them in 32-bit words. The outline of the work
107 * to be done for 4-cc (not including special cases for boundary
108 * words, such as the first line or the last word in each line)
109 * is as follows. Let the filling mask be m. The
110 * seed is to fill "under" the mask; i.e., limited by an AND
111 * with the mask. Let the current word be w, the word
112 * in the line above be wa, and the previous word in the
113 * current line be wp. Let t be a temporary word that
114 * is used in computation. Note that masking is performed by
115 * w & m. (If we had instead used a "blocking" mask, we
116 * would perform masking by the set subtraction operation,
117 * w - m, which is defined to be w & ~m.)
118 *
119 * The entire operation can be implemented with shifts,
120 * logical operations and tests. For each word in the seed image
121 * there are two steps. The first step is to OR the word with
122 * the word above and with the rightmost pixel in wp (call it "x").
123 * Because wp is shifted one pixel to its right, "x" is ORed
124 * to the leftmost pixel of w. We then clip to the ON pixels in
125 * the mask. The result is
126 * t <-- (w | wa | x000... ) & m
127 * We've now finished taking data from above and to the left.
128 * The second step is to allow filling to propagate horizontally
129 * in t, always making sure that it is properly masked at each
130 * step. So if filling can be done (i.e., t is neither all 0s
131 * nor all 1s), iteratively take:
132 * t <-- (t | (t >> 1) | (t << 1)) & m
133 * until t stops changing. Then write t back into w.
134 *
135 * Finally, the boundary conditions require we note that in doing
136 * the above steps:
137 * (a) The words in the first row have no wa
138 * (b) The first word in each row has no wp in that row
139 * (c) The last word in each row must be masked so that
140 * pixels don't propagate beyond the right edge of the
141 * actual image. (This is easily accomplished by
142 * setting the out-of-bound pixels in m to OFF.)
143 */
144
145 #include <stdio.h>
146 #include <stdlib.h>
147 #include "allheaders.h"
148
149 #ifndef NO_CONSOLE_IO
150 #define DEBUG_PRINT_ITERS 0
151 #endif /* ~NO_CONSOLE_IO */
152
153 /* Two-way (UL --> LR, LR --> UL) sweep iterations; typically need only 4 */
154 static const l_int32 MAX_ITERS = 40;
155
156 /* Static function */
157 static l_int32 pixQualifyLocalMinima(PIX *pixs, PIX *pixm, l_int32 maxval);
158
159
160 /*-----------------------------------------------------------------------*
161 * Vincent's Iterative Binary Seedfill method *
162 *-----------------------------------------------------------------------*/
163 /*!
164 * pixSeedfillBinary()
165 *
166 * Input: pixd (<optional>; this can be null, equal to pixs,
167 * or different from pixs; 1 bpp)
168 * pixs (1 bpp seed)
169 * pixm (1 bpp filling mask)
170 * connectivity (4 or 8)
171 * Return: pixd always
172 *
173 * Notes:
174 * (1) This is for binary seedfill (aka "binary reconstruction").
175 * (2) There are 3 cases:
176 * (a) pixd == null (make a new pixd)
177 * (b) pixd == pixs (in-place)
178 * (c) pixd != pixs
179 * (3) If you know the case, use these patterns for clarity:
180 * (a) pixd = pixSeedfillBinary(NULL, pixs, ...);
181 * (b) pixSeedfillBinary(pixs, pixs, ...);
182 * (c) pixSeedfillBinary(pixd, pixs, ...);
183 * (4) The resulting pixd contains the filled seed. For some
184 * applications you want to OR it with the inverse of
185 * the filling mask.
186 * (5) The input seed and mask images can be different sizes, but
187 * in typical use the difference, if any, would be only
188 * a few pixels in each direction. If the sizes differ,
189 * the clipping is handled by the low-level function
190 * seedfillBinaryLow().
191 */
192 PIX *
pixSeedfillBinary(PIX * pixd,PIX * pixs,PIX * pixm,l_int32 connectivity)193 pixSeedfillBinary(PIX *pixd,
194 PIX *pixs,
195 PIX *pixm,
196 l_int32 connectivity)
197 {
198 l_int32 i, boolval;
199 l_int32 hd, hm, wpld, wplm;
200 l_uint32 *datad, *datam;
201 PIX *pixt;
202
203 PROCNAME("pixSeedfillBinary");
204
205 if (!pixs || pixGetDepth(pixs) != 1)
206 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, pixd);
207 if (!pixm || pixGetDepth(pixm) != 1)
208 return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, pixd);
209 if (connectivity != 4 && connectivity != 8)
210 return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, pixd);
211
212 /* Prepare pixd as a copy of pixs if not identical */
213 if ((pixd = pixCopy(pixd, pixs)) == NULL)
214 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
215
216 /* pixt is used to test for completion */
217 if ((pixt = pixCreateTemplate(pixs)) == NULL)
218 return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
219
220 hd = pixGetHeight(pixd);
221 hm = pixGetHeight(pixm); /* included so seedfillBinaryLow() can clip */
222 datad = pixGetData(pixd);
223 datam = pixGetData(pixm);
224 wpld = pixGetWpl(pixd);
225 wplm = pixGetWpl(pixm);
226
227 pixSetPadBits(pixm, 0);
228
229 for (i = 0; i < MAX_ITERS; i++) {
230 pixCopy(pixt, pixd);
231 seedfillBinaryLow(datad, hd, wpld, datam, hm, wplm, connectivity);
232 pixEqual(pixd, pixt, &boolval);
233 if (boolval == 1) {
234 #if DEBUG_PRINT_ITERS
235 fprintf(stderr, "Binary seed fill converged: %d iters\n", i + 1);
236 #endif /* DEBUG_PRINT_ITERS */
237 break;
238 }
239 }
240
241 pixDestroy(&pixt);
242 return pixd;
243 }
244
245
246 /*!
247 * pixSeedfillBinaryRestricted()
248 *
249 * Input: pixd (<optional>; this can be null, equal to pixs,
250 * or different from pixs; 1 bpp)
251 * pixs (1 bpp seed)
252 * pixm (1 bpp filling mask)
253 * connectivity (4 or 8)
254 * xmax (max distance in x direction of fill into the mask)
255 * ymax (max distance in y direction of fill into the mask)
256 * Return: pixd always
257 *
258 * Notes:
259 * (1) See usage for pixSeedfillBinary(), which has unrestricted fill.
260 * In pixSeedfillBinary(), the filling distance is unrestricted
261 * and can be larger than pixs, depending on the topology of
262 * th mask.
263 * (2) There are occasions where it is useful not to permit the
264 * fill to go more than a certain distance into the mask.
265 * @xmax specifies the maximum horizontal distance allowed
266 * in the fill; @ymax does likewise in the vertical direction.
267 * (3) Operationally, the max "distance" allowed for the fill
268 * is a linear distance from the original seed, independent
269 * of the actual mask topology.
270 * (4) Another formulation of this problem, not implemented,
271 * would use the manhattan distance from the seed, as
272 * determined by a breadth-first search starting at the seed
273 * boundaries and working outward where the mask fg allows.
274 * How this might use the constraints of separate xmax and ymax
275 * is not clear.
276 */
277 PIX *
pixSeedfillBinaryRestricted(PIX * pixd,PIX * pixs,PIX * pixm,l_int32 connectivity,l_int32 xmax,l_int32 ymax)278 pixSeedfillBinaryRestricted(PIX *pixd,
279 PIX *pixs,
280 PIX *pixm,
281 l_int32 connectivity,
282 l_int32 xmax,
283 l_int32 ymax)
284 {
285 l_int32 w, h;
286 PIX *pixr, *pixt;
287
288 PROCNAME("pixSeedfillBinaryRestricted");
289
290 if (xmax <= 0 && ymax <= 0) /* no filling permitted */
291 return pixClone(pixs);
292 if (xmax < 0 || ymax < 0)
293 return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
294
295 /* Full fill from the seed into the mask. */
296 if ((pixt = pixSeedfillBinary(NULL, pixs, pixm, connectivity)) == NULL)
297 return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
298
299 /* Dilate the seed. This gives the maximal region where changes
300 * are permitted. Invert to get the region where pixs is
301 * not allowed to change. */
302 pixr = pixDilateCompBrick(NULL, pixs, 2 * xmax + 1, 2 * ymax + 1);
303 pixInvert(pixr, pixr);
304
305 /* Blank the region of pixt specified by the fg of pixr.
306 * This is not the final result, because it may have fg that
307 * is not accessible from the seed in the restricted distance.
308 * There we treat this as a new mask, and eliminate the
309 * bad fg regions by doing a second seedfill from the original seed. */
310 pixGetDimensions(pixs, &w, &h, NULL);
311 pixRasterop(pixt, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), pixr, 0, 0);
312
313 /* Fill again from the seed, into this new mask. */
314 pixd = pixSeedfillBinary(pixd, pixs, pixt, connectivity);
315
316 pixDestroy(&pixt);
317 pixDestroy(&pixr);
318 return pixd;
319 }
320
321
322 /*!
323 * pixHolesByFilling()
324 *
325 * Input: pixs (1 bpp)
326 * connectivity (4 or 8)
327 * Return: pixd (inverted image of all holes), or null on error
328 *
329 * Action:
330 * (1) Start with 1-pixel black border on otherwise white pixd
331 * (2) Use the inverted pixs as the filling mask to fill in
332 * all the pixels from the border to the pixs foreground
333 * (3) OR the result with pixs to have an image with all
334 * ON pixels except for the holes.
335 * (4) Invert the result to get the holes as foreground
336 *
337 * Notes:
338 * (1) To get 4-c.c. holes of the 8-c.c. as foreground, use
339 * 4-connected filling; to get 8-c.c. holes of the 4-c.c.
340 * as foreground, use 8-connected filling.
341 */
342 PIX *
pixHolesByFilling(PIX * pixs,l_int32 connectivity)343 pixHolesByFilling(PIX *pixs,
344 l_int32 connectivity)
345 {
346 PIX *pixsi, *pixd;
347
348 PROCNAME("pixHolesByFilling");
349
350 if (!pixs || pixGetDepth(pixs) != 1)
351 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
352 if (connectivity != 4 && connectivity != 8)
353 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
354
355 if ((pixd = pixCreateTemplate(pixs)) == NULL)
356 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
357 if ((pixsi = pixInvert(NULL, pixs)) == NULL)
358 return (PIX *)ERROR_PTR("pixsi not made", procName, NULL);
359
360 pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
361 pixSeedfillBinary(pixd, pixd, pixsi, connectivity);
362 pixOr(pixd, pixd, pixs);
363 pixInvert(pixd, pixd);
364 pixDestroy(&pixsi);
365
366 return pixd;
367 }
368
369
370 /*!
371 * pixFillClosedBorders()
372 *
373 * Input: pixs (1 bpp)
374 * filling connectivity (4 or 8)
375 * Return: pixd (all topologically outer closed borders are filled
376 * as connected comonents), or null on error
377 *
378 * Notes:
379 * (1) Start with 1-pixel black border on otherwise white pixd
380 * (2) Subtract input pixs to remove border pixels that were
381 * also on the closed border
382 * (3) Use the inverted pixs as the filling mask to fill in
383 * all the pixels from the outer border to the closed border
384 * on pixs
385 * (4) Invert the result to get the filled component, including
386 * the input border
387 * (5) If the borders are 4-c.c., use 8-c.c. filling, and v.v.
388 * (6) Closed borders within c.c. that represent holes, etc., are filled.
389 */
390 PIX *
pixFillClosedBorders(PIX * pixs,l_int32 connectivity)391 pixFillClosedBorders(PIX *pixs,
392 l_int32 connectivity)
393 {
394 PIX *pixsi, *pixd;
395
396 PROCNAME("pixFillClosedBorders");
397
398 if (!pixs || pixGetDepth(pixs) != 1)
399 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
400 if (connectivity != 4 && connectivity != 8)
401 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
402
403 if ((pixd = pixCreateTemplate(pixs)) == NULL)
404 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
405 pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
406 pixSubtract(pixd, pixd, pixs);
407 if ((pixsi = pixInvert(NULL, pixs)) == NULL)
408 return (PIX *)ERROR_PTR("pixsi not made", procName, NULL);
409
410 pixSeedfillBinary(pixd, pixd, pixsi, connectivity);
411 pixInvert(pixd, pixd);
412 pixDestroy(&pixsi);
413
414 return pixd;
415 }
416
417
418 /*!
419 * pixExtractBorderConnComps()
420 *
421 * Input: pixs (1 bpp)
422 * filling connectivity (4 or 8)
423 * Return: pixd (all pixels in the src that are in connected
424 * components touching the border), or null on error
425 */
426 PIX *
pixExtractBorderConnComps(PIX * pixs,l_int32 connectivity)427 pixExtractBorderConnComps(PIX *pixs,
428 l_int32 connectivity)
429 {
430 PIX *pixd;
431
432 PROCNAME("pixExtractBorderConnComps");
433
434 if (!pixs || pixGetDepth(pixs) != 1)
435 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
436 if (connectivity != 4 && connectivity != 8)
437 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
438
439 /* Start with 1 pixel wide black border as seed in pixd */
440 if ((pixd = pixCreateTemplate(pixs)) == NULL)
441 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
442 pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
443
444 /* Fill in pixd from the seed, using pixs as the filling mask.
445 * This fills all components from pixs that are touching the border. */
446 pixSeedfillBinary(pixd, pixd, pixs, connectivity);
447
448 return pixd;
449 }
450
451
452 /*!
453 * pixRemoveBorderConnComps()
454 *
455 * Input: pixs (1 bpp)
456 * filling connectivity (4 or 8)
457 * Return: pixd (all pixels in the src that are not touching the
458 * border) or null on error
459 */
460 PIX *
pixRemoveBorderConnComps(PIX * pixs,l_int32 connectivity)461 pixRemoveBorderConnComps(PIX *pixs,
462 l_int32 connectivity)
463 {
464 PIX *pixd;
465
466 PROCNAME("pixRemoveBorderConnComps");
467
468 if (!pixs || pixGetDepth(pixs) != 1)
469 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
470 if (connectivity != 4 && connectivity != 8)
471 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
472
473 /* Fill from a 1 pixel wide seed at the border into all components
474 * in pixs (the filling mask) that are touching the border */
475 pixd = pixExtractBorderConnComps(pixs, connectivity);
476
477 /* Save in pixd only those components in pixs not touching the border */
478 pixXor(pixd, pixd, pixs);
479
480 return pixd;
481 }
482
483
484 /*-----------------------------------------------------------------------*
485 * Hole-filling of components to bounding rectangle *
486 *-----------------------------------------------------------------------*/
487 /*!
488 * pixFillHolesToBoundingRect()
489 *
490 * Input: pixs (1 bpp)
491 * minsize (min number of pixels in the hole)
492 * maxhfract (max hole area as fraction of fg pixels in the cc)
493 * minfgfract (min fg area as fraction of bounding rectangle)
494 * Return: pixd (pixs, with some holes possibly filled and some c.c.
495 * possibly expanded to their bounding rects),
496 * or null on error
497 *
498 * Notes:
499 * (1) This does not fill holes that are smaller in area than 'minsize'.
500 * (2) This does not fill holes with an area larger than
501 * 'maxhfract' times the fg area of the c.c.
502 * (3) This does not expand the fg of the c.c. to bounding rect if
503 * the fg area is less than 'minfgfract' times the area of the
504 * bounding rect.
505 * (4) The decisions are made as follows:
506 * - Decide if we are filling the holes; if so, when using
507 * the fg area, include the filled holes.
508 * - Decide based on the fg area if we are filling to a bounding rect.
509 * If so, do it.
510 * If not, fill the holes if the condition is satisfied.
511 * (5) The choice of minsize depends on the resolution.
512 * (6) For solidifying image mask regions on printed materials,
513 * which tend to be rectangular, values for maxhfract
514 * and minfgfract around 0.5 are reasonable.
515 */
516 PIX *
pixFillHolesToBoundingRect(PIX * pixs,l_int32 minsize,l_float32 maxhfract,l_float32 minfgfract)517 pixFillHolesToBoundingRect(PIX *pixs,
518 l_int32 minsize,
519 l_float32 maxhfract,
520 l_float32 minfgfract)
521 {
522 l_int32 i, x, y, w, h, n, nfg, nh, ntot, area;
523 l_int32 *tab;
524 l_float32 hfract; /* measured hole fraction */
525 l_float32 fgfract; /* measured fg fraction */
526 BOXA *boxa;
527 PIX *pixd, *pixfg, *pixh;
528 PIXA *pixa;
529
530 PROCNAME("pixFillHolesToBoundingRect");
531
532 if (!pixs || pixGetDepth(pixs) != 1)
533 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
534
535 pixd = pixCopy(NULL, pixs);
536 boxa = pixConnComp(pixd, &pixa, 8);
537 n = boxaGetCount(boxa);
538 tab = makePixelSumTab8();
539 for (i = 0; i < n; i++) {
540 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
541 area = w * h;
542 if (area < minsize)
543 continue;
544 pixfg = pixaGetPix(pixa, i, L_COPY);
545 pixh = pixHolesByFilling(pixfg, 4); /* holes only */
546 pixCountPixels(pixfg, &nfg, tab);
547 pixCountPixels(pixh, &nh, tab);
548 hfract = (l_float32)nh / (l_float32)nfg;
549 ntot = nfg;
550 if (hfract <= maxhfract) /* we will fill the holes (at least) */
551 ntot = nfg + nh;
552 fgfract = (l_float32)ntot / (l_float32)area;
553 if (fgfract >= minfgfract) { /* fill to bounding rect */
554 pixSetAll(pixfg);
555 pixRasterop(pixd, x, y, w, h, PIX_SRC, pixfg, 0, 0);
556 }
557 else if (hfract <= maxhfract) { /* fill just the holes */
558 pixRasterop(pixd, x, y, w, h, PIX_DST | PIX_SRC , pixh, 0, 0);
559 }
560 pixDestroy(&pixfg);
561 pixDestroy(&pixh);
562 }
563 boxaDestroy(&boxa);
564 pixaDestroy(&pixa);
565 FREE(tab);
566
567 return pixd;
568 }
569
570
571 /*-----------------------------------------------------------------------*
572 * Vincent's hybrid Grayscale Seedfill method *
573 *-----------------------------------------------------------------------*/
574 /*!
575 * pixSeedfillGray()
576 *
577 * Input: pixs (8 bpp seed; filled in place)
578 * pixm (8 bpp filling mask)
579 * connectivity (4 or 8)
580 * Return: 0 if OK, 1 on error
581 *
582 * Notes:
583 * (1) This is an in-place filling operation on the seed, pixs,
584 * where the clipping mask is always above or at the level
585 * of the seed as it is filled.
586 * (2) For details of the operation, see the description in
587 * seedfillGrayLow() and the code there.
588 * (3) As an example of use, see the description in pixHDome().
589 * There, the seed is an image where each pixel is a fixed
590 * amount smaller than the corresponding mask pixel.
591 * (4) Reference paper :
592 * L. Vincent, Morphological grayscale reconstruction in image
593 * analysis: applications and efficient algorithms, IEEE Transactions
594 * on Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
595 */
596 l_int32
pixSeedfillGray(PIX * pixs,PIX * pixm,l_int32 connectivity)597 pixSeedfillGray(PIX *pixs,
598 PIX *pixm,
599 l_int32 connectivity)
600 {
601 l_int32 h, w, wpls, wplm;
602 l_uint32 *datas, *datam;
603
604 PROCNAME("pixSeedfillGray");
605
606 if (!pixs || pixGetDepth(pixs) != 8)
607 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
608 if (!pixm || pixGetDepth(pixm) != 8)
609 return ERROR_INT("pixm not defined or not 8 bpp", procName, 1);
610 if (connectivity != 4 && connectivity != 8)
611 return ERROR_INT("connectivity not in {4,8}", procName, 1);
612
613 /* Make sure the sizes of seed and mask images are the same */
614 if (pixSizesEqual(pixs, pixm) == 0)
615 return ERROR_INT("pixs and pixm sizes differ", procName, 1);
616
617 datas = pixGetData(pixs);
618 datam = pixGetData(pixm);
619 wpls = pixGetWpl(pixs);
620 wplm = pixGetWpl(pixm);
621 pixGetDimensions(pixs, &w, &h, NULL);
622 seedfillGrayLow(datas, w, h, wpls, datam, wplm, connectivity);
623
624 return 0;
625 }
626
627
628 /*!
629 * pixSeedfillGrayInv()
630 *
631 * Input: pixs (8 bpp seed; filled in place)
632 * pixm (8 bpp filling mask)
633 * connectivity (4 or 8)
634 * Return: 0 if OK, 1 on error
635 *
636 * Notes:
637 * (1) This is an in-place filling operation on the seed, pixs,
638 * where the clipping mask is always below or at the level
639 * of the seed as it is filled. Think of filling up a basin
640 * to a particular level, given by the maximum seed value
641 * in the basin. Outside the filled region, the mask
642 * is above the filling level.
643 * (2) Contrast this with pixSeedfillGray(), where the clipping mask
644 * is always above or at the level of the fill. An example
645 * of its use is the hdome fill, where the seed is an image
646 * where each pixel is a fixed amount smaller than the
647 * corresponding mask pixel.
648 * (3) The basin fill, pixSeedfillGrayBasin(), is a special case
649 * where the seed pixel values are generated from the mask,
650 * and where the implementation uses pixSeedfillGray() by
651 * inverting both the seed and mask.
652 */
653 l_int32
pixSeedfillGrayInv(PIX * pixs,PIX * pixm,l_int32 connectivity)654 pixSeedfillGrayInv(PIX *pixs,
655 PIX *pixm,
656 l_int32 connectivity)
657 {
658 l_int32 h, w, wpls, wplm;
659 l_uint32 *datas, *datam;
660
661 PROCNAME("pixSeedfillGrayInv");
662
663 if (!pixs || pixGetDepth(pixs) != 8)
664 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
665 if (!pixm || pixGetDepth(pixm) != 8)
666 return ERROR_INT("pixm not defined or not 8 bpp", procName, 1);
667 if (connectivity != 4 && connectivity != 8)
668 return ERROR_INT("connectivity not in {4,8}", procName, 1);
669
670 /* Make sure the sizes of seed and mask images are the same */
671 if (pixSizesEqual(pixs, pixm) == 0)
672 return ERROR_INT("pixs and pixm sizes differ", procName, 1);
673
674 datas = pixGetData(pixs);
675 datam = pixGetData(pixm);
676 wpls = pixGetWpl(pixs);
677 wplm = pixGetWpl(pixm);
678 pixGetDimensions(pixs, &w, &h, NULL);
679 seedfillGrayInvLow(datas, w, h, wpls, datam, wplm, connectivity);
680
681 return 0;
682 }
683
684 /*-----------------------------------------------------------------------*
685 * Vincent's Iterative Grayscale Seedfill method *
686 *-----------------------------------------------------------------------*/
687 /*!
688 * pixSeedfillGraySimple()
689 *
690 * Input: pixs (8 bpp seed; filled in place)
691 * pixm (8 bpp filling mask)
692 * connectivity (4 or 8)
693 * Return: 0 if OK, 1 on error
694 *
695 * Notes:
696 * (1) This is an in-place filling operation on the seed, pixs,
697 * where the clipping mask is always above or at the level
698 * of the seed as it is filled.
699 * (2) For details of the operation, see the description in
700 * seedfillGrayLowSimple() and the code there.
701 * (3) As an example of use, see the description in pixHDome().
702 * There, the seed is an image where each pixel is a fixed
703 * amount smaller than the corresponding mask pixel.
704 * (4) Reference paper :
705 * L. Vincent, Morphological grayscale reconstruction in image
706 * analysis: applications and efficient algorithms, IEEE Transactions
707 * on Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
708 */
709 l_int32
pixSeedfillGraySimple(PIX * pixs,PIX * pixm,l_int32 connectivity)710 pixSeedfillGraySimple(PIX *pixs,
711 PIX *pixm,
712 l_int32 connectivity)
713 {
714 l_int32 i, h, w, wpls, wplm, boolval;
715 l_uint32 *datas, *datam;
716 PIX *pixt;
717
718 PROCNAME("pixSeedfillGraySimple");
719
720 if (!pixs || pixGetDepth(pixs) != 8)
721 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
722 if (!pixm || pixGetDepth(pixm) != 8)
723 return ERROR_INT("pixm not defined or not 8 bpp", procName, 1);
724 if (connectivity != 4 && connectivity != 8)
725 return ERROR_INT("connectivity not in {4,8}", procName, 1);
726
727 /* Make sure the sizes of seed and mask images are the same */
728 if (pixSizesEqual(pixs, pixm) == 0)
729 return ERROR_INT("pixs and pixm sizes differ", procName, 1);
730
731 /* This is used to test for completion */
732 if ((pixt = pixCreateTemplate(pixs)) == NULL)
733 return ERROR_INT("pixt not made", procName, 1);
734
735 datas = pixGetData(pixs);
736 datam = pixGetData(pixm);
737 wpls = pixGetWpl(pixs);
738 wplm = pixGetWpl(pixm);
739 pixGetDimensions(pixs, &w, &h, NULL);
740 for (i = 0; i < MAX_ITERS; i++) {
741 pixCopy(pixt, pixs);
742 seedfillGrayLowSimple(datas, w, h, wpls, datam, wplm, connectivity);
743 pixEqual(pixs, pixt, &boolval);
744 if (boolval == 1) {
745 #if DEBUG_PRINT_ITERS
746 L_INFO_INT("Gray seed fill converged: %d iters", procName, i + 1);
747 #endif /* DEBUG_PRINT_ITERS */
748 break;
749 }
750 }
751
752 pixDestroy(&pixt);
753 return 0;
754 }
755
756
757 /*!
758 * pixSeedfillGrayInvSimple()
759 *
760 * Input: pixs (8 bpp seed; filled in place)
761 * pixm (8 bpp filling mask)
762 * connectivity (4 or 8)
763 * Return: 0 if OK, 1 on error
764 *
765 * Notes:
766 * (1) This is an in-place filling operation on the seed, pixs,
767 * where the clipping mask is always below or at the level
768 * of the seed as it is filled. Think of filling up a basin
769 * to a particular level, given by the maximum seed value
770 * in the basin. Outside the filled region, the mask
771 * is above the filling level.
772 * (2) Contrast this with pixSeedfillGraySimple(), where the clipping mask
773 * is always above or at the level of the fill. An example
774 * of its use is the hdome fill, where the seed is an image
775 * where each pixel is a fixed amount smaller than the
776 * corresponding mask pixel.
777 */
778 l_int32
pixSeedfillGrayInvSimple(PIX * pixs,PIX * pixm,l_int32 connectivity)779 pixSeedfillGrayInvSimple(PIX *pixs,
780 PIX *pixm,
781 l_int32 connectivity)
782 {
783 l_int32 i, h, w, wpls, wplm, boolval;
784 l_uint32 *datas, *datam;
785 PIX *pixt;
786
787 PROCNAME("pixSeedfillGrayInvSimple");
788
789 if (!pixs || pixGetDepth(pixs) != 8)
790 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
791 if (!pixm || pixGetDepth(pixm) != 8)
792 return ERROR_INT("pixm not defined or not 8 bpp", procName, 1);
793 if (connectivity != 4 && connectivity != 8)
794 return ERROR_INT("connectivity not in {4,8}", procName, 1);
795
796 /* Make sure the sizes of seed and mask images are the same */
797 if (pixSizesEqual(pixs, pixm) == 0)
798 return ERROR_INT("pixs and pixm sizes differ", procName, 1);
799
800 /* This is used to test for completion */
801 if ((pixt = pixCreateTemplate(pixs)) == NULL)
802 return ERROR_INT("pixt not made", procName, 1);
803
804 datas = pixGetData(pixs);
805 datam = pixGetData(pixm);
806 wpls = pixGetWpl(pixs);
807 wplm = pixGetWpl(pixm);
808 pixGetDimensions(pixs, &w, &h, NULL);
809 for (i = 0; i < MAX_ITERS; i++) {
810 pixCopy(pixt, pixs);
811 seedfillGrayInvLowSimple(datas, w, h, wpls, datam, wplm, connectivity);
812 pixEqual(pixs, pixt, &boolval);
813 if (boolval == 1) {
814 #if DEBUG_PRINT_ITERS
815 L_INFO_INT("Gray seed fill converged: %d iters", procName, i + 1);
816 #endif /* DEBUG_PRINT_ITERS */
817 break;
818 }
819 }
820
821 pixDestroy(&pixt);
822 return 0;
823 }
824
825
826 /*-----------------------------------------------------------------------*
827 * Gray seedfill variations *
828 *-----------------------------------------------------------------------*/
829 /*!
830 * pixSeedfillGrayBasin()
831 *
832 * Input: pixb (binary mask giving seed locations)
833 * pixm (8 bpp basin-type filling mask)
834 * delta (amount of seed value above mask)
835 * connectivity (4 or 8)
836 * Return: pixd (filled seed) if OK, null on error
837 *
838 * Notes:
839 * (1) This fills from a seed within basins defined by a filling mask.
840 * The seed value(s) are greater than the corresponding
841 * filling mask value, and the result has the bottoms of
842 * the basins raised by the initial seed value.
843 * (2) The seed has value 255 except where pixb has fg (1), which
844 * are the seed 'locations'. At the seed locations, the seed
845 * value is the corresponding value of the mask pixel in pixm
846 * plus @delta. If @delta == 0, we return a copy of pixm.
847 * (3) The actual filling is done using the standard grayscale filling
848 * operation on the inverse of the mask and using the inverse
849 * of the seed image. After filling, we return the inverse of
850 * the filled seed.
851 * (4) As an example of use: pixm can describe a grayscale image
852 * of text, where the (dark) text pixels are basins of
853 * low values; pixb can identify the local minima in pixm (say, at
854 * the bottom of the basins); and delta is the amount that we wish
855 * to raise (lighten) the basins. We construct the seed
856 * (a.k.a marker) image from pixb, pixm and @delta.
857 */
858 PIX *
pixSeedfillGrayBasin(PIX * pixb,PIX * pixm,l_int32 delta,l_int32 connectivity)859 pixSeedfillGrayBasin(PIX *pixb,
860 PIX *pixm,
861 l_int32 delta,
862 l_int32 connectivity)
863 {
864 PIX *pixbi, *pixmi, *pixsd;
865
866 PROCNAME("pixSeedfillGrayBasin");
867
868 if (!pixb || pixGetDepth(pixb) != 1)
869 return (PIX *)ERROR_PTR("pixb undefined or not 1 bpp", procName, NULL);
870 if (!pixm || pixGetDepth(pixm) != 8)
871 return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", procName, NULL);
872 if (connectivity != 4 && connectivity != 8)
873 return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, NULL);
874
875 if (delta <= 0) {
876 L_WARNING("delta <= 0; returning a copy of pixm", procName);
877 return pixCopy(NULL, pixm);
878 }
879
880 /* Add delta to every pixel in pixm */
881 pixsd = pixCopy(NULL, pixm);
882 pixAddConstantGray(pixsd, delta);
883
884 /* Prepare the seed. Write 255 in all pixels of
885 * ([pixm] + delta) where pixb is 0. */
886 pixbi = pixInvert(NULL, pixb);
887 pixSetMasked(pixsd, pixbi, 255);
888
889 /* Fill the inverse seed, using the inverse clipping mask */
890 pixmi = pixInvert(NULL, pixm);
891 pixInvert(pixsd, pixsd);
892 pixSeedfillGray(pixsd, pixmi, connectivity);
893
894 /* Re-invert the filled seed */
895 pixInvert(pixsd, pixsd);
896
897 pixDestroy(&pixbi);
898 pixDestroy(&pixmi);
899 return pixsd;
900 }
901
902
903 /*-----------------------------------------------------------------------*
904 * Vincent's Distance Function method *
905 *-----------------------------------------------------------------------*/
906 /*!
907 * pixDistanceFunction()
908 *
909 * Input: pixs (1 bpp source)
910 * connectivity (4 or 8)
911 * outdepth (8 or 16 bits for pixd)
912 * boundcond (L_BOUNDARY_BG, L_BOUNDARY_FG)
913 * Return: pixd, or null on error
914 *
915 * Notes:
916 * (1) This computes the distance of each pixel from the nearest
917 * background pixel. All bg pixels therefore have a distance of 0,
918 * and the fg pixel distances increase linearly from 1 at the
919 * boundary. It can also be used to compute the distance of
920 * each pixel from the nearest fg pixel, by inverting the input
921 * image before calling this function. Then all fg pixels have
922 * a distance 0 and the bg pixel distances increase linearly
923 * from 1 at the boundary.
924 * (2) The algorithm, described in Leptonica on the page on seed
925 * filling and connected components, is due to Luc Vincent.
926 * In brief, we generate an 8 or 16 bpp image, initialized
927 * with the fg pixels of the input pix set to 1 and the
928 * 1-boundary pixels (i.e., the boundary pixels of width 1 on
929 * the four sides set as either:
930 * * L_BOUNDARY_BG: 0
931 * * L_BOUNDARY_FG: max
932 * where max = 0xff for 8 bpp and 0xffff for 16 bpp.
933 * Then do raster/anti-raster sweeps over all pixels interior
934 * to the 1-boundary, where the value of each new pixel is
935 * taken to be 1 more than the minimum of the previously-seen
936 * connected pixels (using either 4 or 8 connectivity).
937 * Finally, set the 1-boundary pixels using the mirrored method;
938 * this removes the max values there.
939 * (3) Using L_BOUNDARY_BG clamps the distance to 0 at the
940 * boundary. Using L_BOUNDARY_FG allows the distance
941 * at the image boundary to "float".
942 * (4) For 4-connected, one could initialize only the left and top
943 * 1-boundary pixels, and go all the way to the right
944 * and bottom; then coming back reset left and top. But we
945 * instead use a method that works for both 4- and 8-connected.
946 */
947 PIX *
pixDistanceFunction(PIX * pixs,l_int32 connectivity,l_int32 outdepth,l_int32 boundcond)948 pixDistanceFunction(PIX *pixs,
949 l_int32 connectivity,
950 l_int32 outdepth,
951 l_int32 boundcond)
952 {
953 l_int32 w, h, wpld;
954 l_uint32 *datad;
955 PIX *pixd;
956
957 PROCNAME("pixDistanceFunction");
958
959 if (!pixs || pixGetDepth(pixs) != 1)
960 return (PIX *)ERROR_PTR("!pixs or pixs not 1 bpp", procName, NULL);
961 if (connectivity != 4 && connectivity != 8)
962 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
963 if (outdepth != 8 && outdepth != 16)
964 return (PIX *)ERROR_PTR("outdepth not 8 or 16 bpp", procName, NULL);
965 if (boundcond != L_BOUNDARY_BG && boundcond != L_BOUNDARY_FG)
966 return (PIX *)ERROR_PTR("invalid boundcond", procName, NULL);
967
968 pixGetDimensions(pixs, &w, &h, NULL);
969 if ((pixd = pixCreate(w, h, outdepth)) == NULL)
970 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
971 datad = pixGetData(pixd);
972 wpld = pixGetWpl(pixd);
973
974 /* Initialize the fg pixels to 1 and the bg pixels to 0 */
975 pixSetMasked(pixd, pixs, 1);
976
977 if (boundcond == L_BOUNDARY_BG)
978 distanceFunctionLow(datad, w, h, outdepth, wpld, connectivity);
979 else { /* L_BOUNDARY_FG: set boundary pixels to max val */
980 pixRasterop(pixd, 0, 0, w, 1, PIX_SET, NULL, 0, 0); /* top */
981 pixRasterop(pixd, 0, h - 1, w, 1, PIX_SET, NULL, 0, 0); /* bot */
982 pixRasterop(pixd, 0, 0, 1, h, PIX_SET, NULL, 0, 0); /* left */
983 pixRasterop(pixd, w - 1, 0, 1, h, PIX_SET, NULL, 0, 0); /* right */
984
985 distanceFunctionLow(datad, w, h, outdepth, wpld, connectivity);
986
987 /* Set each boundary pixel equal to the pixel next to it */
988 pixSetMirroredBorder(pixd, 1, 1, 1, 1);
989 }
990
991 return pixd;
992 }
993
994
995 /*-----------------------------------------------------------------------*
996 * Seed spread (based on distance function) *
997 *-----------------------------------------------------------------------*/
998 /*!
999 * pixSeedspread()
1000 *
1001 * Input: pixs (8 bpp source)
1002 * connectivity (4 or 8)
1003 * Return: pixd, or null on error
1004 *
1005 * Notes:
1006 * (1) The raster/anti-raster method for implementing this filling
1007 * operation was suggested by Ray Smith.
1008 * (2) This takes an arbitrary set of nonzero pixels in pixs, which
1009 * can be sparse, and spreads (extrapolates) the values to
1010 * fill all the pixels in pixd with the nonzero value it is
1011 * closest to in pixs. This is similar (though not completely
1012 * equivalent) to doing a Voronoi tiling of the image, with a
1013 * tile surrounding each pixel that has a nonzero value.
1014 * All pixels within a tile are then closer to its "central"
1015 * pixel than to any others. Then assign the value of the
1016 * "central" pixel to each pixel in the tile.
1017 * (3) This is implemented by computing a distance function in parallel
1018 * with the fill. The distance function uses free boundary
1019 * conditions (assumed maxval outside), and it controls the
1020 * propagation of the pixels in pixd away from the nonzero
1021 * (seed) values. This is done in 2 traversals (raster/antiraster).
1022 * In the raster direction, whenever the distance function
1023 * is nonzero, the spread pixel takes on the value of its
1024 * predecessor that has the minimum distance value. In the
1025 * antiraster direction, whenever the distance function is nonzero
1026 * and its value is replaced by a smaller value, the spread
1027 * pixel takes the value of the predecessor with the minimum
1028 * distance value.
1029 * (4) At boundaries where a pixel is equidistant from two
1030 * nearest nonzero (seed) pixels, the decision of which value
1031 * to use is arbitrary (greedy in search for minimum distance).
1032 * This can give rise to strange-looking results, particularly
1033 * for 4-connectivity where the L1 distance is computed from
1034 * steps in N,S,E and W directions (no diagonals).
1035 */
1036 PIX *
pixSeedspread(PIX * pixs,l_int32 connectivity)1037 pixSeedspread(PIX *pixs,
1038 l_int32 connectivity)
1039 {
1040 l_int32 w, h, wplt, wplg;
1041 l_uint32 *datat, *datag;
1042 PIX *pixm, *pixt, *pixg, *pixd;
1043
1044 PROCNAME("pixSeedspread");
1045
1046 if (!pixs || pixGetDepth(pixs) != 8)
1047 return (PIX *)ERROR_PTR("!pixs or pixs not 8 bpp", procName, NULL);
1048 if (connectivity != 4 && connectivity != 8)
1049 return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL);
1050
1051 /* Add a 4 byte border to pixs. This simplifies the computation. */
1052 pixg = pixAddBorder(pixs, 4, 0);
1053 pixGetDimensions(pixg, &w, &h, NULL);
1054
1055 /* Initialize distance function pixt. Threshold pixs to get
1056 * a 0 at the seed points where the pixs pixel is nonzero, and
1057 * a 1 at all points that need to be filled. Use this as a
1058 * mask to set a 1 in pixt at all non-seed points. Also, set all
1059 * pixt pixels in an interior boundary of width 1 to the
1060 * maximum value. For debugging, to view the distance function,
1061 * use pixConvert16To8(pixt, 0) on small images. */
1062 pixm = pixThresholdToBinary(pixg, 1);
1063 pixt = pixCreate(w, h, 16);
1064 pixSetMasked(pixt, pixm, 1);
1065 pixRasterop(pixt, 0, 0, w, 1, PIX_SET, NULL, 0, 0); /* top */
1066 pixRasterop(pixt, 0, h - 1, w, 1, PIX_SET, NULL, 0, 0); /* bot */
1067 pixRasterop(pixt, 0, 0, 1, h, PIX_SET, NULL, 0, 0); /* left */
1068 pixRasterop(pixt, w - 1, 0, 1, h, PIX_SET, NULL, 0, 0); /* right */
1069 datat = pixGetData(pixt);
1070 wplt = pixGetWpl(pixt);
1071
1072 /* Do the interpolation and remove the border. */
1073 datag = pixGetData(pixg);
1074 wplg = pixGetWpl(pixg);
1075 seedspreadLow(datag, w, h, wplg, datat, wplt, connectivity);
1076 pixd = pixRemoveBorder(pixg, 4);
1077
1078 pixDestroy(&pixm);
1079 pixDestroy(&pixg);
1080 pixDestroy(&pixt);
1081 return pixd;
1082 }
1083
1084
1085
1086 /*-----------------------------------------------------------------------*
1087 * Local extrema *
1088 *-----------------------------------------------------------------------*/
1089 /*!
1090 * pixLocalExtrema()
1091 *
1092 * Input: pixs (8 bpp)
1093 * maxmin (max allowed for the min in a 3x3 neighborhood;
1094 * use 0 for default which is to have no upper bound)
1095 * minmax (min allowed for the max in a 3x3 neighborhood;
1096 * use 0 for default which is to have no lower bound)
1097 * &ppixmin (<optional return> mask of local minima)
1098 * &ppixmax (<optional return> mask of local maxima)
1099 * Return: 0 if OK, 1 on error
1100 *
1101 * Notes:
1102 * (1) This gives the actual local minima and maxima.
1103 * A local minimum is a pixel whose surrounding pixels all
1104 * have values at least as large, and likewise for a local
1105 * maximum. For the local minima, @maxmin is the upper
1106 * bound for the value of pixs. Likewise, for the local maxima,
1107 * @minmax is the lower bound for the value of pixs.
1108 * (2) The minima are found by starting with the erosion-and-equality
1109 * approach of pixSelectedLocalExtrema. This is followed
1110 * by a qualification step, where each c.c. in the resulting
1111 * minimum mask is extracted, the pixels bordering it are
1112 * located, and they are queried. If all of those pixels
1113 * are larger than the value of that minimum, it is a true
1114 * minimum and its c.c. is saved; otherwise the c.c. is
1115 * rejected. Note that if a bordering pixel has the
1116 * same value as the minimum, it must then have a
1117 * neighbor that is smaller, so the component is not a
1118 * true minimum.
1119 * (3) The maxima are found by inverting the image and looking
1120 * for the minima there.
1121 * (4) The generated masks can be used as markers for
1122 * further operations.
1123 */
1124 l_int32
pixLocalExtrema(PIX * pixs,l_int32 maxmin,l_int32 minmax,PIX ** ppixmin,PIX ** ppixmax)1125 pixLocalExtrema(PIX *pixs,
1126 l_int32 maxmin,
1127 l_int32 minmax,
1128 PIX **ppixmin,
1129 PIX **ppixmax)
1130 {
1131 PIX *pixmin, *pixmax, *pixt1, *pixt2;
1132
1133 PROCNAME("pixLocalExtrema");
1134
1135 if (!pixs || pixGetDepth(pixs) != 8)
1136 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
1137 if (!ppixmin && !ppixmax)
1138 return ERROR_INT("neither &pixmin, &pixmax are defined", procName, 1);
1139 if (maxmin <= 0) maxmin = 254;
1140 if (minmax <= 0) minmax = 1;
1141
1142 if (ppixmin) {
1143 pixt1 = pixErodeGray(pixs, 3, 3);
1144 pixmin = pixFindEqualValues(pixs, pixt1);
1145 pixDestroy(&pixt1);
1146 pixQualifyLocalMinima(pixs, pixmin, maxmin);
1147 *ppixmin = pixmin;
1148 }
1149
1150 if (ppixmax) {
1151 pixt1 = pixInvert(NULL, pixs);
1152 pixt2 = pixErodeGray(pixt1, 3, 3);
1153 pixmax = pixFindEqualValues(pixt1, pixt2);
1154 pixDestroy(&pixt2);
1155 pixQualifyLocalMinima(pixt1, pixmax, 255 - minmax);
1156 *ppixmax = pixmax;
1157 pixDestroy(&pixt1);
1158 }
1159
1160 return 0;
1161 }
1162
1163
1164 /*!
1165 * pixQualifyLocalMinima()
1166 *
1167 * Input: pixs (8 bpp)
1168 * pixm (1 bpp mask of values equal to min in 3x3 neighborhood)
1169 * maxval (max allowed for the min in a 3x3 neighborhood;
1170 * use 0 for default which is to have no upper bound)
1171 * Return: 0 if OK, 1 on error
1172 *
1173 * Notes:
1174 * (1) This function acts in-place to remove all c.c. in pixm
1175 * that are not true local minima. See notes in pixLocalExtrema().
1176 * (2) The maximum allowed value for each local minimum can be
1177 * bounded with @maxval. Use 0 for default, which is to have
1178 * no upper bound (equivalent to maxval == 254).
1179 */
1180 static l_int32
pixQualifyLocalMinima(PIX * pixs,PIX * pixm,l_int32 maxval)1181 pixQualifyLocalMinima(PIX *pixs,
1182 PIX *pixm,
1183 l_int32 maxval)
1184 {
1185 l_int32 n, i, j, k, x, y, w, h, xc, yc, wc, hc, xon, yon;
1186 l_int32 vals, wpls, wplc, ismin;
1187 l_uint32 val;
1188 l_uint32 *datas, *datac, *lines, *linec;
1189 BOXA *boxa;
1190 PIX *pixt1, *pixt2, *pixc;
1191 PIXA *pixa;
1192
1193 PROCNAME("pixQualifyLocalMinima");
1194
1195 if (!pixs || pixGetDepth(pixs) != 8)
1196 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
1197 if (!pixm || pixGetDepth(pixm) != 1)
1198 return ERROR_INT("pixm not defined or not 1 bpp", procName, 1);
1199 if (maxval <= 0) maxval = 254;
1200
1201 pixGetDimensions(pixs, &w, &h, NULL);
1202 datas = pixGetData(pixs);
1203 wpls = pixGetWpl(pixs);
1204 boxa = pixConnComp(pixm, &pixa, 8);
1205 n = pixaGetCount(pixa);
1206 for (k = 0; k < n; k++) {
1207 boxaGetBoxGeometry(boxa, k, &xc, &yc, &wc, &hc);
1208 pixt1 = pixaGetPix(pixa, k, L_COPY);
1209 pixt2 = pixAddBorder(pixt1, 1, 0);
1210 pixc = pixDilateBrick(NULL, pixt2, 3, 3);
1211 pixXor(pixc, pixc, pixt2); /* exterior boundary pixels */
1212 datac = pixGetData(pixc);
1213 wplc = pixGetWpl(pixc);
1214 nextOnPixelInRaster(pixt1, 0, 0, &xon, &yon);
1215 pixGetPixel(pixs, xc + xon, yc + yon, &val);
1216 if (val > maxval) { /* too large; erase */
1217 pixRasterop(pixm, xc, yc, wc, hc, PIX_XOR, pixt1, 0, 0);
1218 pixDestroy(&pixt1);
1219 pixDestroy(&pixt2);
1220 pixDestroy(&pixc);
1221 continue;
1222 }
1223 ismin = TRUE;
1224 for (i = 0, y = yc - 1; i < hc + 2 && y >= 0 && y < h; i++, y++) {
1225 lines = datas + y * wpls;
1226 linec = datac + i * wplc;
1227 for (j = 0, x = xc - 1; j < wc + 2 && x >= 0 && x < w; j++, x++) {
1228 if (GET_DATA_BIT(linec, j)) {
1229 vals = GET_DATA_BYTE(lines, x);
1230 if (vals <= val) { /* not a minimum! */
1231 ismin = FALSE;
1232 break;
1233 }
1234 }
1235 }
1236 if (!ismin)
1237 break;
1238 }
1239 if (!ismin) /* erase it */
1240 pixRasterop(pixm, xc, yc, wc, hc, PIX_XOR, pixt1, 0, 0);
1241 pixDestroy(&pixt1);
1242 pixDestroy(&pixt2);
1243 pixDestroy(&pixc);
1244 }
1245
1246 boxaDestroy(&boxa);
1247 pixaDestroy(&pixa);
1248 return 0;
1249 }
1250
1251
1252 /*!
1253 * pixSelectedLocalExtrema()
1254 *
1255 * Input: pixs (8 bpp)
1256 * mindist (-1 for keeping all pixels; >= 0 specifies distance)
1257 * &ppixmin (<return> mask of local minima)
1258 * &ppixmax (<return> mask of local maxima)
1259 * Return: 0 if OK, 1 on error
1260 *
1261 * Notes:
1262 * (1) This selects those local 3x3 minima that are at least a
1263 * specified distance from the nearest local 3x3 maxima, and v.v.
1264 * for the selected set of local 3x3 maxima.
1265 * The local 3x3 minima is the set of pixels whose value equals
1266 * the value after a 3x3 brick erosion, and the local 3x3 maxima
1267 * is the set of pixels whose value equals the value after
1268 * a 3x3 brick dilation.
1269 * (2) mindist is the minimum distance allowed between
1270 * local 3x3 minima and local 3x3 maxima, in an 8-connected sense.
1271 * mindist == 1 keeps all pixels found in step 1.
1272 * mindist == 0 removes all pixels from each mask that are
1273 * both a local 3x3 minimum and a local 3x3 maximum.
1274 * mindist == 1 removes any local 3x3 minimum pixel that touches a
1275 * local 3x3 maximum pixel, and likewise for the local maxima.
1276 * To make the decision, visualize each local 3x3 minimum pixel
1277 * as being surrounded by a square of size (2 * mindist + 1)
1278 * on each side, such that no local 3x3 maximum pixel is within
1279 * that square; and v.v.
1280 * (3) The generated masks can be used as markers for further operations.
1281 */
1282 l_int32
pixSelectedLocalExtrema(PIX * pixs,l_int32 mindist,PIX ** ppixmin,PIX ** ppixmax)1283 pixSelectedLocalExtrema(PIX *pixs,
1284 l_int32 mindist,
1285 PIX **ppixmin,
1286 PIX **ppixmax)
1287 {
1288 PIX *pixmin, *pixmax, *pixt, *pixtmin, *pixtmax;
1289
1290 PROCNAME("pixSelectedLocalExtrema");
1291
1292 if (!pixs || pixGetDepth(pixs) != 8)
1293 return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
1294 if (!ppixmin || !ppixmax)
1295 return ERROR_INT("&pixmin and &pixmax not both defined", procName, 1);
1296
1297 pixt = pixErodeGray(pixs, 3, 3);
1298 pixmin = pixFindEqualValues(pixs, pixt);
1299 pixDestroy(&pixt);
1300 pixt = pixDilateGray(pixs, 3, 3);
1301 pixmax = pixFindEqualValues(pixs, pixt);
1302 pixDestroy(&pixt);
1303
1304 /* Remove all points that are within the prescribed distance
1305 * from each other. */
1306 if (mindist < 0) { /* remove no points */
1307 *ppixmin = pixmin;
1308 *ppixmax = pixmax;
1309 } else if (mindist == 0) { /* remove points belonging to both sets */
1310 pixt = pixAnd(NULL, pixmin, pixmax);
1311 *ppixmin = pixSubtract(pixmin, pixmin, pixt);
1312 *ppixmax = pixSubtract(pixmax, pixmax, pixt);
1313 pixDestroy(&pixt);
1314 } else {
1315 pixtmin = pixDilateBrick(NULL, pixmin,
1316 2 * mindist + 1, 2 * mindist + 1);
1317 pixtmax = pixDilateBrick(NULL, pixmax,
1318 2 * mindist + 1, 2 * mindist + 1);
1319 *ppixmin = pixSubtract(pixmin, pixmin, pixtmax);
1320 *ppixmax = pixSubtract(pixmax, pixmax, pixtmin);
1321 pixDestroy(&pixtmin);
1322 pixDestroy(&pixtmax);
1323 }
1324 return 0;
1325 }
1326
1327
1328 /*!
1329 * pixFindEqualValues()
1330 *
1331 * Input: pixs1 (8 bpp)
1332 * pixs2 (8 bpp)
1333 * Return: pixd (1 bpp mask), or null on error
1334 *
1335 * Notes:
1336 * (1) The two images are aligned at the UL corner, and the returned
1337 * image has ON pixels where the pixels in pixs1 and pixs2
1338 * have equal values.
1339 */
1340 PIX *
pixFindEqualValues(PIX * pixs1,PIX * pixs2)1341 pixFindEqualValues(PIX *pixs1,
1342 PIX *pixs2)
1343 {
1344 l_int32 w1, h1, w2, h2, w, h;
1345 l_int32 i, j, val1, val2, wpls1, wpls2, wpld;
1346 l_uint32 *datas1, *datas2, *datad, *lines1, *lines2, *lined;
1347 PIX *pixd;
1348
1349 PROCNAME("pixFindEqualValues");
1350
1351 if (!pixs1 || pixGetDepth(pixs1) != 8)
1352 return (PIX *)ERROR_PTR("pixs1 undefined or not 8 bpp", procName, NULL);
1353 if (!pixs2 || pixGetDepth(pixs2) != 8)
1354 return (PIX *)ERROR_PTR("pixs2 undefined or not 8 bpp", procName, NULL);
1355 pixGetDimensions(pixs1, &w1, &h1, NULL);
1356 pixGetDimensions(pixs2, &w2, &h2, NULL);
1357 w = L_MIN(w1, w2);
1358 h = L_MIN(h1, h2);
1359 pixd = pixCreate(w, h, 1);
1360 datas1 = pixGetData(pixs1);
1361 datas2 = pixGetData(pixs2);
1362 datad = pixGetData(pixd);
1363 wpls1 = pixGetWpl(pixs1);
1364 wpls2 = pixGetWpl(pixs2);
1365 wpld = pixGetWpl(pixd);
1366
1367 for (i = 0; i < h; i++) {
1368 lines1 = datas1 + i * wpls1;
1369 lines2 = datas2 + i * wpls2;
1370 lined = datad + i * wpld;
1371 for (j = 0; j < w; j++) {
1372 val1 = GET_DATA_BYTE(lines1, j);
1373 val2 = GET_DATA_BYTE(lines2, j);
1374 if (val1 == val2)
1375 SET_DATA_BIT(lined, j);
1376 }
1377 }
1378
1379 return pixd;
1380 }
1381
1382
1383 /*-----------------------------------------------------------------------*
1384 * Selection of minima in mask connected components *
1385 *-----------------------------------------------------------------------*/
1386 /*!
1387 * pixSelectMinInConnComp()
1388 *
1389 * Input: pixs (8 bpp)
1390 * pixm (1 bpp)
1391 * &nav (<optional return> numa of minima values)
1392 * Return: pta (of min pixels), or null on error
1393 *
1394 * Notes:
1395 * (1) For each 8 connected component in pixm, this finds
1396 * a pixel in pixs that has the lowest value, and saves
1397 * it in a Pta. If several pixels in pixs have the same
1398 * minimum value, it picks the first one found.
1399 * (2) For a mask pixm of true local minima, all pixels in each
1400 * connected component have the same value in pixs, so it is
1401 * fastest to select one of them using a special seedfill
1402 * operation. Not yet implemented.
1403 */
1404 PTA *
pixSelectMinInConnComp(PIX * pixs,PIX * pixm,NUMA ** pnav)1405 pixSelectMinInConnComp(PIX *pixs,
1406 PIX *pixm,
1407 NUMA **pnav)
1408 {
1409 l_int32 ws, hs, wm, hm, w, h, bx, by, bw, bh, i, j, c, n;
1410 l_int32 xs, ys, minx, miny, wpls, wplt, val, minval;
1411 l_uint32 *datas, *datat, *lines, *linet;
1412 BOXA *boxa;
1413 NUMA *nav;
1414 PIX *pixt;
1415 PIXA *pixa;
1416 PTA *pta;
1417
1418 PROCNAME("pixSelectMinInConnComp");
1419
1420 if (!pixs || pixGetDepth(pixs) != 8)
1421 return (PTA *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
1422 if (!pixm || pixGetDepth(pixm) != 1)
1423 return (PTA *)ERROR_PTR("pixm undefined or not 1 bpp", procName, NULL);
1424 pixGetDimensions(pixs, &ws, &hs, NULL);
1425 pixGetDimensions(pixm, &wm, &hm, NULL);
1426 w = L_MIN(ws, wm);
1427 h = L_MIN(hs, hm);
1428
1429 boxa = pixConnComp(pixm, &pixa, 8);
1430 n = boxaGetCount(boxa);
1431 pta = ptaCreate(n);
1432 nav = numaCreate(n);
1433 datas = pixGetData(pixs);
1434 wpls = pixGetWpl(pixs);
1435 for (c = 0; c < n; c++) {
1436 pixt = pixaGetPix(pixa, c, L_CLONE);
1437 boxaGetBoxGeometry(boxa, c, &bx, &by, &bw, &bh);
1438 if (bw == 1 && bh == 1) {
1439 ptaAddPt(pta, bx, by);
1440 numaAddNumber(nav, GET_DATA_BYTE(datas + by * wpls, bx));
1441 pixDestroy(&pixt);
1442 continue;
1443 }
1444 datat = pixGetData(pixt);
1445 wplt = pixGetWpl(pixt);
1446 minx = miny = 1000000;
1447 minval = 256;
1448 for (i = 0; i < bh; i++) {
1449 ys = by + i;
1450 lines = datas + ys * wpls;
1451 linet = datat + i * wplt;
1452 for (j = 0; j < bw; j++) {
1453 xs = bx + j;
1454 if (GET_DATA_BIT(linet, j)) {
1455 val = GET_DATA_BYTE(lines, xs);
1456 if (val < minval) {
1457 minval = val;
1458 minx = xs;
1459 miny = ys;
1460 }
1461 }
1462 }
1463 }
1464 ptaAddPt(pta, minx, miny);
1465 numaAddNumber(nav, GET_DATA_BYTE(datas + miny * wpls, minx));
1466 pixDestroy(&pixt);
1467 }
1468
1469 boxaDestroy(&boxa);
1470 pixaDestroy(&pixa);
1471 if (pnav)
1472 *pnav = nav;
1473 else
1474 numaDestroy(&nav);
1475 return pta;
1476 }
1477
1478
1479 /*-----------------------------------------------------------------------*
1480 * Removal of seeded connected components from a mask *
1481 *-----------------------------------------------------------------------*/
1482 /*!
1483 * pixRemoveSeededComponents()
1484 *
1485 * Input: pixd (<optional>; this can be null or equal to pixm; 1 bpp)
1486 * pixs (1 bpp seed)
1487 * pixm (1 bpp filling mask)
1488 * connectivity (4 or 8)
1489 * bordersize (amount of border clearing)
1490 * Return: pixd, or null on error
1491 *
1492 * Notes:
1493 * (1) This removes each component in pixm for which there is
1494 * at least one seed in pixs. If pixd == NULL, this returns
1495 * the result in a new pixd. Otherwise, it is an in-place
1496 * operation on pixm. In no situation is pixs altered,
1497 * because we do the filling with a copy of pixs.
1498 * (2) If bordersize > 0, it also clears all pixels within a
1499 * distance @bordersize of the edge of pixd. This is here
1500 * because pixLocalExtrema() typically finds local minima
1501 * at the border. Use @bordersize >= 2 to remove these.
1502 */
1503 PIX *
pixRemoveSeededComponents(PIX * pixd,PIX * pixs,PIX * pixm,l_int32 connectivity,l_int32 bordersize)1504 pixRemoveSeededComponents(PIX *pixd,
1505 PIX *pixs,
1506 PIX *pixm,
1507 l_int32 connectivity,
1508 l_int32 bordersize)
1509 {
1510 PIX *pixt;
1511
1512 PROCNAME("pixRemoveSeededComponents");
1513
1514 if (!pixs || pixGetDepth(pixs) != 1)
1515 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, pixd);
1516 if (!pixm || pixGetDepth(pixm) != 1)
1517 return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", procName, pixd);
1518 if (pixd && pixd != pixm)
1519 return (PIX *)ERROR_PTR("operation not inplace", procName, pixd);
1520
1521 pixt = pixCopy(NULL, pixs);
1522 pixSeedfillBinary(pixt, pixt, pixm, connectivity);
1523 pixd = pixXor(pixd, pixm, pixt);
1524 if (bordersize > 0)
1525 pixSetOrClearBorder(pixd, bordersize, bordersize, bordersize,
1526 bordersize, PIX_CLR);
1527 pixDestroy(&pixt);
1528 return pixd;
1529 }
1530