Coverage Report

Created: 2024-06-18 06:05

/src/leptonica/src/seedfill.c
Line
Count
Source (jump to first uncovered line)
1
/*====================================================================*
2
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
3
 -
4
 -  Redistribution and use in source and binary forms, with or without
5
 -  modification, are permitted provided that the following conditions
6
 -  are met:
7
 -  1. Redistributions of source code must retain the above copyright
8
 -     notice, this list of conditions and the following disclaimer.
9
 -  2. Redistributions in binary form must reproduce the above
10
 -     copyright notice, this list of conditions and the following
11
 -     disclaimer in the documentation and/or other materials
12
 -     provided with the distribution.
13
 -
14
 -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
 -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
 -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
 -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18
 -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19
 -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
 -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21
 -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22
 -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23
 -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 *====================================================================*/
26
27
/*!
28
 * \file seedfill.c
29
 * <pre>
30
 *
31
 *      Binary seedfill (source: Luc Vincent)
32
 *               PIX         *pixSeedfillBinary()
33
 *               PIX         *pixSeedfillBinaryRestricted()
34
 *               static void  seedfillBinaryLow()
35
 *
36
 *      Applications of binary seedfill to find and fill holes,
37
 *      remove c.c. touching the border and fill bg from border:
38
 *               PIX         *pixHolesByFilling()
39
 *               PIX         *pixFillClosedBorders()
40
 *               PIX         *pixExtractBorderConnComps()
41
 *               PIX         *pixRemoveBorderConnComps()
42
 *               PIX         *pixFillBgFromBorder()
43
 *
44
 *      Hole-filling of components to bounding rectangle
45
 *               PIX         *pixFillHolesToBoundingRect()
46
 *
47
 *      Gray seedfill (source: Luc Vincent:fast-hybrid-grayscale-reconstruction)
48
 *               l_int32      pixSeedfillGray()
49
 *               l_int32      pixSeedfillGrayInv()
50
 *               static void  seedfillGrayLow()
51
 *               static void  seedfillGrayInvLow()
52
53
 *
54
 *      Gray seedfill (source: Luc Vincent: sequential-reconstruction algorithm)
55
 *               l_int32      pixSeedfillGraySimple()
56
 *               l_int32      pixSeedfillGrayInvSimple()
57
 *               static void  seedfillGrayLowSimple()
58
 *               static void  seedfillGrayInvLowSimple()
59
 *
60
 *      Gray seedfill variations
61
 *               PIX         *pixSeedfillGrayBasin()
62
 *
63
 *      Distance function (source: Luc Vincent)
64
 *               PIX         *pixDistanceFunction()
65
 *               static void  distanceFunctionLow()
66
 *
67
 *      Seed spread (based on distance function)
68
 *               PIX         *pixSeedspread()
69
 *               static void  seedspreadLow()
70
 *
71
 *      Local extrema:
72
 *               l_int32      pixLocalExtrema()
73
 *            static l_int32  pixQualifyLocalMinima()
74
 *               l_int32      pixSelectedLocalExtrema()
75
 *               PIX         *pixFindEqualValues()
76
 *
77
 *      Selection of minima in mask of connected components
78
 *               PTA         *pixSelectMinInConnComp()
79
 *
80
 *      Removal of seeded connected components from a mask
81
 *               PIX         *pixRemoveSeededComponents()
82
 *
83
 *
84
 *           ITERATIVE RASTER-ORDER SEEDFILL
85
 *
86
 *      The basic method in the Vincent seedfill (aka reconstruction)
87
 *      algorithm is simple.  We describe here the situation for
88
 *      binary seedfill.  Pixels are sampled in raster order in
89
 *      the seed image.  If they are 4-connected to ON pixels
90
 *      either directly above or to the left, and are not masked
91
 *      out by the mask image, they are turned on (or remain on).
92
 *      (Ditto for 8-connected, except you need to check 3 pixels
93
 *      on the previous line as well as the pixel to the left
94
 *      on the current line.  This is extra computational work
95
 *      for relatively little gain, so it is preferable
96
 *      in most situations to use the 4-connected version.)
97
 *      The algorithm proceeds from UR to LL of the image, and
98
 *      then reverses and sweeps up from LL to UR.
99
 *      These double sweeps are iterated until there is no change.
100
 *      At this point, the seed has entirely filled the region it
101
 *      is allowed to, as delimited by the mask image.
102
 *
103
 *      The grayscale seedfill is a straightforward generalization
104
 *      of the binary seedfill, and is described in seedfillLowGray().
105
 *
106
 *      For some applications, the filled seed will later be OR'd
107
 *      with the negative of the mask.   This is used, for example,
108
 *      when you flood fill into a 4-connected region of OFF pixels
109
 *      and you want the result after those pixels are turned ON.
110
 *
111
 *      Note carefully that the mask we use delineates which pixels
112
 *      are allowed to be ON as the seed is filled.  We will call this
113
 *      a "filling mask".  As the seed expands, it is repeatedly
114
 *      ANDed with the filling mask: s & fm.  The process can equivalently
115
 *      be formulated using the inverse of the filling mask, which
116
 *      we will call a "blocking mask": bm = ~fm.   As the seed
117
 *      expands, the blocking mask is repeatedly used to prevent
118
 *      the seed from expanding into the blocking mask.  This is done
119
 *      by set subtracting the blocking mask from the expanded seed:
120
 *      s - bm.  Set subtraction of the blocking mask is equivalent
121
 *      to ANDing with the inverse of the blocking mask: s & (~bm).
122
 *      But from the inverse relation between blocking and filling
123
 *      masks, this is equal to s & fm, which proves the equivalence.
124
 *
125
 *      For efficiency, the pixels can be taken in larger units
126
 *      for processing, but still in raster order.  It is natural
127
 *      to take them in 32-bit words.  The outline of the work
128
 *      to be done for 4-cc (not including special cases for boundary
129
 *      words, such as the first line or the last word in each line)
130
 *      is as follows.  Let the filling mask be m.  The
131
 *      seed is to fill "under" the mask; i.e., limited by an AND
132
 *      with the mask.  Let the current word be w, the word
133
 *      in the line above be wa, and the previous word in the
134
 *      current line be wp.   Let t be a temporary word that
135
 *      is used in computation.  Note that masking is performed by
136
 *      w & m.  (If we had instead used a "blocking" mask, we
137
 *      would perform masking by the set subtraction operation,
138
 *      w - m, which is defined to be w & ~m.)
139
 *
140
 *      The entire operation can be implemented with shifts,
141
 *      logical operations and tests.  For each word in the seed image
142
 *      there are two steps.  The first step is to OR the word with
143
 *      the word above and with the rightmost pixel in wp (call it "x").
144
 *      Because wp is shifted one pixel to its right, "x" is ORed
145
 *      to the leftmost pixel of w.  We then clip to the ON pixels in
146
 *      the mask.  The result is
147
 *               t  <--  (w | wa | x000... ) & m
148
 *      We've now finished taking data from above and to the left.
149
 *      The second step is to allow filling to propagate horizontally
150
 *      in t, always making sure that it is properly masked at each
151
 *      step.  So if filling can be done (i.e., t is neither all 0s
152
 *      nor all 1s), iteratively take:
153
 *           t  <--  (t | (t >> 1) | (t << 1)) & m
154
 *      until t stops changing.  Then write t back into w.
155
 *
156
 *      Finally, the boundary conditions require we note that in doing
157
 *      the above steps:
158
 *          (a) The words in the first row have no wa
159
 *          (b) The first word in each row has no wp in that row
160
 *          (c) The last word in each row must be masked so that
161
 *              pixels don't propagate beyond the right edge of the
162
 *              actual image.  (This is easily accomplished by
163
 *              setting the out-of-bound pixels in m to OFF.)
164
 * </pre>
165
 */
166
167
#ifdef HAVE_CONFIG_H
168
#include <config_auto.h>
169
#endif  /* HAVE_CONFIG_H */
170
171
#include <math.h>
172
#include "allheaders.h"
173
174
struct L_Pixel
175
{
176
    l_int32    x;
177
    l_int32    y;
178
};
179
typedef struct L_Pixel  L_PIXEL;
180
181
static void seedfillBinaryLow(l_uint32 *datas, l_int32 hs, l_int32 wpls,
182
                              l_uint32 *datam, l_int32 hm, l_int32 wplm,
183
                              l_int32 connectivity);
184
static void seedfillGrayLow(l_uint32 *datas, l_int32 w, l_int32 h,
185
                            l_int32 wpls, l_uint32 *datam, l_int32 wplm,
186
                            l_int32 connectivity);
187
static void seedfillGrayInvLow(l_uint32 *datas, l_int32 w, l_int32 h,
188
                               l_int32 wpls, l_uint32 *datam, l_int32 wplm,
189
                               l_int32 connectivity);
190
static void seedfillGrayLowSimple(l_uint32 *datas, l_int32 w, l_int32 h,
191
                                  l_int32 wpls, l_uint32 *datam, l_int32 wplm,
192
                                  l_int32 connectivity);
193
static void seedfillGrayInvLowSimple(l_uint32 *datas, l_int32 w, l_int32 h,
194
                                     l_int32 wpls, l_uint32 *datam,
195
                                     l_int32 wplm, l_int32 connectivity);
196
static void distanceFunctionLow(l_uint32 *datad, l_int32 w, l_int32 h,
197
                                l_int32 d, l_int32 wpld, l_int32 connectivity);
198
static void seedspreadLow(l_uint32 *datad, l_int32 w, l_int32 h, l_int32 wpld,
199
                          l_uint32 *datat, l_int32 wplt, l_int32 connectivity);
200
201
202
static l_int32 pixQualifyLocalMinima(PIX *pixs, PIX *pixm, l_int32 maxval);
203
204
#ifndef  NO_CONSOLE_IO
205
#define   DEBUG_PRINT_ITERS    0
206
#endif  /* ~NO_CONSOLE_IO */
207
208
  /* Two-way (UL --> LR, LR --> UL) sweep iterations; typically need only 4 */
209
static const l_int32  MaxIters = 40;
210
211
212
/*-----------------------------------------------------------------------*
213
 *              Vincent's Iterative Binary Seedfill method               *
214
 *-----------------------------------------------------------------------*/
215
/*!
216
 * \brief   pixSeedfillBinary()
217
 *
218
 * \param[in]    pixd          [optional]; can be null, equal to pixs,
219
 *                             or different from pixs; 1 bpp
220
 * \param[in]    pixs          1 bpp seed
221
 * \param[in]    pixm          1 bpp filling mask
222
 * \param[in]    connectivity  4 or 8
223
 * \return  pixd always
224
 *
225
 * <pre>
226
 * Notes:
227
 *      (1) This is for binary seedfill (aka "binary reconstruction").
228
 *      (2) There are 3 cases:
229
 *            (a) pixd == null (make a new pixd)
230
 *            (b) pixd == pixs (in-place)
231
 *            (c) pixd != pixs
232
 *      (3) If you know the case, use these patterns for clarity:
233
 *            (a) pixd = pixSeedfillBinary(NULL, pixs, ...);
234
 *            (b) pixSeedfillBinary(pixs, pixs, ...);
235
 *            (c) pixSeedfillBinary(pixd, pixs, ...);
236
 *      (4) The resulting pixd contains the filled seed.  For some
237
 *          applications you want to OR it with the inverse of
238
 *          the filling mask.
239
 *      (5) The input seed and mask images can be different sizes, but
240
 *          in typical use the difference, if any, would be only
241
 *          a few pixels in each direction.  If the sizes differ,
242
 *          the clipping is handled by the low-level function
243
 *          seedfillBinaryLow().
244
 * </pre>
245
 */
246
PIX *
247
pixSeedfillBinary(PIX     *pixd,
248
                  PIX     *pixs,
249
                  PIX     *pixm,
250
                  l_int32  connectivity)
251
0
{
252
0
l_int32    i, boolval;
253
0
l_int32    hd, hm, wpld, wplm;
254
0
l_uint32  *datad, *datam;
255
0
PIX       *pixt;
256
257
0
    if (!pixs || pixGetDepth(pixs) != 1)
258
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, pixd);
259
0
    if (!pixm || pixGetDepth(pixm) != 1)
260
0
        return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, pixd);
261
0
    if (connectivity != 4 && connectivity != 8)
262
0
        return (PIX *)ERROR_PTR("connectivity not in {4,8}", __func__, pixd);
263
264
        /* Prepare pixd as a copy of pixs if not identical */
265
0
    if ((pixd = pixCopy(pixd, pixs)) == NULL)
266
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
267
0
    pixSetPadBits(pixd, 0);  /* be safe: */
268
0
    pixSetPadBits(pixm, 0);  /* avoid using uninitialized memory */
269
270
        /* pixt is used to test for completion */
271
0
    if ((pixt = pixCreateTemplate(pixs)) == NULL)
272
0
        return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
273
274
0
    hd = pixGetHeight(pixd);
275
0
    hm = pixGetHeight(pixm);  /* included so seedfillBinaryLow() can clip */
276
0
    datad = pixGetData(pixd);
277
0
    datam = pixGetData(pixm);
278
0
    wpld = pixGetWpl(pixd);
279
0
    wplm = pixGetWpl(pixm);
280
281
282
0
    for (i = 0; i < MaxIters; i++) {
283
0
        pixCopy(pixt, pixd);
284
0
        seedfillBinaryLow(datad, hd, wpld, datam, hm, wplm, connectivity);
285
0
        pixEqual(pixd, pixt, &boolval);
286
0
        if (boolval == 1) {
287
#if DEBUG_PRINT_ITERS
288
            lept_stderr("Binary seed fill converged: %d iters\n", i + 1);
289
#endif  /* DEBUG_PRINT_ITERS */
290
0
            break;
291
0
        }
292
0
    }
293
294
0
    pixDestroy(&pixt);
295
0
    return pixd;
296
0
}
297
298
299
/*!
300
 * \brief   pixSeedfillBinaryRestricted()
301
 *
302
 * \param[in]    pixd          [optional]; can be null, equal to pixs,
303
 *                             or different from pixs; 1 bpp
304
 * \param[in]    pixs          1 bpp seed
305
 * \param[in]    pixm          1 bpp filling mask
306
 * \param[in]    connectivity  4 or 8
307
 * \param[in]    xmax          max distance in x direction of fill into mask
308
 * \param[in]    ymax          max distance in y direction of fill into mask
309
 * \return  pixd always
310
 *
311
 * <pre>
312
 * Notes:
313
 *      (1) See usage for pixSeedfillBinary(), which has unrestricted fill.
314
 *          In pixSeedfillBinary(), the filling distance is unrestricted
315
 *          and can be larger than pixs, depending on the topology of
316
 *          th mask.
317
 *      (2) There are occasions where it is useful not to permit the
318
 *          fill to go more than a certain distance into the mask.
319
 *          %xmax specifies the maximum horizontal distance allowed
320
 *          in the fill; %ymax does likewise in the vertical direction.
321
 *      (3) Operationally, the max "distance" allowed for the fill
322
 *          is a linear distance from the original seed, independent
323
 *          of the actual mask topology.
324
 *      (4) Another formulation of this problem, not implemented,
325
 *          would use the manhattan distance from the seed, as
326
 *          determined by a breadth-first search starting at the seed
327
 *          boundaries and working outward where the mask fg allows.
328
 *          How this might use the constraints of separate xmax and ymax
329
 *          is not clear.
330
 * </pre>
331
 */
332
PIX *
333
pixSeedfillBinaryRestricted(PIX     *pixd,
334
                            PIX     *pixs,
335
                            PIX     *pixm,
336
                            l_int32  connectivity,
337
                            l_int32  xmax,
338
                            l_int32  ymax)
339
0
{
340
0
l_int32  w, h;
341
0
PIX     *pix1, *pix2;
342
343
0
    if (!pixs || pixGetDepth(pixs) != 1)
344
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, pixd);
345
0
    if (!pixm || pixGetDepth(pixm) != 1)
346
0
        return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, pixd);
347
0
    if (connectivity != 4 && connectivity != 8)
348
0
        return (PIX *)ERROR_PTR("connectivity not in {4,8}", __func__, pixd);
349
0
    if (xmax == 0 && ymax == 0)  /* no filling permitted */
350
0
        return pixClone(pixs);
351
0
    if (xmax < 0 || ymax < 0) {
352
0
        L_ERROR("xmax and ymax must be non-negative", __func__);
353
0
        return pixClone(pixs);
354
0
    }
355
356
        /* Full fill from the seed into the mask. */
357
0
    if ((pix1 = pixSeedfillBinary(NULL, pixs, pixm, connectivity)) == NULL)
358
0
        return (PIX *)ERROR_PTR("pix1 not made", __func__, pixd);
359
360
        /* Dilate the seed.  This gives the maximal region where changes
361
         * are permitted.  Invert to get the region where pixs is
362
         * not allowed to change.  */
363
0
    pix2 = pixDilateCompBrick(NULL, pixs, 2 * xmax + 1, 2 * ymax + 1);
364
0
    pixInvert(pix2, pix2);
365
366
        /* Blank the region of pix1 specified by the fg of pix2.
367
         * This is not yet the final result, because it may have fg pixels
368
         * that are not accessible from the seed in the restricted distance.
369
         * For example, such pixels may be connected to the original seed,
370
         * but through a path that goes outside the permitted region. */
371
0
    pixGetDimensions(pixs, &w, &h, NULL);
372
0
    pixRasterop(pix1, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0);
373
374
        /* To get the accessible pixels in the restricted region, do
375
         * a second seedfill from the original seed, using pix1 as
376
         * a mask.  The result, in pixd, will not have any bad fg
377
         * pixels that were in pix1. */
378
0
    pixd = pixSeedfillBinary(pixd, pixs, pix1, connectivity);
379
380
0
    pixDestroy(&pix1);
381
0
    pixDestroy(&pix2);
382
0
    return pixd;
383
0
}
384
385
386
/*!
387
 * \brief   seedfillBinaryLow()
388
 *
389
 *  Notes:
390
 *      (1) This is an in-place fill, where the seed image is
391
 *          filled, clipping to the filling mask, in one full
392
 *          cycle of UL -> LR and LR -> UL raster scans.
393
 *      (2) Assume the mask is a filling mask, not a blocking mask.
394
 *      (3) Assume that the RHS pad bits of the mask
395
 *          are properly set to 0.
396
 *      (4) Clip to the smallest dimensions to avoid invalid reads.
397
 */
398
static void
399
seedfillBinaryLow(l_uint32  *datas,
400
                  l_int32    hs,
401
                  l_int32    wpls,
402
                  l_uint32  *datam,
403
                  l_int32    hm,
404
                  l_int32    wplm,
405
                  l_int32    connectivity)
406
0
{
407
0
l_int32    i, j, h, wpl;
408
0
l_uint32   word, mask;
409
0
l_uint32   wordabove, wordleft, wordbelow, wordright;
410
0
l_uint32   wordprev;  /* test against this in previous iteration */
411
0
l_uint32  *lines, *linem;
412
413
0
    h = L_MIN(hs, hm);
414
0
    wpl = L_MIN(wpls, wplm);
415
416
0
    switch (connectivity)
417
0
    {
418
0
    case 4:
419
            /* UL --> LR scan */
420
0
        for (i = 0; i < h; i++) {
421
0
            lines = datas + i * wpls;
422
0
            linem = datam + i * wplm;
423
0
            for (j = 0; j < wpl; j++) {
424
0
                word = *(lines + j);
425
0
                mask = *(linem + j);
426
427
                    /* OR from word above and from word to left; mask */
428
0
                if (i > 0) {
429
0
                    wordabove = *(lines - wpls + j);
430
0
                    word |= wordabove;
431
0
                }
432
0
                if (j > 0) {
433
0
                    wordleft = *(lines + j - 1);
434
0
                    word |= wordleft << 31;
435
0
                }
436
0
                word &= mask;
437
438
                    /* No need to fill horizontally? */
439
0
                if (!word || !(~word)) {
440
0
                    *(lines + j) = word;
441
0
                    continue;
442
0
                }
443
444
0
                while (1) {
445
0
                    wordprev = word;
446
0
                    word = (word | (word >> 1) | (word << 1)) & mask;
447
0
                    if ((word ^ wordprev) == 0) {
448
0
                        *(lines + j) = word;
449
0
                        break;
450
0
                    }
451
0
                }
452
0
            }
453
0
        }
454
455
            /* LR --> UL scan */
456
0
        for (i = h - 1; i >= 0; i--) {
457
0
            lines = datas + i * wpls;
458
0
            linem = datam + i * wplm;
459
0
            for (j = wpl - 1; j >= 0; j--) {
460
0
                word = *(lines + j);
461
0
                mask = *(linem + j);
462
463
                    /* OR from word below and from word to right; mask */
464
0
                if (i < h - 1) {
465
0
                    wordbelow = *(lines + wpls + j);
466
0
                    word |= wordbelow;
467
0
                }
468
0
                if (j < wpl - 1) {
469
0
                    wordright = *(lines + j + 1);
470
0
                    word |= wordright >> 31;
471
0
                }
472
0
                word &= mask;
473
474
                    /* No need to fill horizontally? */
475
0
                if (!word || !(~word)) {
476
0
                    *(lines + j) = word;
477
0
                    continue;
478
0
                }
479
480
0
                while (1) {
481
0
                    wordprev = word;
482
0
                    word = (word | (word >> 1) | (word << 1)) & mask;
483
0
                    if ((word ^ wordprev) == 0) {
484
0
                        *(lines + j) = word;
485
0
                        break;
486
0
                    }
487
0
                }
488
0
            }
489
0
        }
490
0
        break;
491
492
0
    case 8:
493
            /* UL --> LR scan */
494
0
        for (i = 0; i < h; i++) {
495
0
            lines = datas + i * wpls;
496
0
            linem = datam + i * wplm;
497
0
            for (j = 0; j < wpl; j++) {
498
0
                word = *(lines + j);
499
0
                mask = *(linem + j);
500
501
                    /* OR from words above and from word to left; mask */
502
0
                if (i > 0) {
503
0
                    wordabove = *(lines - wpls + j);
504
0
                    word |= (wordabove | (wordabove << 1) | (wordabove >> 1));
505
0
                    if (j > 0)
506
0
                        word |= (*(lines - wpls + j - 1)) << 31;
507
0
                    if (j < wpl - 1)
508
0
                        word |= (*(lines - wpls + j + 1)) >> 31;
509
0
                }
510
0
                if (j > 0) {
511
0
                    wordleft = *(lines + j - 1);
512
0
                    word |= wordleft << 31;
513
0
                }
514
0
                word &= mask;
515
516
                    /* No need to fill horizontally? */
517
0
                if (!word || !(~word)) {
518
0
                    *(lines + j) = word;
519
0
                    continue;
520
0
                }
521
522
0
                while (1) {
523
0
                    wordprev = word;
524
0
                    word = (word | (word >> 1) | (word << 1)) & mask;
525
0
                    if ((word ^ wordprev) == 0) {
526
0
                        *(lines + j) = word;
527
0
                        break;
528
0
                    }
529
0
                }
530
0
            }
531
0
        }
532
533
            /* LR --> UL scan */
534
0
        for (i = h - 1; i >= 0; i--) {
535
0
            lines = datas + i * wpls;
536
0
            linem = datam + i * wplm;
537
0
            for (j = wpl - 1; j >= 0; j--) {
538
0
                word = *(lines + j);
539
0
                mask = *(linem + j);
540
541
                    /* OR from words below and from word to right; mask */
542
0
                if (i < h - 1) {
543
0
                    wordbelow = *(lines + wpls + j);
544
0
                    word |= (wordbelow | (wordbelow << 1) | (wordbelow >> 1));
545
0
                    if (j > 0)
546
0
                        word |= (*(lines + wpls + j - 1)) << 31;
547
0
                    if (j < wpl - 1)
548
0
                        word |= (*(lines + wpls + j + 1)) >> 31;
549
0
                }
550
0
                if (j < wpl - 1) {
551
0
                    wordright = *(lines + j + 1);
552
0
                    word |= wordright >> 31;
553
0
                }
554
0
                word &= mask;
555
556
                    /* No need to fill horizontally? */
557
0
                if (!word || !(~word)) {
558
0
                    *(lines + j) = word;
559
0
                    continue;
560
0
                }
561
562
0
                while (1) {
563
0
                    wordprev = word;
564
0
                    word = (word | (word >> 1) | (word << 1)) & mask;
565
0
                    if ((word ^ wordprev) == 0) {
566
0
                        *(lines + j) = word;
567
0
                        break;
568
0
                    }
569
0
                }
570
0
            }
571
0
        }
572
0
        break;
573
574
0
    default:
575
0
        L_ERROR("connectivity must be 4 or 8\n", __func__);
576
0
    }
577
0
}
578
579
580
/*!
581
 * \brief   pixHolesByFilling()
582
 *
583
 * \param[in]    pixs           1 bpp
584
 * \param[in]    connectivity   4 or 8
585
 * \return  pixd  inverted image of all holes, or NULL on error
586
 *
587
 * Action:
588
 *     1 Start with 1-pixel black border on otherwise white pixd
589
 *     2 Use the inverted pixs as the filling mask to fill in
590
 *         all the pixels from the border to the pixs foreground
591
 *     3 OR the result with pixs to have an image with all
592
 *         ON pixels except for the holes.
593
 *     4 Invert the result to get the holes as foreground
594
 *
595
 * <pre>
596
 * Notes:
597
 *     (1) To get 4-c.c. holes of the 8-c.c. as foreground, use
598
 *         4-connected filling; to get 8-c.c. holes of the 4-c.c.
599
 *         as foreground, use 8-connected filling.
600
 * </pre>
601
 */
602
PIX *
603
pixHolesByFilling(PIX     *pixs,
604
                  l_int32  connectivity)
605
0
{
606
0
PIX  *pixsi, *pixd;
607
608
0
    if (!pixs || pixGetDepth(pixs) != 1)
609
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
610
0
    if (connectivity != 4 && connectivity != 8)
611
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
612
613
0
    if ((pixd = pixCreateTemplate(pixs)) == NULL)
614
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
615
0
    if ((pixsi = pixInvert(NULL, pixs)) == NULL) {
616
0
        pixDestroy(&pixd);
617
0
        return (PIX *)ERROR_PTR("pixsi not made", __func__, NULL);
618
0
    }
619
620
0
    pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
621
0
    pixSeedfillBinary(pixd, pixd, pixsi, connectivity);
622
0
    pixOr(pixd, pixd, pixs);
623
0
    pixInvert(pixd, pixd);
624
0
    pixDestroy(&pixsi);
625
0
    return pixd;
626
0
}
627
628
629
/*!
630
 * \brief   pixFillClosedBorders()
631
 *
632
 * \param[in]    pixs           1 bpp
633
 * \param[in]    connectivity   filling connectivity 4 or 8
634
 * \return  pixd  all topologically outer closed borders are filled
635
 *                     as connected comonents, or NULL on error
636
 *
637
 * <pre>
638
 * Notes:
639
 *      (1) Start with 1-pixel black border on otherwise white pixd
640
 *      (2) Subtract input pixs to remove border pixels that were
641
 *          also on the closed border
642
 *      (3) Use the inverted pixs as the filling mask to fill in
643
 *          all the pixels from the outer border to the closed border
644
 *          on pixs
645
 *      (4) Invert the result to get the filled component, including
646
 *          the input border
647
 *      (5) If the borders are 4-c.c., use 8-c.c. filling, and v.v.
648
 *      (6) Closed borders within c.c. that represent holes, etc., are filled.
649
 * </pre>
650
 */
651
PIX *
652
pixFillClosedBorders(PIX     *pixs,
653
                     l_int32  connectivity)
654
0
{
655
0
PIX  *pixsi, *pixd;
656
657
0
    if (!pixs || pixGetDepth(pixs) != 1)
658
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
659
0
    if (connectivity != 4 && connectivity != 8)
660
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
661
662
0
    if ((pixd = pixCreateTemplate(pixs)) == NULL)
663
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
664
0
    pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
665
0
    pixSubtract(pixd, pixd, pixs);
666
0
    if ((pixsi = pixInvert(NULL, pixs)) == NULL) {
667
0
        pixDestroy(&pixd);
668
0
        return (PIX *)ERROR_PTR("pixsi not made", __func__, NULL);
669
0
    }
670
671
0
    pixSeedfillBinary(pixd, pixd, pixsi, connectivity);
672
0
    pixInvert(pixd, pixd);
673
0
    pixDestroy(&pixsi);
674
675
0
    return pixd;
676
0
}
677
678
679
/*!
680
 * \brief   pixExtractBorderConnComps()
681
 *
682
 * \param[in]    pixs           1 bpp
683
 * \param[in]    connectivity   filling connectivity 4 or 8
684
 * \return  pixd  all pixels in the src that are in connected
685
 *                components touching the border, or NULL on error
686
 */
687
PIX *
688
pixExtractBorderConnComps(PIX     *pixs,
689
                          l_int32  connectivity)
690
0
{
691
0
PIX  *pixd;
692
693
0
    if (!pixs || pixGetDepth(pixs) != 1)
694
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
695
0
    if (connectivity != 4 && connectivity != 8)
696
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
697
698
        /* Start with 1 pixel wide black border as seed in pixd */
699
0
    if ((pixd = pixCreateTemplate(pixs)) == NULL)
700
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
701
0
    pixSetOrClearBorder(pixd, 1, 1, 1, 1, PIX_SET);
702
703
       /* Fill in pixd from the seed, using pixs as the filling mask.
704
        * This fills all components from pixs that are touching the border. */
705
0
    pixSeedfillBinary(pixd, pixd, pixs, connectivity);
706
707
0
    return pixd;
708
0
}
709
710
711
/*!
712
 * \brief   pixRemoveBorderConnComps()
713
 *
714
 * \param[in]    pixs           1 bpp
715
 * \param[in]    connectivity   filling connectivity 4 or 8
716
 * \return  pixd  all pixels in the src that are not touching the
717
 *                border or NULL on error
718
 *
719
 * <pre>
720
 * Notes:
721
 *      (1) This removes all fg components touching the border.
722
 * </pre>
723
 */
724
PIX *
725
pixRemoveBorderConnComps(PIX     *pixs,
726
                         l_int32  connectivity)
727
0
{
728
0
PIX  *pixd;
729
730
0
    if (!pixs || pixGetDepth(pixs) != 1)
731
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
732
0
    if (connectivity != 4 && connectivity != 8)
733
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
734
735
       /* Fill from a 1 pixel wide seed at the border into all components
736
        * in pixs (the filling mask) that are touching the border */
737
0
    pixd = pixExtractBorderConnComps(pixs, connectivity);
738
739
       /* Save in pixd only those components in pixs not touching the border */
740
0
    pixXor(pixd, pixd, pixs);
741
0
    return pixd;
742
0
}
743
744
745
/*!
746
 * \brief   pixFillBgFromBorder()
747
 *
748
 * \param[in]    pixs           1 bpp
749
 * \param[in]    connectivity   filling connectivity 4 or 8
750
 * \return  pixd with the background c.c. touching the border
751
 *               filled to foreground, or NULL on error
752
 *
753
 * <pre>
754
 * Notes:
755
 *      (1) This fills all bg components touching the border to fg.
756
 *          It is the photometric inverse of pixRemoveBorderConnComps().
757
 *      (2) Invert the result to get the "holes" left after this fill.
758
 *          This can be done multiple times, extracting holes within
759
 *          holes after each pair of fillings.  Specifically, this code
760
 *          peels away n successive embeddings of components:
761
 * \code
762
 *              pix1 = <initial image>
763
 *              for (i = 0; i < 2 * n; i++) {
764
 *                   pix2 = pixFillBgFromBorder(pix1, 8);
765
 *                   pixInvert(pix2, pix2);
766
 *                   pixDestroy(&pix1);
767
 *                   pix1 = pix2;
768
 *              }
769
 * \endcode
770
 * </pre>
771
 */
772
PIX *
773
pixFillBgFromBorder(PIX     *pixs,
774
                    l_int32  connectivity)
775
0
{
776
0
PIX  *pixd;
777
778
0
    if (!pixs || pixGetDepth(pixs) != 1)
779
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
780
0
    if (connectivity != 4 && connectivity != 8)
781
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
782
783
       /* Invert to turn bg touching the border to a fg component.
784
        * Extract this by filling from a 1 pixel wide seed at the border. */
785
0
    pixInvert(pixs, pixs);
786
0
    pixd = pixExtractBorderConnComps(pixs, connectivity);
787
0
    pixInvert(pixs, pixs);  /* restore pixs */
788
789
       /* Bit-or the filled bg component with pixs */
790
0
    pixOr(pixd, pixd, pixs);
791
0
    return pixd;
792
0
}
793
794
795
/*-----------------------------------------------------------------------*
796
 *            Hole-filling of components to bounding rectangle           *
797
 *-----------------------------------------------------------------------*/
798
/*!
799
 * \brief   pixFillHolesToBoundingRect()
800
 *
801
 * \param[in]    pixs         1 bpp
802
 * \param[in]    minsize      min number of pixels in the hole
803
 * \param[in]    maxhfract    max hole area as fraction of fg pixels in the cc
804
 * \param[in]    minfgfract   min fg area as fraction of bounding rectangle
805
 * \return  pixd   with some holes possibly filled and some c.c. possibly
806
 *                 expanded to their bounding rects, or NULL on error
807
 *
808
 * <pre>
809
 * Notes:
810
 *      (1) This does not fill holes that are smaller in area than 'minsize'.
811
 *          Use %minsize = 0 and %maxhfract = 1.0 to fill all holes.
812
 *      (2) This does not fill holes with an area larger than
813
 *          %maxhfract times the fg area of the c.c.
814
 *          Use 1.0 to fill all holes.
815
 *      (3) This does not expand the fg of the c.c. to bounding rect if
816
 *          the fg area is less than %minfgfract times the area of the
817
 *          bounding rect.  Use 1.0 to skip expanding to the bounding rect.
818
 *      (4) The decisions are made as follows:
819
 *           ~ Decide if we are filling the holes; if so, when using
820
 *             the fg area, include the filled holes.
821
 *           ~ Decide based on the fg area if we are filling to a bounding rect.
822
 *             If so, do it.
823
 *             If not, fill the holes if the condition is satisfied.
824
 *      (5) The choice of %minsize depends on the resolution.
825
 *      (6) For solidifying image mask regions on printed materials,
826
 *          which tend to be rectangular, values for %maxhfract
827
 *          and %minfgfract around 0.5 are reasonable.
828
 * </pre>
829
 */
830
PIX *
831
pixFillHolesToBoundingRect(PIX       *pixs,
832
                           l_int32    minsize,
833
                           l_float32  maxhfract,
834
                           l_float32  minfgfract)
835
0
{
836
0
l_int32    i, x, y, w, h, n, nfg, nh, ntot, area;
837
0
l_int32   *tab;
838
0
l_float32  hfract;  /* measured hole fraction */
839
0
l_float32  fgfract;  /* measured fg fraction */
840
0
BOXA      *boxa;
841
0
PIX       *pixd, *pixfg, *pixh;
842
0
PIXA      *pixa;
843
844
0
    if (!pixs || pixGetDepth(pixs) != 1)
845
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
846
0
    maxhfract = L_MIN(L_MAX(maxhfract, 0.0), 1.0);
847
0
    minfgfract = L_MIN(L_MAX(minfgfract, 0.0), 1.0);
848
849
0
    pixd = pixCopy(NULL, pixs);
850
0
    boxa = pixConnComp(pixd, &pixa, 8);
851
0
    n = boxaGetCount(boxa);
852
0
    tab = makePixelSumTab8();
853
0
    for (i = 0; i < n; i++) {
854
0
        boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
855
0
        area = w * h;
856
0
        if (area < minsize)
857
0
            continue;
858
0
        pixfg = pixaGetPix(pixa, i, L_COPY);
859
0
        pixh = pixHolesByFilling(pixfg, 4);  /* holes only */
860
0
        pixCountPixels(pixfg, &nfg, tab);
861
0
        pixCountPixels(pixh, &nh, tab);
862
0
        hfract = (l_float32)nh / (l_float32)nfg;
863
0
        ntot = nfg;
864
0
        if (hfract <= maxhfract)  /* we will fill the holes (at least) */
865
0
            ntot = nfg + nh;
866
0
        fgfract = (l_float32)ntot / (l_float32)area;
867
0
        if (fgfract >= minfgfract) {  /* fill to bounding rect */
868
0
            pixSetAll(pixfg);
869
0
            pixRasterop(pixd, x, y, w, h, PIX_SRC, pixfg, 0, 0);
870
0
        } else if (hfract <= maxhfract) {  /* fill just the holes */
871
0
            pixRasterop(pixd, x, y, w, h, PIX_DST | PIX_SRC , pixh, 0, 0);
872
0
        }
873
0
        pixDestroy(&pixfg);
874
0
        pixDestroy(&pixh);
875
0
    }
876
0
    boxaDestroy(&boxa);
877
0
    pixaDestroy(&pixa);
878
0
    LEPT_FREE(tab);
879
0
    return pixd;
880
0
}
881
882
883
/*-----------------------------------------------------------------------*
884
 *               Vincent's hybrid Grayscale Seedfill method              *
885
 *-----------------------------------------------------------------------*/
886
/*!
887
 * \brief   pixSeedfillGray()
888
 *
889
 * \param[in]    pixs           8 bpp seed; filled in place
890
 * \param[in]    pixm           8 bpp filling mask
891
 * \param[in]    connectivity   4 or 8
892
 * \return  0 if OK, 1 on error
893
 *
894
 * <pre>
895
 * Notes:
896
 *      (1) This is an in-place filling operation on the seed, pixs,
897
 *          where the clipping mask is always above or at the level
898
 *          of the seed as it is filled.
899
 *      (2) For details of the operation, see the description in
900
 *          seedfillGrayLow() and the code there.
901
 *      (3) As an example of use, see the description in pixHDome().
902
 *          There, the seed is an image where each pixel is a fixed
903
 *          amount smaller than the corresponding mask pixel.
904
 *      (4) Reference paper :
905
 *            L. Vincent, Morphological grayscale reconstruction in image
906
 *            analysis: applications and efficient algorithms, IEEE Transactions
907
 *            on  Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
908
 * </pre>
909
 */
910
l_ok
911
pixSeedfillGray(PIX     *pixs,
912
                PIX     *pixm,
913
                l_int32  connectivity)
914
0
{
915
0
l_int32    h, w, wpls, wplm;
916
0
l_uint32  *datas, *datam;
917
918
0
    if (!pixs || pixGetDepth(pixs) != 8)
919
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
920
0
    if (!pixm || pixGetDepth(pixm) != 8)
921
0
        return ERROR_INT("pixm not defined or not 8 bpp", __func__, 1);
922
0
    if (connectivity != 4 && connectivity != 8)
923
0
        return ERROR_INT("connectivity not in {4,8}", __func__, 1);
924
925
        /* Make sure the sizes of seed and mask images are the same */
926
0
    if (pixSizesEqual(pixs, pixm) == 0)
927
0
        return ERROR_INT("pixs and pixm sizes differ", __func__, 1);
928
929
0
    datas = pixGetData(pixs);
930
0
    datam = pixGetData(pixm);
931
0
    wpls = pixGetWpl(pixs);
932
0
    wplm = pixGetWpl(pixm);
933
0
    pixGetDimensions(pixs, &w, &h, NULL);
934
0
    seedfillGrayLow(datas, w, h, wpls, datam, wplm, connectivity);
935
936
0
    return 0;
937
0
}
938
939
940
/*!
941
 * \brief   pixSeedfillGrayInv()
942
 *
943
 * \param[in]    pixs           8 bpp seed; filled in place
944
 * \param[in]    pixm           8 bpp filling mask
945
 * \param[in]    connectivity   4 or 8
946
 * \return  0 if OK, 1 on error
947
 *
948
 * <pre>
949
 * Notes:
950
 *      (1) This is an in-place filling operation on the seed, pixs,
951
 *          where the clipping mask is always below or at the level
952
 *          of the seed as it is filled.  Think of filling up a basin
953
 *          to a particular level, given by the maximum seed value
954
 *          in the basin.  Outside the filled region, the mask
955
 *          is above the filling level.
956
 *      (2) Contrast this with pixSeedfillGray(), where the clipping mask
957
 *          is always above or at the level of the fill.  An example
958
 *          of its use is the hdome fill, where the seed is an image
959
 *          where each pixel is a fixed amount smaller than the
960
 *          corresponding mask pixel.
961
 *      (3) The basin fill, pixSeedfillGrayBasin(), is a special case
962
 *          where the seed pixel values are generated from the mask,
963
 *          and where the implementation uses pixSeedfillGray() by
964
 *          inverting both the seed and mask.
965
 * </pre>
966
 */
967
l_ok
968
pixSeedfillGrayInv(PIX     *pixs,
969
                   PIX     *pixm,
970
                   l_int32  connectivity)
971
0
{
972
0
l_int32    h, w, wpls, wplm;
973
0
l_uint32  *datas, *datam;
974
975
0
    if (!pixs || pixGetDepth(pixs) != 8)
976
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
977
0
    if (!pixm || pixGetDepth(pixm) != 8)
978
0
        return ERROR_INT("pixm not defined or not 8 bpp", __func__, 1);
979
0
    if (connectivity != 4 && connectivity != 8)
980
0
        return ERROR_INT("connectivity not in {4,8}", __func__, 1);
981
982
        /* Make sure the sizes of seed and mask images are the same */
983
0
    if (pixSizesEqual(pixs, pixm) == 0)
984
0
        return ERROR_INT("pixs and pixm sizes differ", __func__, 1);
985
986
0
    datas = pixGetData(pixs);
987
0
    datam = pixGetData(pixm);
988
0
    wpls = pixGetWpl(pixs);
989
0
    wplm = pixGetWpl(pixm);
990
0
    pixGetDimensions(pixs, &w, &h, NULL);
991
0
    seedfillGrayInvLow(datas, w, h, wpls, datam, wplm, connectivity);
992
993
0
    return 0;
994
0
}
995
996
997
/*!
998
 * \brief   seedfillGrayLow()
999
 *
1000
 *  Notes:
1001
 *      (1) The pixels are numbered as follows:
1002
 *              1  2  3
1003
 *              4  x  5
1004
 *              6  7  8
1005
 *          This low-level filling operation consists of two scans,
1006
 *          raster and anti-raster, covering the entire seed image.
1007
 *          This is followed by a breadth-first propagation operation to
1008
 *          complete the fill.
1009
 *          During the anti-raster scan, every pixel p whose current value
1010
 *          could still be propagated after the anti-raster scan is put into
1011
 *          the FIFO queue.
1012
 *          The propagation step is a breadth-first fill to completion.
1013
 *          Unlike the simple grayscale seedfill pixSeedfillGraySimple(),
1014
 *          where at least two full raster/anti-raster iterations are required
1015
 *          for completion and verification, the hybrid method uses only a
1016
 *          single raster/anti-raster set of scans.
1017
 *      (2) The filling action can be visualized from the following example.
1018
 *          Suppose the mask, which clips the fill, is a sombrero-shaped
1019
 *          surface, where the highest point is 200 and the low pixels
1020
 *          around the rim are 30.  Beyond the rim, the mask goes up a bit.
1021
 *          Suppose the seed, which is filled, consists of a single point
1022
 *          of height 150, located below the max of the mask, with
1023
 *          the rest 0.  Then in the raster scan, nothing happens until
1024
 *          the high seed point is encountered, and then this value is
1025
 *          propagated right and down, until it hits the side of the
1026
 *          sombrero.   The seed can never exceed the mask, so it fills
1027
 *          to the rim, going lower along the mask surface.  When it
1028
 *          passes the rim, the seed continues to fill at the rim
1029
 *          height to the edge of the seed image.  Then on the
1030
 *          anti-raster scan, the seed fills flat inside the
1031
 *          sombrero to the upper and left, and then out from the
1032
 *          rim as before.  The final result has a seed that is
1033
 *          flat outside the rim, and inside it fills the sombrero
1034
 *          but only up to 150.  If the rim height varies, the
1035
 *          filled seed outside the rim will be at the highest
1036
 *          point on the rim, which is a saddle point on the rim.
1037
 *      (3) Reference paper :
1038
 *            L. Vincent, Morphological grayscale reconstruction in image
1039
 *            analysis: applications and efficient algorithms, IEEE Transactions
1040
 *            on  Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
1041
 */
1042
static void
1043
seedfillGrayLow(l_uint32  *datas,
1044
                l_int32    w,
1045
                l_int32    h,
1046
                l_int32    wpls,
1047
                l_uint32  *datam,
1048
                l_int32    wplm,
1049
                l_int32    connectivity)
1050
0
{
1051
0
l_uint8    val1, val2, val3, val4, val5, val6, val7, val8;
1052
0
l_uint8    val, maxval, maskval, boolval;
1053
0
l_int32    i, j, imax, jmax, queue_size;
1054
0
l_uint32  *lines, *linem;
1055
0
L_PIXEL *pixel;
1056
0
L_QUEUE  *lq_pixel;
1057
1058
0
    if (connectivity != 4 && connectivity != 8) {
1059
0
        L_ERROR("connectivity must be 4 or 8\n", __func__);
1060
0
        return;
1061
0
    }
1062
1063
0
    imax = h - 1;
1064
0
    jmax = w - 1;
1065
1066
        /* In the worst case, most of the pixels could be pushed
1067
         * onto the FIFO queue during anti-raster scan.  However this
1068
         * will rarely happen, and we initialize the queue ptr size to
1069
         * the image perimeter. */
1070
0
    lq_pixel = lqueueCreate(2 * (w + h));
1071
1072
0
    switch (connectivity)
1073
0
    {
1074
0
    case 4:
1075
            /* UL --> LR scan  (Raster Order)
1076
             * If I : mask image
1077
             *    J : marker image
1078
             * Let p be the currect pixel;
1079
             * J(p) <- (max{J(p) union J(p) neighbors in raster order})
1080
             *          intersection I(p) */
1081
0
        for (i = 0; i < h; i++) {
1082
0
            lines = datas + i * wpls;
1083
0
            linem = datam + i * wplm;
1084
0
            for (j = 0; j < w; j++) {
1085
0
                if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
1086
0
                    maxval = 0;
1087
0
                    if (i > 0)
1088
0
                        maxval = GET_DATA_BYTE(lines - wpls, j);
1089
0
                    if (j > 0) {
1090
0
                        val4 = GET_DATA_BYTE(lines, j - 1);
1091
0
                        maxval = L_MAX(maxval, val4);
1092
0
                    }
1093
0
                    val = GET_DATA_BYTE(lines, j);
1094
0
                    maxval = L_MAX(maxval, val);
1095
0
                    val = L_MIN(maxval, maskval);
1096
0
                    SET_DATA_BYTE(lines, j, val);
1097
0
                }
1098
0
            }
1099
0
        }
1100
1101
            /* LR --> UL scan (anti-raster order)
1102
             * Let p be the currect pixel;
1103
             * J(p) <- (max{J(p) union J(p) neighbors in anti-raster order})
1104
             *          intersection I(p) */
1105
0
        for (i = imax; i >= 0; i--) {
1106
0
            lines = datas + i * wpls;
1107
0
            linem = datam + i * wplm;
1108
0
            for (j = jmax; j >= 0; j--) {
1109
0
                boolval = FALSE;
1110
0
                if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
1111
0
                    maxval = 0;
1112
0
                    if (i < imax)
1113
0
                        maxval = GET_DATA_BYTE(lines + wpls, j);
1114
0
                    if (j < jmax) {
1115
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
1116
0
                        maxval = L_MAX(maxval, val5);
1117
0
                    }
1118
0
                    val = GET_DATA_BYTE(lines, j);
1119
0
                    maxval = L_MAX(maxval, val);
1120
0
                    val = L_MIN(maxval, maskval);
1121
0
                    SET_DATA_BYTE(lines, j, val);
1122
1123
                        /*
1124
                         * If there exists a point (q) which belongs to J(p)
1125
                         * neighbors in anti-raster order such that J(q) < J(p)
1126
                         * and J(q) < I(q) then
1127
                         * fifo_add(p) */
1128
0
                    if (i < imax) {
1129
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
1130
0
                        if ((val7 < val) &&
1131
0
                            (val7 < GET_DATA_BYTE(linem + wplm, j))) {
1132
0
                            boolval = TRUE;
1133
0
                        }
1134
0
                    }
1135
0
                    if (j < jmax) {
1136
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
1137
0
                        if (!boolval && (val5 < val) &&
1138
0
                            (val5 < GET_DATA_BYTE(linem, j + 1))) {
1139
0
                            boolval = TRUE;
1140
0
                        }
1141
0
                    }
1142
0
                    if (boolval) {
1143
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1144
0
                        pixel->x = i;
1145
0
                        pixel->y = j;
1146
0
                        lqueueAdd(lq_pixel, pixel);
1147
0
                    }
1148
0
                }
1149
0
            }
1150
0
        }
1151
1152
            /* Propagation step:
1153
             *        while fifo_empty = false
1154
             *          p <- fifo_first()
1155
             *          for every pixel (q) belong to neighbors of (p)
1156
             *            if J(q) < J(p) and I(q) != J(q)
1157
             *              J(q) <- min(J(p), I(q));
1158
             *              fifo_add(q);
1159
             *            end
1160
             *          end
1161
             *        end */
1162
0
        queue_size = lqueueGetCount(lq_pixel);
1163
0
        while (queue_size) {
1164
0
            pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
1165
0
            i = pixel->x;
1166
0
            j = pixel->y;
1167
0
            LEPT_FREE(pixel);
1168
0
            lines = datas + i * wpls;
1169
0
            linem = datam + i * wplm;
1170
1171
0
            if ((val = GET_DATA_BYTE(lines, j)) > 0) {
1172
0
                if (i > 0) {
1173
0
                    val2 = GET_DATA_BYTE(lines - wpls, j);
1174
0
                    maskval = GET_DATA_BYTE(linem - wplm, j);
1175
0
                    if (val > val2 && val2 != maskval) {
1176
0
                        SET_DATA_BYTE(lines - wpls, j, L_MIN(val, maskval));
1177
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1178
0
                        pixel->x = i - 1;
1179
0
                        pixel->y = j;
1180
0
                        lqueueAdd(lq_pixel, pixel);
1181
0
                    }
1182
1183
0
                }
1184
0
                if (j > 0) {
1185
0
                    val4 = GET_DATA_BYTE(lines, j - 1);
1186
0
                    maskval = GET_DATA_BYTE(linem, j - 1);
1187
0
                    if (val > val4 && val4 != maskval) {
1188
0
                        SET_DATA_BYTE(lines, j - 1, L_MIN(val, maskval));
1189
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1190
0
                        pixel->x = i;
1191
0
                        pixel->y = j - 1;
1192
0
                        lqueueAdd(lq_pixel, pixel);
1193
0
                    }
1194
0
                }
1195
0
                if (i < imax) {
1196
0
                    val7 = GET_DATA_BYTE(lines + wpls, j);
1197
0
                    maskval = GET_DATA_BYTE(linem + wplm, j);
1198
0
                    if (val > val7 && val7 != maskval) {
1199
0
                        SET_DATA_BYTE(lines + wpls, j, L_MIN(val, maskval));
1200
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1201
0
                        pixel->x = i + 1;
1202
0
                        pixel->y = j;
1203
0
                        lqueueAdd(lq_pixel, pixel);
1204
0
                    }
1205
0
                }
1206
0
                if (j < jmax) {
1207
0
                    val5 = GET_DATA_BYTE(lines, j + 1);
1208
0
                    maskval = GET_DATA_BYTE(linem, j + 1);
1209
0
                    if (val > val5 && val5 != maskval) {
1210
0
                        SET_DATA_BYTE(lines, j + 1, L_MIN(val, maskval));
1211
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1212
0
                        pixel->x = i;
1213
0
                        pixel->y = j + 1;
1214
0
                        lqueueAdd(lq_pixel, pixel);
1215
0
                    }
1216
0
                }
1217
0
            }
1218
1219
0
            queue_size = lqueueGetCount(lq_pixel);
1220
0
        }
1221
0
        break;
1222
1223
0
    case 8:
1224
            /* UL --> LR scan  (Raster Order)
1225
             * If I : mask image
1226
             *    J : marker image
1227
             * Let p be the currect pixel;
1228
             * J(p) <- (max{J(p) union J(p) neighbors in raster order})
1229
             *          intersection I(p) */
1230
0
        for (i = 0; i < h; i++) {
1231
0
            lines = datas + i * wpls;
1232
0
            linem = datam + i * wplm;
1233
0
            for (j = 0; j < w; j++) {
1234
0
                if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
1235
0
                    maxval = 0;
1236
0
                    if (i > 0) {
1237
0
                        if (j > 0)
1238
0
                            maxval = GET_DATA_BYTE(lines - wpls, j - 1);
1239
0
                        if (j < jmax) {
1240
0
                            val3 = GET_DATA_BYTE(lines - wpls, j + 1);
1241
0
                            maxval = L_MAX(maxval, val3);
1242
0
                        }
1243
0
                        val2 = GET_DATA_BYTE(lines - wpls, j);
1244
0
                        maxval = L_MAX(maxval, val2);
1245
0
                    }
1246
0
                    if (j > 0) {
1247
0
                        val4 = GET_DATA_BYTE(lines, j - 1);
1248
0
                        maxval = L_MAX(maxval, val4);
1249
0
                    }
1250
0
                    val = GET_DATA_BYTE(lines, j);
1251
0
                    maxval = L_MAX(maxval, val);
1252
0
                    val = L_MIN(maxval, maskval);
1253
0
                    SET_DATA_BYTE(lines, j, val);
1254
0
                }
1255
0
            }
1256
0
        }
1257
1258
            /* LR --> UL scan (anti-raster order)
1259
             * Let p be the currect pixel;
1260
             * J(p) <- (max{J(p) union J(p) neighbors in anti-raster order})
1261
             *          intersection I(p) */
1262
0
        for (i = imax; i >= 0; i--) {
1263
0
            lines = datas + i * wpls;
1264
0
            linem = datam + i * wplm;
1265
0
            for (j = jmax; j >= 0; j--) {
1266
0
                boolval = FALSE;
1267
0
                if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
1268
0
                    maxval = 0;
1269
0
                    if (i < imax) {
1270
0
                        if (j > 0) {
1271
0
                            maxval = GET_DATA_BYTE(lines + wpls, j - 1);
1272
0
                        }
1273
0
                        if (j < jmax) {
1274
0
                            val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1275
0
                            maxval = L_MAX(maxval, val8);
1276
0
                        }
1277
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
1278
0
                        maxval = L_MAX(maxval, val7);
1279
0
                    }
1280
0
                    if (j < jmax) {
1281
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
1282
0
                        maxval = L_MAX(maxval, val5);
1283
0
                    }
1284
0
                    val = GET_DATA_BYTE(lines, j);
1285
0
                    maxval = L_MAX(maxval, val);
1286
0
                    val = L_MIN(maxval, maskval);
1287
0
                    SET_DATA_BYTE(lines, j, val);
1288
1289
                        /* If there exists a point (q) which belongs to J(p)
1290
                         * neighbors in anti-raster order such that J(q) < J(p)
1291
                         * and J(q) < I(q) then
1292
                         * fifo_add(p) */
1293
0
                    if (i < imax) {
1294
0
                        if (j > 0) {
1295
0
                            val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1296
0
                            if ((val6 < val) &&
1297
0
                                (val6 < GET_DATA_BYTE(linem + wplm, j - 1))) {
1298
0
                                boolval = TRUE;
1299
0
                            }
1300
0
                        }
1301
0
                        if (j < jmax) {
1302
0
                            val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1303
0
                            if (!boolval && (val8 < val) &&
1304
0
                                (val8 < GET_DATA_BYTE(linem + wplm, j + 1))) {
1305
0
                                boolval = TRUE;
1306
0
                            }
1307
0
                        }
1308
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
1309
0
                        if (!boolval && (val7 < val) &&
1310
0
                            (val7 < GET_DATA_BYTE(linem + wplm, j))) {
1311
0
                            boolval = TRUE;
1312
0
                        }
1313
0
                    }
1314
0
                    if (j < jmax) {
1315
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
1316
0
                        if (!boolval && (val5 < val) &&
1317
0
                            (val5 < GET_DATA_BYTE(linem, j + 1))) {
1318
0
                            boolval = TRUE;
1319
0
                        }
1320
0
                    }
1321
0
                    if (boolval) {
1322
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1323
0
                        pixel->x = i;
1324
0
                        pixel->y = j;
1325
0
                        lqueueAdd(lq_pixel, pixel);
1326
0
                    }
1327
0
                }
1328
0
            }
1329
0
        }
1330
1331
            /* Propagation step:
1332
             *        while fifo_empty = false
1333
             *          p <- fifo_first()
1334
             *          for every pixel (q) belong to neighbors of (p)
1335
             *            if J(q) < J(p) and I(q) != J(q)
1336
             *              J(q) <- min(J(p), I(q));
1337
             *              fifo_add(q);
1338
             *            end
1339
             *          end
1340
             *        end */
1341
0
        queue_size = lqueueGetCount(lq_pixel);
1342
0
        while (queue_size) {
1343
0
            pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
1344
0
            i = pixel->x;
1345
0
            j = pixel->y;
1346
0
            LEPT_FREE(pixel);
1347
0
            lines = datas + i * wpls;
1348
0
            linem = datam + i * wplm;
1349
1350
0
            if ((val = GET_DATA_BYTE(lines, j)) > 0) {
1351
0
                if (i > 0) {
1352
0
                    if (j > 0) {
1353
0
                        val1 = GET_DATA_BYTE(lines - wpls, j - 1);
1354
0
                        maskval = GET_DATA_BYTE(linem - wplm, j - 1);
1355
0
                        if (val > val1 && val1 != maskval) {
1356
0
                            SET_DATA_BYTE(lines - wpls, j - 1,
1357
0
                                          L_MIN(val, maskval));
1358
0
                            pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1359
0
                            pixel->x = i - 1;
1360
0
                            pixel->y = j - 1;
1361
0
                            lqueueAdd(lq_pixel, pixel);
1362
0
                        }
1363
0
                    }
1364
0
                    if (j < jmax) {
1365
0
                        val3 = GET_DATA_BYTE(lines - wpls, j + 1);
1366
0
                        maskval = GET_DATA_BYTE(linem - wplm, j + 1);
1367
0
                        if (val > val3 && val3 != maskval) {
1368
0
                            SET_DATA_BYTE(lines - wpls, j + 1,
1369
0
                                          L_MIN(val, maskval));
1370
0
                            pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1371
0
                            pixel->x = i - 1;
1372
0
                            pixel->y = j + 1;
1373
0
                            lqueueAdd(lq_pixel, pixel);
1374
0
                        }
1375
0
                    }
1376
0
                    val2 = GET_DATA_BYTE(lines - wpls, j);
1377
0
                    maskval = GET_DATA_BYTE(linem - wplm, j);
1378
0
                    if (val > val2 && val2 != maskval) {
1379
0
                        SET_DATA_BYTE(lines - wpls, j, L_MIN(val, maskval));
1380
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1381
0
                        pixel->x = i - 1;
1382
0
                        pixel->y = j;
1383
0
                        lqueueAdd(lq_pixel, pixel);
1384
0
                    }
1385
1386
0
                }
1387
0
                if (j > 0) {
1388
0
                    val4 = GET_DATA_BYTE(lines, j - 1);
1389
0
                    maskval = GET_DATA_BYTE(linem, j - 1);
1390
0
                    if (val > val4 && val4 != maskval) {
1391
0
                        SET_DATA_BYTE(lines, j - 1, L_MIN(val, maskval));
1392
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1393
0
                        pixel->x = i;
1394
0
                        pixel->y = j - 1;
1395
0
                        lqueueAdd(lq_pixel, pixel);
1396
0
                    }
1397
0
                }
1398
0
                if (i < imax) {
1399
0
                    if (j > 0) {
1400
0
                        val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1401
0
                        maskval = GET_DATA_BYTE(linem + wplm, j - 1);
1402
0
                        if (val > val6 && val6 != maskval) {
1403
0
                            SET_DATA_BYTE(lines + wpls, j - 1,
1404
0
                                          L_MIN(val, maskval));
1405
0
                            pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1406
0
                            pixel->x = i + 1;
1407
0
                            pixel->y = j - 1;
1408
0
                            lqueueAdd(lq_pixel, pixel);
1409
0
                        }
1410
0
                    }
1411
0
                    if (j < jmax) {
1412
0
                        val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1413
0
                        maskval = GET_DATA_BYTE(linem + wplm, j + 1);
1414
0
                        if (val > val8 && val8 != maskval) {
1415
0
                            SET_DATA_BYTE(lines + wpls, j + 1,
1416
0
                                          L_MIN(val, maskval));
1417
0
                            pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1418
0
                            pixel->x = i + 1;
1419
0
                            pixel->y = j + 1;
1420
0
                            lqueueAdd(lq_pixel, pixel);
1421
0
                        }
1422
0
                    }
1423
0
                    val7 = GET_DATA_BYTE(lines + wpls, j);
1424
0
                    maskval = GET_DATA_BYTE(linem + wplm, j);
1425
0
                    if (val > val7 && val7 != maskval) {
1426
0
                        SET_DATA_BYTE(lines + wpls, j, L_MIN(val, maskval));
1427
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1428
0
                        pixel->x = i + 1;
1429
0
                        pixel->y = j;
1430
0
                        lqueueAdd(lq_pixel, pixel);
1431
0
                    }
1432
0
                }
1433
0
                if (j < jmax) {
1434
0
                    val5 = GET_DATA_BYTE(lines, j + 1);
1435
0
                    maskval = GET_DATA_BYTE(linem, j + 1);
1436
0
                    if (val > val5 && val5 != maskval) {
1437
0
                        SET_DATA_BYTE(lines, j + 1, L_MIN(val, maskval));
1438
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1439
0
                        pixel->x = i;
1440
0
                        pixel->y = j + 1;
1441
0
                        lqueueAdd(lq_pixel, pixel);
1442
0
                    }
1443
0
                }
1444
0
            }
1445
1446
0
            queue_size = lqueueGetCount(lq_pixel);
1447
0
        }
1448
0
        break;
1449
1450
0
    default:
1451
0
        L_ERROR("shouldn't get here!\n", __func__);
1452
0
    }
1453
1454
0
    lqueueDestroy(&lq_pixel, TRUE);
1455
0
}
1456
1457
1458
/*!
1459
 * \brief   seedfillGrayInvLow()
1460
 *
1461
 *  Notes:
1462
 *      (1) The pixels are numbered as follows:
1463
 *              1  2  3
1464
 *              4  x  5
1465
 *              6  7  8
1466
 *          This low-level filling operation consists of two scans,
1467
 *          raster and anti-raster, covering the entire seed image.
1468
 *          During the anti-raster scan, every pixel p such that its
1469
 *          current value could still be propagated during the next
1470
 *          raster scanning is put into the FIFO-queue.
1471
 *          Next step is the propagation step where where we update
1472
 *          and propagate the values using FIFO structure created in
1473
 *          anti-raster scan.
1474
 *      (2) The "Inv" signifies the fact that in this case, filling
1475
 *          of the seed only takes place when the seed value is
1476
 *          greater than the mask value.  The mask will act to stop
1477
 *          the fill when it is higher than the seed level.  (This is
1478
 *          in contrast to conventional grayscale filling where the
1479
 *          seed always fills below the mask.)
1480
 *      (3) An example of use is a basin, described by the mask (pixm),
1481
 *          where within the basin, the seed pix (pixs) gets filled to the
1482
 *          height of the highest seed pixel that is above its
1483
 *          corresponding max pixel.  Filling occurs while the
1484
 *          propagating seed pixels in pixs are larger than the
1485
 *          corresponding mask values in pixm.
1486
 *      (4) Reference paper :
1487
 *            L. Vincent, Morphological grayscale reconstruction in image
1488
 *            analysis: applications and efficient algorithms, IEEE Transactions
1489
 *            on  Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
1490
 */
1491
static void
1492
seedfillGrayInvLow(l_uint32  *datas,
1493
                   l_int32    w,
1494
                   l_int32    h,
1495
                   l_int32    wpls,
1496
                   l_uint32  *datam,
1497
                   l_int32    wplm,
1498
                   l_int32    connectivity)
1499
0
{
1500
0
l_uint8    val1, val2, val3, val4, val5, val6, val7, val8;
1501
0
l_uint8    val, maxval, maskval, boolval;
1502
0
l_int32    i, j, imax, jmax, queue_size;
1503
0
l_uint32  *lines, *linem;
1504
0
L_PIXEL *pixel;
1505
0
L_QUEUE  *lq_pixel;
1506
1507
0
    if (connectivity != 4 && connectivity != 8) {
1508
0
        L_ERROR("connectivity must be 4 or 8\n", __func__);
1509
0
        return;
1510
0
    }
1511
1512
0
    imax = h - 1;
1513
0
    jmax = w - 1;
1514
1515
        /* In the worst case, most of the pixels could be pushed
1516
         * onto the FIFO queue during anti-raster scan.  However this
1517
         * will rarely happen, and we initialize the queue ptr size to
1518
         * the image perimeter. */
1519
0
    lq_pixel = lqueueCreate(2 * (w + h));
1520
1521
0
    switch (connectivity)
1522
0
    {
1523
0
    case 4:
1524
            /* UL --> LR scan  (Raster Order)
1525
             * If I : mask image
1526
             *    J : marker image
1527
             * Let p be the currect pixel;
1528
             * tmp <- max{J(p) union J(p) neighbors in raster order}
1529
             * if (tmp > I(p))
1530
             *   J(p) <- tmp
1531
             * end */
1532
0
        for (i = 0; i < h; i++) {
1533
0
            lines = datas + i * wpls;
1534
0
            linem = datam + i * wplm;
1535
0
            for (j = 0; j < w; j++) {
1536
0
                if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
1537
0
                    maxval = GET_DATA_BYTE(lines, j);
1538
0
                    if (i > 0) {
1539
0
                        val2 = GET_DATA_BYTE(lines - wpls, j);
1540
0
                        maxval = L_MAX(maxval, val2);
1541
0
                    }
1542
0
                    if (j > 0) {
1543
0
                        val4 = GET_DATA_BYTE(lines, j - 1);
1544
0
                        maxval = L_MAX(maxval, val4);
1545
0
                    }
1546
0
                    if (maxval > maskval)
1547
0
                        SET_DATA_BYTE(lines, j, maxval);
1548
0
                }
1549
0
            }
1550
0
        }
1551
1552
            /* LR --> UL scan (anti-raster order)
1553
             * If I : mask image
1554
             *    J : marker image
1555
             * Let p be the currect pixel;
1556
             * tmp <- max{J(p) union J(p) neighbors in anti-raster order}
1557
             * if (tmp > I(p))
1558
             *   J(p) <- tmp
1559
             * end */
1560
0
        for (i = imax; i >= 0; i--) {
1561
0
            lines = datas + i * wpls;
1562
0
            linem = datam + i * wplm;
1563
0
            for (j = jmax; j >= 0; j--) {
1564
0
                boolval = FALSE;
1565
0
                if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
1566
0
                    val = maxval = GET_DATA_BYTE(lines, j);
1567
0
                    if (i < imax) {
1568
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
1569
0
                        maxval = L_MAX(maxval, val7);
1570
0
                    }
1571
0
                    if (j < jmax) {
1572
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
1573
0
                        maxval = L_MAX(maxval, val5);
1574
0
                    }
1575
0
                    if (maxval > maskval)
1576
0
                        SET_DATA_BYTE(lines, j, maxval);
1577
0
                    val = GET_DATA_BYTE(lines, j);
1578
1579
                        /*
1580
                         * If there exists a point (q) which belongs to J(p)
1581
                         * neighbors in anti-raster order such that J(q) < J(p)
1582
                         * and J(p) > I(q) then
1583
                         * fifo_add(p) */
1584
0
                    if (i < imax) {
1585
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
1586
0
                        if ((val7 < val) &&
1587
0
                            (val > GET_DATA_BYTE(linem + wplm, j))) {
1588
0
                            boolval = TRUE;
1589
0
                        }
1590
0
                    }
1591
0
                    if (j < jmax) {
1592
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
1593
0
                        if (!boolval && (val5 < val) &&
1594
0
                            (val > GET_DATA_BYTE(linem, j + 1))) {
1595
0
                            boolval = TRUE;
1596
0
                        }
1597
0
                    }
1598
0
                    if (boolval) {
1599
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1600
0
                        pixel->x = i;
1601
0
                        pixel->y = j;
1602
0
                        lqueueAdd(lq_pixel, pixel);
1603
0
                    }
1604
0
                }
1605
0
            }
1606
0
        }
1607
1608
            /* Propagation step:
1609
             *        while fifo_empty = false
1610
             *          p <- fifo_first()
1611
             *          for every pixel (q) belong to neighbors of (p)
1612
             *            if J(q) < J(p) and J(p) > I(q)
1613
             *              J(q) <- min(J(p), I(q));
1614
             *              fifo_add(q);
1615
             *            end
1616
             *          end
1617
             *        end */
1618
0
        queue_size = lqueueGetCount(lq_pixel);
1619
0
        while (queue_size) {
1620
0
            pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
1621
0
            i = pixel->x;
1622
0
            j = pixel->y;
1623
0
            LEPT_FREE(pixel);
1624
0
            lines = datas + i * wpls;
1625
0
            linem = datam + i * wplm;
1626
1627
0
            if ((val = GET_DATA_BYTE(lines, j)) > 0) {
1628
0
                if (i > 0) {
1629
0
                    val2 = GET_DATA_BYTE(lines - wpls, j);
1630
0
                    maskval = GET_DATA_BYTE(linem - wplm, j);
1631
0
                    if (val > val2 && val > maskval) {
1632
0
                        SET_DATA_BYTE(lines - wpls, j, val);
1633
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1634
0
                        pixel->x = i - 1;
1635
0
                        pixel->y = j;
1636
0
                        lqueueAdd(lq_pixel, pixel);
1637
0
                    }
1638
1639
0
                }
1640
0
                if (j > 0) {
1641
0
                    val4 = GET_DATA_BYTE(lines, j - 1);
1642
0
                    maskval = GET_DATA_BYTE(linem, j - 1);
1643
0
                    if (val > val4 && val > maskval) {
1644
0
                        SET_DATA_BYTE(lines, j - 1, val);
1645
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1646
0
                        pixel->x = i;
1647
0
                        pixel->y = j - 1;
1648
0
                        lqueueAdd(lq_pixel, pixel);
1649
0
                    }
1650
0
                }
1651
0
                if (i < imax) {
1652
0
                    val7 = GET_DATA_BYTE(lines + wpls, j);
1653
0
                    maskval = GET_DATA_BYTE(linem + wplm, j);
1654
0
                    if (val > val7 && val > maskval) {
1655
0
                        SET_DATA_BYTE(lines + wpls, j, val);
1656
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1657
0
                        pixel->x = i + 1;
1658
0
                        pixel->y = j;
1659
0
                        lqueueAdd(lq_pixel, pixel);
1660
0
                    }
1661
0
                }
1662
0
                if (j < jmax) {
1663
0
                    val5 = GET_DATA_BYTE(lines, j + 1);
1664
0
                    maskval = GET_DATA_BYTE(linem, j + 1);
1665
0
                    if (val > val5 && val > maskval) {
1666
0
                        SET_DATA_BYTE(lines, j + 1, val);
1667
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1668
0
                        pixel->x = i;
1669
0
                        pixel->y = j + 1;
1670
0
                        lqueueAdd(lq_pixel, pixel);
1671
0
                    }
1672
0
                }
1673
0
            }
1674
1675
0
            queue_size = lqueueGetCount(lq_pixel);
1676
0
        }
1677
0
        break;
1678
1679
0
    case 8:
1680
            /* UL --> LR scan  (Raster Order)
1681
             * If I : mask image
1682
             *    J : marker image
1683
             * Let p be the currect pixel;
1684
             * tmp <- max{J(p) union J(p) neighbors in raster order}
1685
             * if (tmp > I(p))
1686
             *   J(p) <- tmp
1687
             * end */
1688
0
        for (i = 0; i < h; i++) {
1689
0
            lines = datas + i * wpls;
1690
0
            linem = datam + i * wplm;
1691
0
            for (j = 0; j < w; j++) {
1692
0
                if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
1693
0
                    maxval = GET_DATA_BYTE(lines, j);
1694
0
                    if (i > 0) {
1695
0
                        if (j > 0) {
1696
0
                            val1 = GET_DATA_BYTE(lines - wpls, j - 1);
1697
0
                            maxval = L_MAX(maxval, val1);
1698
0
                        }
1699
0
                        if (j < jmax) {
1700
0
                            val3 = GET_DATA_BYTE(lines - wpls, j + 1);
1701
0
                            maxval = L_MAX(maxval, val3);
1702
0
                        }
1703
0
                        val2 = GET_DATA_BYTE(lines - wpls, j);
1704
0
                        maxval = L_MAX(maxval, val2);
1705
0
                    }
1706
0
                    if (j > 0) {
1707
0
                        val4 = GET_DATA_BYTE(lines, j - 1);
1708
0
                        maxval = L_MAX(maxval, val4);
1709
0
                    }
1710
0
                    if (maxval > maskval)
1711
0
                        SET_DATA_BYTE(lines, j, maxval);
1712
0
                }
1713
0
            }
1714
0
        }
1715
1716
            /* LR --> UL scan (anti-raster order)
1717
             * If I : mask image
1718
             *    J : marker image
1719
             * Let p be the currect pixel;
1720
             * tmp <- max{J(p) union J(p) neighbors in anti-raster order}
1721
             * if (tmp > I(p))
1722
             *   J(p) <- tmp
1723
             * end */
1724
0
        for (i = imax; i >= 0; i--) {
1725
0
            lines = datas + i * wpls;
1726
0
            linem = datam + i * wplm;
1727
0
            for (j = jmax; j >= 0; j--) {
1728
0
                boolval = FALSE;
1729
0
                if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
1730
0
                    maxval = GET_DATA_BYTE(lines, j);
1731
0
                    if (i < imax) {
1732
0
                        if (j > 0) {
1733
0
                            val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1734
0
                            maxval = L_MAX(maxval, val6);
1735
0
                        }
1736
0
                        if (j < jmax) {
1737
0
                            val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1738
0
                            maxval = L_MAX(maxval, val8);
1739
0
                        }
1740
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
1741
0
                        maxval = L_MAX(maxval, val7);
1742
0
                    }
1743
0
                    if (j < jmax) {
1744
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
1745
0
                        maxval = L_MAX(maxval, val5);
1746
0
                    }
1747
0
                    if (maxval > maskval)
1748
0
                        SET_DATA_BYTE(lines, j, maxval);
1749
0
                    val = GET_DATA_BYTE(lines, j);
1750
1751
                        /*
1752
                         * If there exists a point (q) which belongs to J(p)
1753
                         * neighbors in anti-raster order such that J(q) < J(p)
1754
                         * and J(p) > I(q) then
1755
                         * fifo_add(p) */
1756
0
                    if (i < imax) {
1757
0
                        if (j > 0) {
1758
0
                            val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1759
0
                            if ((val6 < val) &&
1760
0
                                (val > GET_DATA_BYTE(linem + wplm, j - 1))) {
1761
0
                                boolval = TRUE;
1762
0
                            }
1763
0
                        }
1764
0
                        if (j < jmax) {
1765
0
                            val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1766
0
                            if (!boolval && (val8 < val) &&
1767
0
                                (val > GET_DATA_BYTE(linem + wplm, j + 1))) {
1768
0
                                boolval = TRUE;
1769
0
                            }
1770
0
                        }
1771
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
1772
0
                        if (!boolval && (val7 < val) &&
1773
0
                            (val > GET_DATA_BYTE(linem + wplm, j))) {
1774
0
                            boolval = TRUE;
1775
0
                        }
1776
0
                    }
1777
0
                    if (j < jmax) {
1778
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
1779
0
                        if (!boolval && (val5 < val) &&
1780
0
                            (val > GET_DATA_BYTE(linem, j + 1))) {
1781
0
                            boolval = TRUE;
1782
0
                        }
1783
0
                    }
1784
0
                    if (boolval) {
1785
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1786
0
                        pixel->x = i;
1787
0
                        pixel->y = j;
1788
0
                        lqueueAdd(lq_pixel, pixel);
1789
0
                    }
1790
0
                }
1791
0
            }
1792
0
        }
1793
1794
            /* Propagation step:
1795
             *        while fifo_empty = false
1796
             *          p <- fifo_first()
1797
             *          for every pixel (q) belong to neighbors of (p)
1798
             *            if J(q) < J(p) and J(p) > I(q)
1799
             *              J(q) <- min(J(p), I(q));
1800
             *              fifo_add(q);
1801
             *            end
1802
             *          end
1803
             *        end */
1804
0
        queue_size = lqueueGetCount(lq_pixel);
1805
0
        while (queue_size) {
1806
0
            pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
1807
0
            i = pixel->x;
1808
0
            j = pixel->y;
1809
0
            LEPT_FREE(pixel);
1810
0
            lines = datas + i * wpls;
1811
0
            linem = datam + i * wplm;
1812
1813
0
            if ((val = GET_DATA_BYTE(lines, j)) > 0) {
1814
0
                if (i > 0) {
1815
0
                    if (j > 0) {
1816
0
                        val1 = GET_DATA_BYTE(lines - wpls, j - 1);
1817
0
                        maskval = GET_DATA_BYTE(linem - wplm, j - 1);
1818
0
                        if (val > val1 && val > maskval) {
1819
0
                            SET_DATA_BYTE(lines - wpls, j - 1, val);
1820
0
                            pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1821
0
                            pixel->x = i - 1;
1822
0
                            pixel->y = j - 1;
1823
0
                            lqueueAdd(lq_pixel, pixel);
1824
0
                        }
1825
0
                    }
1826
0
                    if (j < jmax) {
1827
0
                        val3 = GET_DATA_BYTE(lines - wpls, j + 1);
1828
0
                        maskval = GET_DATA_BYTE(linem - wplm, j + 1);
1829
0
                        if (val > val3 && val > maskval) {
1830
0
                            SET_DATA_BYTE(lines - wpls, j + 1, val);
1831
0
                            pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1832
0
                            pixel->x = i - 1;
1833
0
                            pixel->y = j + 1;
1834
0
                            lqueueAdd(lq_pixel, pixel);
1835
0
                        }
1836
0
                    }
1837
0
                    val2 = GET_DATA_BYTE(lines - wpls, j);
1838
0
                    maskval = GET_DATA_BYTE(linem - wplm, j);
1839
0
                    if (val > val2 && val > maskval) {
1840
0
                        SET_DATA_BYTE(lines - wpls, j, val);
1841
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1842
0
                        pixel->x = i - 1;
1843
0
                        pixel->y = j;
1844
0
                        lqueueAdd(lq_pixel, pixel);
1845
0
                    }
1846
1847
0
                }
1848
0
                if (j > 0) {
1849
0
                    val4 = GET_DATA_BYTE(lines, j - 1);
1850
0
                    maskval = GET_DATA_BYTE(linem, j - 1);
1851
0
                    if (val > val4 && val > maskval) {
1852
0
                        SET_DATA_BYTE(lines, j - 1, val);
1853
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1854
0
                        pixel->x = i;
1855
0
                        pixel->y = j - 1;
1856
0
                        lqueueAdd(lq_pixel, pixel);
1857
0
                    }
1858
0
                }
1859
0
                if (i < imax) {
1860
0
                    if (j > 0) {
1861
0
                        val6 = GET_DATA_BYTE(lines + wpls, j - 1);
1862
0
                        maskval = GET_DATA_BYTE(linem + wplm, j - 1);
1863
0
                        if (val > val6 && val > maskval) {
1864
0
                            SET_DATA_BYTE(lines + wpls, j - 1, val);
1865
0
                            pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1866
0
                            pixel->x = i + 1;
1867
0
                            pixel->y = j - 1;
1868
0
                            lqueueAdd(lq_pixel, pixel);
1869
0
                        }
1870
0
                    }
1871
0
                    if (j < jmax) {
1872
0
                        val8 = GET_DATA_BYTE(lines + wpls, j + 1);
1873
0
                        maskval = GET_DATA_BYTE(linem + wplm, j + 1);
1874
0
                        if (val > val8 && val > maskval) {
1875
0
                            SET_DATA_BYTE(lines + wpls, j + 1, val);
1876
0
                            pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1877
0
                            pixel->x = i + 1;
1878
0
                            pixel->y = j + 1;
1879
0
                            lqueueAdd(lq_pixel, pixel);
1880
0
                        }
1881
0
                    }
1882
0
                    val7 = GET_DATA_BYTE(lines + wpls, j);
1883
0
                    maskval = GET_DATA_BYTE(linem + wplm, j);
1884
0
                    if (val > val7 && val > maskval) {
1885
0
                        SET_DATA_BYTE(lines + wpls, j, val);
1886
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1887
0
                        pixel->x = i + 1;
1888
0
                        pixel->y = j;
1889
0
                        lqueueAdd(lq_pixel, pixel);
1890
0
                    }
1891
0
                }
1892
0
                if (j < jmax) {
1893
0
                    val5 = GET_DATA_BYTE(lines, j + 1);
1894
0
                    maskval = GET_DATA_BYTE(linem, j + 1);
1895
0
                    if (val > val5 && val > maskval) {
1896
0
                        SET_DATA_BYTE(lines, j + 1, val);
1897
0
                        pixel = (L_PIXEL *)LEPT_CALLOC(1, sizeof(L_PIXEL));
1898
0
                        pixel->x = i;
1899
0
                        pixel->y = j + 1;
1900
0
                        lqueueAdd(lq_pixel, pixel);
1901
0
                    }
1902
0
                }
1903
0
            }
1904
1905
0
            queue_size = lqueueGetCount(lq_pixel);
1906
0
        }
1907
0
        break;
1908
1909
0
    default:
1910
0
        L_ERROR("shouldn't get here!\n", __func__);
1911
0
    }
1912
1913
0
    lqueueDestroy(&lq_pixel, TRUE);
1914
0
}
1915
1916
1917
/*-----------------------------------------------------------------------*
1918
 *             Vincent's Iterative Grayscale Seedfill method             *
1919
 *-----------------------------------------------------------------------*/
1920
/*!
1921
 * \brief   pixSeedfillGraySimple()
1922
 *
1923
 * \param[in]    pixs           8 bpp seed; filled in place
1924
 * \param[in]    pixm           8 bpp filling mask
1925
 * \param[in]    connectivity   4 or 8
1926
 * \return  0 if OK, 1 on error
1927
 *
1928
 * <pre>
1929
 * Notes:
1930
 *      (1) This is an in-place filling operation on the seed, pixs,
1931
 *          where the clipping mask is always above or at the level
1932
 *          of the seed as it is filled.
1933
 *      (2) For details of the operation, see the description in
1934
 *          seedfillGrayLowSimple() and the code there.
1935
 *      (3) As an example of use, see the description in pixHDome().
1936
 *          There, the seed is an image where each pixel is a fixed
1937
 *          amount smaller than the corresponding mask pixel.
1938
 *      (4) Reference paper :
1939
 *            L. Vincent, Morphological grayscale reconstruction in image
1940
 *            analysis: applications and efficient algorithms, IEEE Transactions
1941
 *            on  Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
1942
 * </pre>
1943
 */
1944
l_ok
1945
pixSeedfillGraySimple(PIX     *pixs,
1946
                      PIX     *pixm,
1947
                      l_int32  connectivity)
1948
0
{
1949
0
l_int32    i, h, w, wpls, wplm, boolval;
1950
0
l_uint32  *datas, *datam;
1951
0
PIX       *pixt;
1952
1953
0
    if (!pixs || pixGetDepth(pixs) != 8)
1954
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
1955
0
    if (!pixm || pixGetDepth(pixm) != 8)
1956
0
        return ERROR_INT("pixm not defined or not 8 bpp", __func__, 1);
1957
0
    if (connectivity != 4 && connectivity != 8)
1958
0
        return ERROR_INT("connectivity not in {4,8}", __func__, 1);
1959
1960
        /* Make sure the sizes of seed and mask images are the same */
1961
0
    if (pixSizesEqual(pixs, pixm) == 0)
1962
0
        return ERROR_INT("pixs and pixm sizes differ", __func__, 1);
1963
1964
        /* This is used to test for completion */
1965
0
    if ((pixt = pixCreateTemplate(pixs)) == NULL)
1966
0
        return ERROR_INT("pixt not made", __func__, 1);
1967
1968
0
    datas = pixGetData(pixs);
1969
0
    datam = pixGetData(pixm);
1970
0
    wpls = pixGetWpl(pixs);
1971
0
    wplm = pixGetWpl(pixm);
1972
0
    pixGetDimensions(pixs, &w, &h, NULL);
1973
0
    for (i = 0; i < MaxIters; i++) {
1974
0
        pixCopy(pixt, pixs);
1975
0
        seedfillGrayLowSimple(datas, w, h, wpls, datam, wplm, connectivity);
1976
0
        pixEqual(pixs, pixt, &boolval);
1977
0
        if (boolval == 1) {
1978
#if DEBUG_PRINT_ITERS
1979
            L_INFO("Gray seed fill converged: %d iters\n", __func__, i + 1);
1980
#endif  /* DEBUG_PRINT_ITERS */
1981
0
            break;
1982
0
        }
1983
0
    }
1984
1985
0
    pixDestroy(&pixt);
1986
0
    return 0;
1987
0
}
1988
1989
1990
/*!
1991
 * \brief   pixSeedfillGrayInvSimple()
1992
 *
1993
 * \param[in]    pixs           8 bpp seed; filled in place
1994
 * \param[in]    pixm           8 bpp filling mask
1995
 * \param[in]    connectivity   4 or 8
1996
 * \return  0 if OK, 1 on error
1997
 *
1998
 * <pre>
1999
 * Notes:
2000
 *      (1) This is an in-place filling operation on the seed, pixs,
2001
 *          where the clipping mask is always below or at the level
2002
 *          of the seed as it is filled.  Think of filling up a basin
2003
 *          to a particular level, given by the maximum seed value
2004
 *          in the basin.  Outside the filled region, the mask
2005
 *          is above the filling level.
2006
 *      (2) Contrast this with pixSeedfillGraySimple(), where the clipping mask
2007
 *          is always above or at the level of the fill.  An example
2008
 *          of its use is the hdome fill, where the seed is an image
2009
 *          where each pixel is a fixed amount smaller than the
2010
 *          corresponding mask pixel.
2011
 * </pre>
2012
 */
2013
l_ok
2014
pixSeedfillGrayInvSimple(PIX     *pixs,
2015
                         PIX     *pixm,
2016
                         l_int32  connectivity)
2017
0
{
2018
0
l_int32    i, h, w, wpls, wplm, boolval;
2019
0
l_uint32  *datas, *datam;
2020
0
PIX       *pixt;
2021
2022
0
    if (!pixs || pixGetDepth(pixs) != 8)
2023
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
2024
0
    if (!pixm || pixGetDepth(pixm) != 8)
2025
0
        return ERROR_INT("pixm not defined or not 8 bpp", __func__, 1);
2026
0
    if (connectivity != 4 && connectivity != 8)
2027
0
        return ERROR_INT("connectivity not in {4,8}", __func__, 1);
2028
2029
        /* Make sure the sizes of seed and mask images are the same */
2030
0
    if (pixSizesEqual(pixs, pixm) == 0)
2031
0
        return ERROR_INT("pixs and pixm sizes differ", __func__, 1);
2032
2033
        /* This is used to test for completion */
2034
0
    if ((pixt = pixCreateTemplate(pixs)) == NULL)
2035
0
        return ERROR_INT("pixt not made", __func__, 1);
2036
2037
0
    datas = pixGetData(pixs);
2038
0
    datam = pixGetData(pixm);
2039
0
    wpls = pixGetWpl(pixs);
2040
0
    wplm = pixGetWpl(pixm);
2041
0
    pixGetDimensions(pixs, &w, &h, NULL);
2042
0
    for (i = 0; i < MaxIters; i++) {
2043
0
        pixCopy(pixt, pixs);
2044
0
        seedfillGrayInvLowSimple(datas, w, h, wpls, datam, wplm, connectivity);
2045
0
        pixEqual(pixs, pixt, &boolval);
2046
0
        if (boolval == 1) {
2047
#if DEBUG_PRINT_ITERS
2048
            L_INFO("Gray seed fill converged: %d iters\n", __func__, i + 1);
2049
#endif  /* DEBUG_PRINT_ITERS */
2050
0
            break;
2051
0
        }
2052
0
    }
2053
2054
0
    pixDestroy(&pixt);
2055
0
    return 0;
2056
0
}
2057
2058
2059
/*!
2060
 * \brief   seedfillGrayLowSimple()
2061
 *
2062
 *  Notes:
2063
 *      (1) The pixels are numbered as follows:
2064
 *              1  2  3
2065
 *              4  x  5
2066
 *              6  7  8
2067
 *          This low-level filling operation consists of two scans,
2068
 *          raster and anti-raster, covering the entire seed image.
2069
 *          The caller typically iterates until the filling is
2070
 *          complete.
2071
 *      (2) The filling action can be visualized from the following example.
2072
 *          Suppose the mask, which clips the fill, is a sombrero-shaped
2073
 *          surface, where the highest point is 200 and the low pixels
2074
 *          around the rim are 30.  Beyond the rim, the mask goes up a bit.
2075
 *          Suppose the seed, which is filled, consists of a single point
2076
 *          of height 150, located below the max of the mask, with
2077
 *          the rest 0.  Then in the raster scan, nothing happens until
2078
 *          the high seed point is encountered, and then this value is
2079
 *          propagated right and down, until it hits the side of the
2080
 *          sombrero.   The seed can never exceed the mask, so it fills
2081
 *          to the rim, going lower along the mask surface.  When it
2082
 *          passes the rim, the seed continues to fill at the rim
2083
 *          height to the edge of the seed image.  Then on the
2084
 *          anti-raster scan, the seed fills flat inside the
2085
 *          sombrero to the upper and left, and then out from the
2086
 *          rim as before.  The final result has a seed that is
2087
 *          flat outside the rim, and inside it fills the sombrero
2088
 *          but only up to 150.  If the rim height varies, the
2089
 *          filled seed outside the rim will be at the highest
2090
 *          point on the rim, which is a saddle point on the rim.
2091
 */
2092
static void
2093
seedfillGrayLowSimple(l_uint32  *datas,
2094
                      l_int32    w,
2095
                      l_int32    h,
2096
                      l_int32    wpls,
2097
                      l_uint32  *datam,
2098
                      l_int32    wplm,
2099
                      l_int32    connectivity)
2100
0
{
2101
0
l_uint8    val2, val3, val4, val5, val7, val8;
2102
0
l_uint8    val, maxval, maskval;
2103
0
l_int32    i, j, imax, jmax;
2104
0
l_uint32  *lines, *linem;
2105
2106
0
    imax = h - 1;
2107
0
    jmax = w - 1;
2108
2109
0
    switch (connectivity)
2110
0
    {
2111
0
    case 4:
2112
            /* UL --> LR scan */
2113
0
        for (i = 0; i < h; i++) {
2114
0
            lines = datas + i * wpls;
2115
0
            linem = datam + i * wplm;
2116
0
            for (j = 0; j < w; j++) {
2117
0
                if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
2118
0
                    maxval = 0;
2119
0
                    if (i > 0)
2120
0
                        maxval = GET_DATA_BYTE(lines - wpls, j);
2121
0
                    if (j > 0) {
2122
0
                        val4 = GET_DATA_BYTE(lines, j - 1);
2123
0
                        maxval = L_MAX(maxval, val4);
2124
0
                    }
2125
0
                    val = GET_DATA_BYTE(lines, j);
2126
0
                    maxval = L_MAX(maxval, val);
2127
0
                    val = L_MIN(maxval, maskval);
2128
0
                    SET_DATA_BYTE(lines, j, val);
2129
0
                }
2130
0
            }
2131
0
        }
2132
2133
            /* LR --> UL scan */
2134
0
        for (i = imax; i >= 0; i--) {
2135
0
            lines = datas + i * wpls;
2136
0
            linem = datam + i * wplm;
2137
0
            for (j = jmax; j >= 0; j--) {
2138
0
                if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
2139
0
                    maxval = 0;
2140
0
                    if (i < imax)
2141
0
                        maxval = GET_DATA_BYTE(lines + wpls, j);
2142
0
                    if (j < jmax) {
2143
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
2144
0
                        maxval = L_MAX(maxval, val5);
2145
0
                    }
2146
0
                    val = GET_DATA_BYTE(lines, j);
2147
0
                    maxval = L_MAX(maxval, val);
2148
0
                    val = L_MIN(maxval, maskval);
2149
0
                    SET_DATA_BYTE(lines, j, val);
2150
0
                }
2151
0
            }
2152
0
        }
2153
0
        break;
2154
2155
0
    case 8:
2156
            /* UL --> LR scan */
2157
0
        for (i = 0; i < h; i++) {
2158
0
            lines = datas + i * wpls;
2159
0
            linem = datam + i * wplm;
2160
0
            for (j = 0; j < w; j++) {
2161
0
                if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
2162
0
                    maxval = 0;
2163
0
                    if (i > 0) {
2164
0
                        if (j > 0)
2165
0
                            maxval = GET_DATA_BYTE(lines - wpls, j - 1);
2166
0
                        if (j < jmax) {
2167
0
                            val2 = GET_DATA_BYTE(lines - wpls, j + 1);
2168
0
                            maxval = L_MAX(maxval, val2);
2169
0
                        }
2170
0
                        val3 = GET_DATA_BYTE(lines - wpls, j);
2171
0
                        maxval = L_MAX(maxval, val3);
2172
0
                    }
2173
0
                    if (j > 0) {
2174
0
                        val4 = GET_DATA_BYTE(lines, j - 1);
2175
0
                        maxval = L_MAX(maxval, val4);
2176
0
                    }
2177
0
                    val = GET_DATA_BYTE(lines, j);
2178
0
                    maxval = L_MAX(maxval, val);
2179
0
                    val = L_MIN(maxval, maskval);
2180
0
                    SET_DATA_BYTE(lines, j, val);
2181
0
                }
2182
0
            }
2183
0
        }
2184
2185
            /* LR --> UL scan */
2186
0
        for (i = imax; i >= 0; i--) {
2187
0
            lines = datas + i * wpls;
2188
0
            linem = datam + i * wplm;
2189
0
            for (j = jmax; j >= 0; j--) {
2190
0
                if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
2191
0
                    maxval = 0;
2192
0
                    if (i < imax) {
2193
0
                        if (j > 0)
2194
0
                            maxval = GET_DATA_BYTE(lines + wpls, j - 1);
2195
0
                        if (j < jmax) {
2196
0
                            val8 = GET_DATA_BYTE(lines + wpls, j + 1);
2197
0
                            maxval = L_MAX(maxval, val8);
2198
0
                        }
2199
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
2200
0
                        maxval = L_MAX(maxval, val7);
2201
0
                    }
2202
0
                    if (j < jmax) {
2203
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
2204
0
                        maxval = L_MAX(maxval, val5);
2205
0
                    }
2206
0
                    val = GET_DATA_BYTE(lines, j);
2207
0
                    maxval = L_MAX(maxval, val);
2208
0
                    val = L_MIN(maxval, maskval);
2209
0
                    SET_DATA_BYTE(lines, j, val);
2210
0
                }
2211
0
            }
2212
0
        }
2213
0
        break;
2214
2215
0
    default:
2216
0
        L_ERROR("connectivity must be 4 or 8\n", __func__);
2217
0
    }
2218
0
}
2219
2220
2221
/*!
2222
 * \brief   seedfillGrayInvLowSimple()
2223
 *
2224
 *  Notes:
2225
 *      (1) The pixels are numbered as follows:
2226
 *              1  2  3
2227
 *              4  x  5
2228
 *              6  7  8
2229
 *          This low-level filling operation consists of two scans,
2230
 *          raster and anti-raster, covering the entire seed image.
2231
 *          The caller typically iterates until the filling is
2232
 *          complete.
2233
 *      (2) The "Inv" signifies the fact that in this case, filling
2234
 *          of the seed only takes place when the seed value is
2235
 *          greater than the mask value.  The mask will act to stop
2236
 *          the fill when it is higher than the seed level.  (This is
2237
 *          in contrast to conventional grayscale filling where the
2238
 *          seed always fills below the mask.)
2239
 *      (3) An example of use is a basin, described by the mask (pixm),
2240
 *          where within the basin, the seed pix (pixs) gets filled to the
2241
 *          height of the highest seed pixel that is above its
2242
 *          corresponding max pixel.  Filling occurs while the
2243
 *          propagating seed pixels in pixs are larger than the
2244
 *          corresponding mask values in pixm.
2245
 */
2246
static void
2247
seedfillGrayInvLowSimple(l_uint32  *datas,
2248
                         l_int32    w,
2249
                         l_int32    h,
2250
                         l_int32    wpls,
2251
                         l_uint32  *datam,
2252
                         l_int32    wplm,
2253
                         l_int32    connectivity)
2254
0
{
2255
0
l_uint8    val1, val2, val3, val4, val5, val6, val7, val8;
2256
0
l_uint8    maxval, maskval;
2257
0
l_int32    i, j, imax, jmax;
2258
0
l_uint32  *lines, *linem;
2259
2260
0
    imax = h - 1;
2261
0
    jmax = w - 1;
2262
2263
0
    switch (connectivity)
2264
0
    {
2265
0
    case 4:
2266
            /* UL --> LR scan */
2267
0
        for (i = 0; i < h; i++) {
2268
0
            lines = datas + i * wpls;
2269
0
            linem = datam + i * wplm;
2270
0
            for (j = 0; j < w; j++) {
2271
0
                if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
2272
0
                    maxval = GET_DATA_BYTE(lines, j);
2273
0
                    if (i > 0) {
2274
0
                        val2 = GET_DATA_BYTE(lines - wpls, j);
2275
0
                        maxval = L_MAX(maxval, val2);
2276
0
                    }
2277
0
                    if (j > 0) {
2278
0
                        val4 = GET_DATA_BYTE(lines, j - 1);
2279
0
                        maxval = L_MAX(maxval, val4);
2280
0
                    }
2281
0
                    if (maxval > maskval)
2282
0
                        SET_DATA_BYTE(lines, j, maxval);
2283
0
                }
2284
0
            }
2285
0
        }
2286
2287
            /* LR --> UL scan */
2288
0
        for (i = imax; i >= 0; i--) {
2289
0
            lines = datas + i * wpls;
2290
0
            linem = datam + i * wplm;
2291
0
            for (j = jmax; j >= 0; j--) {
2292
0
                if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
2293
0
                    maxval = GET_DATA_BYTE(lines, j);
2294
0
                    if (i < imax) {
2295
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
2296
0
                        maxval = L_MAX(maxval, val7);
2297
0
                    }
2298
0
                    if (j < jmax) {
2299
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
2300
0
                        maxval = L_MAX(maxval, val5);
2301
0
                    }
2302
0
                    if (maxval > maskval)
2303
0
                        SET_DATA_BYTE(lines, j, maxval);
2304
0
                }
2305
0
            }
2306
0
        }
2307
0
        break;
2308
2309
0
    case 8:
2310
            /* UL --> LR scan */
2311
0
        for (i = 0; i < h; i++) {
2312
0
            lines = datas + i * wpls;
2313
0
            linem = datam + i * wplm;
2314
0
            for (j = 0; j < w; j++) {
2315
0
                if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
2316
0
                    maxval = GET_DATA_BYTE(lines, j);
2317
0
                    if (i > 0) {
2318
0
                        if (j > 0) {
2319
0
                            val1 = GET_DATA_BYTE(lines - wpls, j - 1);
2320
0
                            maxval = L_MAX(maxval, val1);
2321
0
                        }
2322
0
                        if (j < jmax) {
2323
0
                            val2 = GET_DATA_BYTE(lines - wpls, j + 1);
2324
0
                            maxval = L_MAX(maxval, val2);
2325
0
                        }
2326
0
                        val3 = GET_DATA_BYTE(lines - wpls, j);
2327
0
                        maxval = L_MAX(maxval, val3);
2328
0
                    }
2329
0
                    if (j > 0) {
2330
0
                        val4 = GET_DATA_BYTE(lines, j - 1);
2331
0
                        maxval = L_MAX(maxval, val4);
2332
0
                    }
2333
0
                    if (maxval > maskval)
2334
0
                        SET_DATA_BYTE(lines, j, maxval);
2335
0
                }
2336
0
            }
2337
0
        }
2338
2339
            /* LR --> UL scan */
2340
0
        for (i = imax; i >= 0; i--) {
2341
0
            lines = datas + i * wpls;
2342
0
            linem = datam + i * wplm;
2343
0
            for (j = jmax; j >= 0; j--) {
2344
0
                if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
2345
0
                    maxval = GET_DATA_BYTE(lines, j);
2346
0
                    if (i < imax) {
2347
0
                        if (j > 0) {
2348
0
                            val6 = GET_DATA_BYTE(lines + wpls, j - 1);
2349
0
                            maxval = L_MAX(maxval, val6);
2350
0
                        }
2351
0
                        if (j < jmax) {
2352
0
                            val8 = GET_DATA_BYTE(lines + wpls, j + 1);
2353
0
                            maxval = L_MAX(maxval, val8);
2354
0
                        }
2355
0
                        val7 = GET_DATA_BYTE(lines + wpls, j);
2356
0
                        maxval = L_MAX(maxval, val7);
2357
0
                    }
2358
0
                    if (j < jmax) {
2359
0
                        val5 = GET_DATA_BYTE(lines, j + 1);
2360
0
                        maxval = L_MAX(maxval, val5);
2361
0
                    }
2362
0
                    if (maxval > maskval)
2363
0
                        SET_DATA_BYTE(lines, j, maxval);
2364
0
                }
2365
0
            }
2366
0
        }
2367
0
        break;
2368
2369
0
    default:
2370
0
        L_ERROR("connectivity must be 4 or 8\n", __func__);
2371
0
    }
2372
0
}
2373
2374
2375
/*-----------------------------------------------------------------------*
2376
 *                         Gray seedfill variations                      *
2377
 *-----------------------------------------------------------------------*/
2378
/*!
2379
 * \brief   pixSeedfillGrayBasin()
2380
 *
2381
 * \param[in]    pixb           binary mask giving seed locations
2382
 * \param[in]    pixm           8 bpp basin-type filling mask
2383
 * \param[in]    delta          amount of seed value above mask
2384
 * \param[in]    connectivity   4 or 8
2385
 * \return  pixd filled seed if OK, NULL on error
2386
 *
2387
 * <pre>
2388
 * Notes:
2389
 *      (1) This fills from a seed within basins defined by a filling mask.
2390
 *          The seed value(s) are greater than the corresponding
2391
 *          filling mask value, and the result has the bottoms of
2392
 *          the basins raised by the initial seed value.
2393
 *      (2) The seed has value 255 except where pixb has fg (1), which
2394
 *          are the seed 'locations'.  At the seed locations, the seed
2395
 *          value is the corresponding value of the mask pixel in pixm
2396
 *          plus %delta.  If %delta == 0, we return a copy of pixm.
2397
 *      (3) The actual filling is done using the standard grayscale filling
2398
 *          operation on the inverse of the mask and using the inverse
2399
 *          of the seed image.  After filling, we return the inverse of
2400
 *          the filled seed.
2401
 *      (4) As an example of use: pixm can describe a grayscale image
2402
 *          of text, where the (dark) text pixels are basins of
2403
 *          low values; pixb can identify the local minima in pixm (say, at
2404
 *          the bottom of the basins); and delta is the amount that we wish
2405
 *          to raise (lighten) the basins.  We construct the seed
2406
 *          (a.k.a marker) image from pixb, pixm and %delta.
2407
 * </pre>
2408
 */
2409
PIX *
2410
pixSeedfillGrayBasin(PIX     *pixb,
2411
                     PIX     *pixm,
2412
                     l_int32  delta,
2413
                     l_int32  connectivity)
2414
0
{
2415
0
PIX  *pixbi, *pixmi, *pixsd;
2416
2417
0
    if (!pixb || pixGetDepth(pixb) != 1)
2418
0
        return (PIX *)ERROR_PTR("pixb undefined or not 1 bpp", __func__, NULL);
2419
0
    if (!pixm || pixGetDepth(pixm) != 8)
2420
0
        return (PIX *)ERROR_PTR("pixm undefined or not 8 bpp", __func__, NULL);
2421
0
    if (connectivity != 4 && connectivity != 8)
2422
0
        return (PIX *)ERROR_PTR("connectivity not in {4,8}", __func__, NULL);
2423
2424
0
    if (delta <= 0) {
2425
0
        L_WARNING("delta <= 0; returning a copy of pixm\n", __func__);
2426
0
        return pixCopy(NULL, pixm);
2427
0
    }
2428
2429
        /* Add delta to every pixel in pixm */
2430
0
    pixsd = pixCopy(NULL, pixm);
2431
0
    pixAddConstantGray(pixsd, delta);
2432
2433
        /* Prepare the seed.  Write 255 in all pixels of
2434
         * ([pixm] + delta) where pixb is 0. */
2435
0
    pixbi = pixInvert(NULL, pixb);
2436
0
    pixSetMasked(pixsd, pixbi, 255);
2437
2438
        /* Fill the inverse seed, using the inverse clipping mask */
2439
0
    pixmi = pixInvert(NULL, pixm);
2440
0
    pixInvert(pixsd, pixsd);
2441
0
    pixSeedfillGray(pixsd, pixmi, connectivity);
2442
2443
        /* Re-invert the filled seed */
2444
0
    pixInvert(pixsd, pixsd);
2445
2446
0
    pixDestroy(&pixbi);
2447
0
    pixDestroy(&pixmi);
2448
0
    return pixsd;
2449
0
}
2450
2451
2452
/*-----------------------------------------------------------------------*
2453
 *                   Vincent's Distance Function method                  *
2454
 *-----------------------------------------------------------------------*/
2455
/*!
2456
 * \brief   pixDistanceFunction()
2457
 *
2458
 * \param[in]    pixs           1 bpp
2459
 * \param[in]    connectivity   4 or 8
2460
 * \param[in]    outdepth       8 or 16 bits for pixd
2461
 * \param[in]    boundcond      L_BOUNDARY_BG, L_BOUNDARY_FG
2462
 * \return  pixd, or NULL on error
2463
 *
2464
 * <pre>
2465
 * Notes:
2466
 *      (1) This computes the distance of each pixel from the nearest
2467
 *          background pixel.  All bg pixels therefore have a distance of 0,
2468
 *          and the fg pixel distances increase linearly from 1 at the
2469
 *          boundary.  It can also be used to compute the distance of
2470
 *          each pixel from the nearest fg pixel, by inverting the input
2471
 *          image before calling this function.  Then all fg pixels have
2472
 *          a distance 0 and the bg pixel distances increase linearly
2473
 *          from 1 at the boundary.
2474
 *      (2) The algorithm, described in Leptonica on the page on seed
2475
 *          filling and connected components, is due to Luc Vincent.
2476
 *          In brief, we generate an 8 or 16 bpp image, initialized
2477
 *          with the fg pixels of the input pix set to 1 and the
2478
 *          1-boundary pixels (i.e., the boundary pixels of width 1 on
2479
 *          the four sides set as either:
2480
 *            * L_BOUNDARY_BG: 0
2481
 *            * L_BOUNDARY_FG:  max
2482
 *          where max = 0xff for 8 bpp and 0xffff for 16 bpp.
2483
 *          Then do raster/anti-raster sweeps over all pixels interior
2484
 *          to the 1-boundary, where the value of each new pixel is
2485
 *          taken to be 1 more than the minimum of the previously-seen
2486
 *          connected pixels (using either 4 or 8 connectivity).
2487
 *          Finally, set the 1-boundary pixels using the mirrored method;
2488
 *          this removes the max values there.
2489
 *      (3) Using L_BOUNDARY_BG clamps the distance to 0 at the
2490
 *          boundary.  Using L_BOUNDARY_FG allows the distance
2491
 *          at the image boundary to "float".
2492
 *      (4) For 4-connected, one could initialize only the left and top
2493
 *          1-boundary pixels, and go all the way to the right
2494
 *          and bottom; then coming back reset left and top.  But we
2495
 *          instead use a method that works for both 4- and 8-connected.
2496
 * </pre>
2497
 */
2498
PIX *
2499
pixDistanceFunction(PIX     *pixs,
2500
                    l_int32  connectivity,
2501
                    l_int32  outdepth,
2502
                    l_int32  boundcond)
2503
0
{
2504
0
l_int32    w, h, wpld;
2505
0
l_uint32  *datad;
2506
0
PIX       *pixd;
2507
2508
0
    if (!pixs || pixGetDepth(pixs) != 1)
2509
0
        return (PIX *)ERROR_PTR("!pixs or pixs not 1 bpp", __func__, NULL);
2510
0
    if (connectivity != 4 && connectivity != 8)
2511
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
2512
0
    if (outdepth != 8 && outdepth != 16)
2513
0
        return (PIX *)ERROR_PTR("outdepth not 8 or 16 bpp", __func__, NULL);
2514
0
    if (boundcond != L_BOUNDARY_BG && boundcond != L_BOUNDARY_FG)
2515
0
        return (PIX *)ERROR_PTR("invalid boundcond", __func__, NULL);
2516
2517
0
    pixGetDimensions(pixs, &w, &h, NULL);
2518
0
    if ((pixd = pixCreate(w, h, outdepth)) == NULL)
2519
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2520
0
    datad = pixGetData(pixd);
2521
0
    wpld = pixGetWpl(pixd);
2522
2523
        /* Initialize the fg pixels to 1 and the bg pixels to 0 */
2524
0
    pixSetMasked(pixd, pixs, 1);
2525
2526
0
    if (boundcond == L_BOUNDARY_BG) {
2527
0
        distanceFunctionLow(datad, w, h, outdepth, wpld, connectivity);
2528
0
    } else {  /* L_BOUNDARY_FG: set boundary pixels to max val */
2529
0
        pixRasterop(pixd, 0, 0, w, 1, PIX_SET, NULL, 0, 0);   /* top */
2530
0
        pixRasterop(pixd, 0, h - 1, w, 1, PIX_SET, NULL, 0, 0);   /* bot */
2531
0
        pixRasterop(pixd, 0, 0, 1, h, PIX_SET, NULL, 0, 0);   /* left */
2532
0
        pixRasterop(pixd, w - 1, 0, 1, h, PIX_SET, NULL, 0, 0);   /* right */
2533
2534
0
        distanceFunctionLow(datad, w, h, outdepth, wpld, connectivity);
2535
2536
            /* Set each boundary pixel equal to the pixel next to it */
2537
0
        pixSetMirroredBorder(pixd, 1, 1, 1, 1);
2538
0
    }
2539
2540
0
    return pixd;
2541
0
}
2542
2543
2544
/*!
2545
 * \brief   distanceFunctionLow()
2546
 */
2547
static void
2548
distanceFunctionLow(l_uint32  *datad,
2549
                    l_int32    w,
2550
                    l_int32    h,
2551
                    l_int32    d,
2552
                    l_int32    wpld,
2553
                    l_int32    connectivity)
2554
0
{
2555
0
l_int32    val1, val2, val3, val4, val5, val6, val7, val8, minval, val;
2556
0
l_int32    i, j, imax, jmax;
2557
0
l_uint32  *lined;
2558
2559
        /* One raster scan followed by one anti-raster scan.
2560
         * This does not re-set the 1-boundary of pixels that
2561
         * were initialized to either 0 or maxval. */
2562
0
    imax = h - 1;
2563
0
    jmax = w - 1;
2564
0
    switch (connectivity)
2565
0
    {
2566
0
    case 4:
2567
0
        if (d == 8) {
2568
                /* UL --> LR scan */
2569
0
            for (i = 1; i < imax; i++) {
2570
0
                lined = datad + i * wpld;
2571
0
                for (j = 1; j < jmax; j++) {
2572
0
                    if ((val = GET_DATA_BYTE(lined, j)) > 0) {
2573
0
                        val2 = GET_DATA_BYTE(lined - wpld, j);
2574
0
                        val4 = GET_DATA_BYTE(lined, j - 1);
2575
0
                        minval = L_MIN(val2, val4);
2576
0
                        minval = L_MIN(minval, 254);
2577
0
                        SET_DATA_BYTE(lined, j, minval + 1);
2578
0
                    }
2579
0
                }
2580
0
            }
2581
2582
                /* LR --> UL scan */
2583
0
            for (i = imax - 1; i > 0; i--) {
2584
0
                lined = datad + i * wpld;
2585
0
                for (j = jmax - 1; j > 0; j--) {
2586
0
                    if ((val = GET_DATA_BYTE(lined, j)) > 0) {
2587
0
                        val7 = GET_DATA_BYTE(lined + wpld, j);
2588
0
                        val5 = GET_DATA_BYTE(lined, j + 1);
2589
0
                        minval = L_MIN(val5, val7);
2590
0
                        minval = L_MIN(minval + 1, val);
2591
0
                        SET_DATA_BYTE(lined, j, minval);
2592
0
                    }
2593
0
                }
2594
0
            }
2595
0
        } else {  /* d == 16 */
2596
                /* UL --> LR scan */
2597
0
            for (i = 1; i < imax; i++) {
2598
0
                lined = datad + i * wpld;
2599
0
                for (j = 1; j < jmax; j++) {
2600
0
                    if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
2601
0
                        val2 = GET_DATA_TWO_BYTES(lined - wpld, j);
2602
0
                        val4 = GET_DATA_TWO_BYTES(lined, j - 1);
2603
0
                        minval = L_MIN(val2, val4);
2604
0
                        minval = L_MIN(minval, 0xfffe);
2605
0
                        SET_DATA_TWO_BYTES(lined, j, minval + 1);
2606
0
                    }
2607
0
                }
2608
0
            }
2609
2610
                /* LR --> UL scan */
2611
0
            for (i = imax - 1; i > 0; i--) {
2612
0
                lined = datad + i * wpld;
2613
0
                for (j = jmax - 1; j > 0; j--) {
2614
0
                    if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
2615
0
                        val7 = GET_DATA_TWO_BYTES(lined + wpld, j);
2616
0
                        val5 = GET_DATA_TWO_BYTES(lined, j + 1);
2617
0
                        minval = L_MIN(val5, val7);
2618
0
                        minval = L_MIN(minval + 1, val);
2619
0
                        SET_DATA_TWO_BYTES(lined, j, minval);
2620
0
                    }
2621
0
                }
2622
0
            }
2623
0
        }
2624
0
        break;
2625
2626
0
    case 8:
2627
0
        if (d == 8) {
2628
                /* UL --> LR scan */
2629
0
            for (i = 1; i < imax; i++) {
2630
0
                lined = datad + i * wpld;
2631
0
                for (j = 1; j < jmax; j++) {
2632
0
                    if ((val = GET_DATA_BYTE(lined, j)) > 0) {
2633
0
                        val1 = GET_DATA_BYTE(lined - wpld, j - 1);
2634
0
                        val2 = GET_DATA_BYTE(lined - wpld, j);
2635
0
                        val3 = GET_DATA_BYTE(lined - wpld, j + 1);
2636
0
                        val4 = GET_DATA_BYTE(lined, j - 1);
2637
0
                        minval = L_MIN(val1, val2);
2638
0
                        minval = L_MIN(minval, val3);
2639
0
                        minval = L_MIN(minval, val4);
2640
0
                        minval = L_MIN(minval, 254);
2641
0
                        SET_DATA_BYTE(lined, j, minval + 1);
2642
0
                    }
2643
0
                }
2644
0
            }
2645
2646
                /* LR --> UL scan */
2647
0
            for (i = imax - 1; i > 0; i--) {
2648
0
                lined = datad + i * wpld;
2649
0
                for (j = jmax - 1; j > 0; j--) {
2650
0
                    if ((val = GET_DATA_BYTE(lined, j)) > 0) {
2651
0
                        val8 = GET_DATA_BYTE(lined + wpld, j + 1);
2652
0
                        val7 = GET_DATA_BYTE(lined + wpld, j);
2653
0
                        val6 = GET_DATA_BYTE(lined + wpld, j - 1);
2654
0
                        val5 = GET_DATA_BYTE(lined, j + 1);
2655
0
                        minval = L_MIN(val8, val7);
2656
0
                        minval = L_MIN(minval, val6);
2657
0
                        minval = L_MIN(minval, val5);
2658
0
                        minval = L_MIN(minval + 1, val);
2659
0
                        SET_DATA_BYTE(lined, j, minval);
2660
0
                    }
2661
0
                }
2662
0
            }
2663
0
        } else {  /* d == 16 */
2664
                /* UL --> LR scan */
2665
0
            for (i = 1; i < imax; i++) {
2666
0
                lined = datad + i * wpld;
2667
0
                for (j = 1; j < jmax; j++) {
2668
0
                    if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
2669
0
                        val1 = GET_DATA_TWO_BYTES(lined - wpld, j - 1);
2670
0
                        val2 = GET_DATA_TWO_BYTES(lined - wpld, j);
2671
0
                        val3 = GET_DATA_TWO_BYTES(lined - wpld, j + 1);
2672
0
                        val4 = GET_DATA_TWO_BYTES(lined, j - 1);
2673
0
                        minval = L_MIN(val1, val2);
2674
0
                        minval = L_MIN(minval, val3);
2675
0
                        minval = L_MIN(minval, val4);
2676
0
                        minval = L_MIN(minval, 0xfffe);
2677
0
                        SET_DATA_TWO_BYTES(lined, j, minval + 1);
2678
0
                    }
2679
0
                }
2680
0
            }
2681
2682
                /* LR --> UL scan */
2683
0
            for (i = imax - 1; i > 0; i--) {
2684
0
                lined = datad + i * wpld;
2685
0
                for (j = jmax - 1; j > 0; j--) {
2686
0
                    if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
2687
0
                        val8 = GET_DATA_TWO_BYTES(lined + wpld, j + 1);
2688
0
                        val7 = GET_DATA_TWO_BYTES(lined + wpld, j);
2689
0
                        val6 = GET_DATA_TWO_BYTES(lined + wpld, j - 1);
2690
0
                        val5 = GET_DATA_TWO_BYTES(lined, j + 1);
2691
0
                        minval = L_MIN(val8, val7);
2692
0
                        minval = L_MIN(minval, val6);
2693
0
                        minval = L_MIN(minval, val5);
2694
0
                        minval = L_MIN(minval + 1, val);
2695
0
                        SET_DATA_TWO_BYTES(lined, j, minval);
2696
0
                    }
2697
0
                }
2698
0
            }
2699
0
        }
2700
0
        break;
2701
2702
0
    default:
2703
0
        L_ERROR("connectivity must be 4 or 8\n", __func__);
2704
0
    }
2705
0
}
2706
2707
2708
/*-----------------------------------------------------------------------*
2709
 *                Seed spread (based on distance function)               *
2710
 *-----------------------------------------------------------------------*/
2711
/*!
2712
 * \brief   pixSeedspread()
2713
 *
2714
 * \param[in]    pixs           8 bpp
2715
 * \param[in]    connectivity   4 or 8
2716
 * \return  pixd, or NULL on error
2717
 *
2718
 * <pre>
2719
 * Notes:
2720
 *      (1) The raster/anti-raster method for implementing this filling
2721
 *          operation was suggested by Ray Smith.
2722
 *      (2) This takes an arbitrary set of nonzero pixels in pixs, which
2723
 *          can be sparse, and spreads (extrapolates) the values to
2724
 *          fill all the pixels in pixd with the nonzero value it is
2725
 *          closest to in pixs.  This is similar (though not completely
2726
 *          equivalent) to doing a Voronoi tiling of the image, with a
2727
 *          tile surrounding each pixel that has a nonzero value.
2728
 *          All pixels within a tile are then closer to its "central"
2729
 *          pixel than to any others.  Then assign the value of the
2730
 *          "central" pixel to each pixel in the tile.
2731
 *      (3) This is implemented by computing a distance function in parallel
2732
 *          with the fill.  The distance function uses free boundary
2733
 *          conditions (assumed maxval outside), and it controls the
2734
 *          propagation of the pixels in pixd away from the nonzero
2735
 *          (seed) values.  This is done in 2 traversals (raster/antiraster).
2736
 *          In the raster direction, whenever the distance function
2737
 *          is nonzero, the spread pixel takes on the value of its
2738
 *          predecessor that has the minimum distance value.  In the
2739
 *          antiraster direction, whenever the distance function is nonzero
2740
 *          and its value is replaced by a smaller value, the spread
2741
 *          pixel takes the value of the predecessor with the minimum
2742
 *          distance value.
2743
 *      (4) At boundaries where a pixel is equidistant from two
2744
 *          nearest nonzero (seed) pixels, the decision of which value
2745
 *          to use is arbitrary (greedy in search for minimum distance).
2746
 *          This can give rise to strange-looking results, particularly
2747
 *          for 4-connectivity where the L1 distance is computed from
2748
 *          steps in N,S,E and W directions (no diagonals).
2749
 * </pre>
2750
 */
2751
PIX *
2752
pixSeedspread(PIX     *pixs,
2753
              l_int32  connectivity)
2754
0
{
2755
0
l_int32    w, h, wplt, wplg;
2756
0
l_uint32  *datat, *datag;
2757
0
PIX       *pixm, *pixt, *pixg, *pixd;
2758
2759
0
    if (!pixs || pixGetDepth(pixs) != 8)
2760
0
        return (PIX *)ERROR_PTR("!pixs or pixs not 8 bpp", __func__, NULL);
2761
0
    if (connectivity != 4 && connectivity != 8)
2762
0
        return (PIX *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL);
2763
2764
        /* Add a 4 byte border to pixs.  This simplifies the computation. */
2765
0
    pixg = pixAddBorder(pixs, 4, 0);
2766
0
    pixGetDimensions(pixg, &w, &h, NULL);
2767
2768
        /* Initialize distance function pixt.  Threshold pixs to get
2769
         * a 0 at the seed points where the pixs pixel is nonzero, and
2770
         * a 1 at all points that need to be filled.  Use this as a
2771
         * mask to set a 1 in pixt at all non-seed points.  Also, set all
2772
         * pixt pixels in an interior boundary of width 1 to the
2773
         * maximum value.   For debugging, to view the distance function,
2774
         * use pixConvert16To8(pixt, L_LS_BYTE) on small images.  */
2775
0
    pixm = pixThresholdToBinary(pixg, 1);
2776
0
    pixt = pixCreate(w, h, 16);
2777
0
    pixSetMasked(pixt, pixm, 1);
2778
0
    pixRasterop(pixt, 0, 0, w, 1, PIX_SET, NULL, 0, 0);   /* top */
2779
0
    pixRasterop(pixt, 0, h - 1, w, 1, PIX_SET, NULL, 0, 0);   /* bot */
2780
0
    pixRasterop(pixt, 0, 0, 1, h, PIX_SET, NULL, 0, 0);   /* left */
2781
0
    pixRasterop(pixt, w - 1, 0, 1, h, PIX_SET, NULL, 0, 0);   /* right */
2782
0
    datat = pixGetData(pixt);
2783
0
    wplt = pixGetWpl(pixt);
2784
2785
        /* Do the interpolation and remove the border. */
2786
0
    datag = pixGetData(pixg);
2787
0
    wplg = pixGetWpl(pixg);
2788
0
    seedspreadLow(datag, w, h, wplg, datat, wplt, connectivity);
2789
0
    pixd = pixRemoveBorder(pixg, 4);
2790
2791
0
    pixDestroy(&pixm);
2792
0
    pixDestroy(&pixg);
2793
0
    pixDestroy(&pixt);
2794
0
    return pixd;
2795
0
}
2796
2797
2798
/*!
2799
 * \brief   seedspreadLow()
2800
 *
2801
 *    See pixSeedspread() for a brief description of the algorithm here.
2802
 */
2803
static void
2804
seedspreadLow(l_uint32  *datad,
2805
              l_int32    w,
2806
              l_int32    h,
2807
              l_int32    wpld,
2808
              l_uint32  *datat,
2809
              l_int32    wplt,
2810
              l_int32    connectivity)
2811
0
{
2812
0
l_int32    val1t, val2t, val3t, val4t, val5t, val6t, val7t, val8t;
2813
0
l_int32    i, j, imax, jmax, minval, valt, vald;
2814
0
l_uint32  *linet, *lined;
2815
2816
        /* One raster scan followed by one anti-raster scan.
2817
         * pixt is initialized to have 0 on pixels where the
2818
         * input is specified in pixd, and to have 1 on all
2819
         * other pixels.  We only change pixels in pixt and pixd
2820
         * that are non-zero in pixt. */
2821
0
    imax = h - 1;
2822
0
    jmax = w - 1;
2823
0
    switch (connectivity)
2824
0
    {
2825
0
    case 4:
2826
            /* UL --> LR scan */
2827
0
        for (i = 1; i < h; i++) {
2828
0
            linet = datat + i * wplt;
2829
0
            lined = datad + i * wpld;
2830
0
            for (j = 1; j < jmax; j++) {
2831
0
                if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
2832
0
                    val2t = GET_DATA_TWO_BYTES(linet - wplt, j);
2833
0
                    val4t = GET_DATA_TWO_BYTES(linet, j - 1);
2834
0
                    minval = L_MIN(val2t, val4t);
2835
0
                    minval = L_MIN(minval, 0xfffe);
2836
0
                    SET_DATA_TWO_BYTES(linet, j, minval + 1);
2837
0
                    if (val2t < val4t)
2838
0
                        vald = GET_DATA_BYTE(lined - wpld, j);
2839
0
                    else
2840
0
                        vald = GET_DATA_BYTE(lined, j - 1);
2841
0
                    SET_DATA_BYTE(lined, j, vald);
2842
0
                }
2843
0
            }
2844
0
        }
2845
2846
            /* LR --> UL scan */
2847
0
        for (i = imax - 1; i > 0; i--) {
2848
0
            linet = datat + i * wplt;
2849
0
            lined = datad + i * wpld;
2850
0
            for (j = jmax - 1; j > 0; j--) {
2851
0
                if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
2852
0
                    val7t = GET_DATA_TWO_BYTES(linet + wplt, j);
2853
0
                    val5t = GET_DATA_TWO_BYTES(linet, j + 1);
2854
0
                    minval = L_MIN(val5t, val7t);
2855
0
                    minval = L_MIN(minval + 1, valt);
2856
0
                    if (valt > minval) {  /* replace */
2857
0
                        SET_DATA_TWO_BYTES(linet, j, minval);
2858
0
                        if (val5t < val7t)
2859
0
                            vald = GET_DATA_BYTE(lined, j + 1);
2860
0
                        else
2861
0
                            vald = GET_DATA_BYTE(lined + wplt, j);
2862
0
                        SET_DATA_BYTE(lined, j, vald);
2863
0
                    }
2864
0
                }
2865
0
            }
2866
0
        }
2867
0
        break;
2868
0
    case 8:
2869
            /* UL --> LR scan */
2870
0
        for (i = 1; i < h; i++) {
2871
0
            linet = datat + i * wplt;
2872
0
            lined = datad + i * wpld;
2873
0
            for (j = 1; j < jmax; j++) {
2874
0
                if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
2875
0
                    val1t = GET_DATA_TWO_BYTES(linet - wplt, j - 1);
2876
0
                    val2t = GET_DATA_TWO_BYTES(linet - wplt, j);
2877
0
                    val3t = GET_DATA_TWO_BYTES(linet - wplt, j + 1);
2878
0
                    val4t = GET_DATA_TWO_BYTES(linet, j - 1);
2879
0
                    minval = L_MIN(val1t, val2t);
2880
0
                    minval = L_MIN(minval, val3t);
2881
0
                    minval = L_MIN(minval, val4t);
2882
0
                    minval = L_MIN(minval, 0xfffe);
2883
0
                    SET_DATA_TWO_BYTES(linet, j, minval + 1);
2884
0
                    if (minval == val1t)
2885
0
                        vald = GET_DATA_BYTE(lined - wpld, j - 1);
2886
0
                    else if (minval == val2t)
2887
0
                        vald = GET_DATA_BYTE(lined - wpld, j);
2888
0
                    else if (minval == val3t)
2889
0
                        vald = GET_DATA_BYTE(lined - wpld, j + 1);
2890
0
                    else  /* minval == val4t */
2891
0
                        vald = GET_DATA_BYTE(lined, j - 1);
2892
0
                    SET_DATA_BYTE(lined, j, vald);
2893
0
                }
2894
0
            }
2895
0
        }
2896
2897
            /* LR --> UL scan */
2898
0
        for (i = imax - 1; i > 0; i--) {
2899
0
            linet = datat + i * wplt;
2900
0
            lined = datad + i * wpld;
2901
0
            for (j = jmax - 1; j > 0; j--) {
2902
0
                if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
2903
0
                    val8t = GET_DATA_TWO_BYTES(linet + wplt, j + 1);
2904
0
                    val7t = GET_DATA_TWO_BYTES(linet + wplt, j);
2905
0
                    val6t = GET_DATA_TWO_BYTES(linet + wplt, j - 1);
2906
0
                    val5t = GET_DATA_TWO_BYTES(linet, j + 1);
2907
0
                    minval = L_MIN(val8t, val7t);
2908
0
                    minval = L_MIN(minval, val6t);
2909
0
                    minval = L_MIN(minval, val5t);
2910
0
                    minval = L_MIN(minval + 1, valt);
2911
0
                    if (valt > minval) {  /* replace */
2912
0
                        SET_DATA_TWO_BYTES(linet, j, minval);
2913
0
                        if (minval == val5t + 1)
2914
0
                            vald = GET_DATA_BYTE(lined, j + 1);
2915
0
                        else if (minval == val6t + 1)
2916
0
                            vald = GET_DATA_BYTE(lined + wpld, j - 1);
2917
0
                        else if (minval == val7t + 1)
2918
0
                            vald = GET_DATA_BYTE(lined + wpld, j);
2919
0
                        else  /* minval == val8t + 1 */
2920
0
                            vald = GET_DATA_BYTE(lined + wpld, j + 1);
2921
0
                        SET_DATA_BYTE(lined, j, vald);
2922
0
                    }
2923
0
                }
2924
0
            }
2925
0
        }
2926
0
        break;
2927
0
    default:
2928
0
        L_ERROR("connectivity must be 4 or 8\n", __func__);
2929
0
        break;
2930
0
    }
2931
0
}
2932
2933
2934
/*-----------------------------------------------------------------------*
2935
 *                              Local extrema                            *
2936
 *-----------------------------------------------------------------------*/
2937
/*!
2938
 * \brief   pixLocalExtrema()
2939
 *
2940
 * \param[in]    pixs       8 bpp
2941
 * \param[in]    maxmin     max allowed for the min in a 3x3 neighborhood;
2942
 *                          use 0 for default which is to have no upper bound
2943
 * \param[in]    minmax     min allowed for the max in a 3x3 neighborhood;
2944
 *                          use 0 for default which is to have no lower bound
2945
 * \param[out]   ppixmin    [optional] mask of local minima
2946
 * \param[out]   ppixmax    [optional] mask of local maxima
2947
 * \return  0 if OK, 1 on error
2948
 *
2949
 * <pre>
2950
 * Notes:
2951
 *      (1) This gives the actual local minima and maxima.
2952
 *          A local minimum is a pixel whose surrounding pixels all
2953
 *          have values at least as large, and likewise for a local
2954
 *          maximum.  For the local minima, %maxmin is the upper
2955
 *          bound for the value of pixs.  Likewise, for the local maxima,
2956
 *          %minmax is the lower bound for the value of pixs.
2957
 *      (2) The minima are found by starting with the erosion-and-equality
2958
 *          approach of pixSelectedLocalExtrema().  This is followed
2959
 *          by a qualification step, where each c.c. in the resulting
2960
 *          minimum mask is extracted, the pixels bordering it are
2961
 *          located, and they are queried.  If all of those pixels
2962
 *          are larger than the value of that minimum, it is a true
2963
 *          minimum and its c.c. is saved; otherwise the c.c. is
2964
 *          rejected.  Note that if a bordering pixel has the
2965
 *          same value as the minimum, it must then have a
2966
 *          neighbor that is smaller, so the component is not a
2967
 *          true minimum.
2968
 *      (3) The maxima are found by inverting the image and looking
2969
 *          for the minima there.
2970
 *      (4) The generated masks can be used as markers for
2971
 *          further operations.
2972
 * </pre>
2973
 */
2974
l_ok
2975
pixLocalExtrema(PIX     *pixs,
2976
                l_int32  maxmin,
2977
                l_int32  minmax,
2978
                PIX    **ppixmin,
2979
                PIX    **ppixmax)
2980
0
{
2981
0
PIX  *pixmin, *pixmax, *pixt1, *pixt2;
2982
2983
0
    if (!pixs || pixGetDepth(pixs) != 8)
2984
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
2985
0
    if (!ppixmin && !ppixmax)
2986
0
        return ERROR_INT("neither &pixmin, &pixmax are defined", __func__, 1);
2987
0
    if (maxmin <= 0) maxmin = 254;
2988
0
    if (minmax <= 0) minmax = 1;
2989
2990
0
    if (ppixmin) {
2991
0
        pixt1 = pixErodeGray(pixs, 3, 3);
2992
0
        pixmin = pixFindEqualValues(pixs, pixt1);
2993
0
        pixDestroy(&pixt1);
2994
0
        pixQualifyLocalMinima(pixs, pixmin, maxmin);
2995
0
        *ppixmin = pixmin;
2996
0
    }
2997
2998
0
    if (ppixmax) {
2999
0
        pixt1 = pixInvert(NULL, pixs);
3000
0
        pixt2 = pixErodeGray(pixt1, 3, 3);
3001
0
        pixmax = pixFindEqualValues(pixt1, pixt2);
3002
0
        pixDestroy(&pixt2);
3003
0
        pixQualifyLocalMinima(pixt1, pixmax, 255 - minmax);
3004
0
        *ppixmax = pixmax;
3005
0
        pixDestroy(&pixt1);
3006
0
    }
3007
3008
0
    return 0;
3009
0
}
3010
3011
3012
/*!
3013
 * \brief   pixQualifyLocalMinima()
3014
 *
3015
 * \param[in]    pixs     8 bpp image from which pixm has been extracted
3016
 * \param[in]    pixm     1 bpp mask of values equal to min in 3x3 neighborhood
3017
 * \param[in]    maxval   max allowed for the min in a 3x3 neighborhood;
3018
 *                        use 0 for default which is to have no upper bound
3019
 * \return  0 if OK, 1 on error
3020
 *
3021
 * <pre>
3022
 * Notes:
3023
 *      (1) This function acts in-place to remove all c.c. in pixm
3024
 *          that are not true local minima in pixs.  As seen in
3025
 *          pixLocalExtrema(), the input pixm are found by selecting those
3026
 *          pixels of pixs whose values do not change with a 3x3
3027
 *          grayscale erosion.  Here, we require that for each c.c.
3028
 *          in pixm, all pixels in pixs that correspond to the exterior
3029
 *          boundary pixels of the c.c. have values that are greater
3030
 *          than the value within the c.c.
3031
 *      (2) The maximum allowed value for each local minimum can be
3032
 *          bounded with %maxval.  Use 0 for default, which is to have
3033
 *          no upper bound (equivalent to maxval == 254).
3034
 * </pre>
3035
 */
3036
static l_int32
3037
pixQualifyLocalMinima(PIX     *pixs,
3038
                      PIX     *pixm,
3039
                      l_int32  maxval)
3040
0
{
3041
0
l_int32    n, i, j, k, x, y, w, h, xc, yc, wc, hc, xon, yon;
3042
0
l_int32    vals, wpls, wplc, ismin;
3043
0
l_uint32   val;
3044
0
l_uint32  *datas, *datac, *lines, *linec;
3045
0
BOXA      *boxa;
3046
0
PIX       *pix1, *pix2, *pix3;
3047
0
PIXA      *pixa;
3048
3049
0
    if (!pixs || pixGetDepth(pixs) != 8)
3050
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
3051
0
    if (!pixm || pixGetDepth(pixm) != 1)
3052
0
        return ERROR_INT("pixm not defined or not 1 bpp", __func__, 1);
3053
0
    if (maxval <= 0) maxval = 254;
3054
3055
0
    pixGetDimensions(pixs, &w, &h, NULL);
3056
0
    datas = pixGetData(pixs);
3057
0
    wpls = pixGetWpl(pixs);
3058
0
    boxa = pixConnComp(pixm, &pixa, 8);
3059
0
    n = pixaGetCount(pixa);
3060
0
    for (k = 0; k < n; k++) {
3061
0
        boxaGetBoxGeometry(boxa, k, &xc, &yc, &wc, &hc);
3062
0
        pix1 = pixaGetPix(pixa, k, L_COPY);
3063
0
        pix2 = pixAddBorder(pix1, 1, 0);
3064
0
        pix3 = pixDilateBrick(NULL, pix2, 3, 3);
3065
0
        pixXor(pix3, pix3, pix2);  /* exterior boundary pixels */
3066
0
        datac = pixGetData(pix3);
3067
0
        wplc = pixGetWpl(pix3);
3068
0
        nextOnPixelInRaster(pix1, 0, 0, &xon, &yon);
3069
0
        pixGetPixel(pixs, xc + xon, yc + yon, &val);
3070
0
        if (val > maxval) {  /* too large; erase */
3071
0
            pixRasterop(pixm, xc, yc, wc, hc, PIX_XOR, pix1, 0, 0);
3072
0
            pixDestroy(&pix1);
3073
0
            pixDestroy(&pix2);
3074
0
            pixDestroy(&pix3);
3075
0
            continue;
3076
0
        }
3077
0
        ismin = TRUE;
3078
3079
            /* Check all values in pixs that correspond to the exterior
3080
             * boundary pixels of the c.c. in pixm.  Verify that the
3081
             * value in the c.c. is always less. */
3082
0
        for (i = 0, y = yc - 1; i < hc + 2 && y >= 0 && y < h; i++, y++) {
3083
0
            lines = datas + y * wpls;
3084
0
            linec = datac + i * wplc;
3085
0
            for (j = 0, x = xc - 1; j < wc + 2 && x >= 0 && x < w; j++, x++) {
3086
0
                if (GET_DATA_BIT(linec, j)) {
3087
0
                    vals = GET_DATA_BYTE(lines, x);
3088
0
                    if (vals <= val) {  /* not a minimum! */
3089
0
                        ismin = FALSE;
3090
0
                        break;
3091
0
                    }
3092
0
                }
3093
0
            }
3094
0
            if (!ismin)
3095
0
                break;
3096
0
        }
3097
0
        if (!ismin)  /* erase it */
3098
0
            pixRasterop(pixm, xc, yc, wc, hc, PIX_XOR, pix1, 0, 0);
3099
0
        pixDestroy(&pix1);
3100
0
        pixDestroy(&pix2);
3101
0
        pixDestroy(&pix3);
3102
0
    }
3103
3104
0
    boxaDestroy(&boxa);
3105
0
    pixaDestroy(&pixa);
3106
0
    return 0;
3107
0
}
3108
3109
3110
/*!
3111
 * \brief   pixSelectedLocalExtrema()
3112
 *
3113
 * \param[in]    pixs       8 bpp
3114
 * \param[in]    mindist    -1 for keeping all pixels; >= 0 specifies distance
3115
 * \param[out]   ppixmin    mask of local minima
3116
 * \param[out]   ppixmax    mask of local maxima
3117
 * \return  0 if OK, 1 on error
3118
 *
3119
 * <pre>
3120
 * Notes:
3121
 *      (1) This selects those local 3x3 minima that are at least a
3122
 *          specified distance from the nearest local 3x3 maxima, and v.v.
3123
 *          for the selected set of local 3x3 maxima.
3124
 *          The local 3x3 minima is the set of pixels whose value equals
3125
 *          the value after a 3x3 brick erosion, and the local 3x3 maxima
3126
 *          is the set of pixels whose value equals the value after
3127
 *          a 3x3 brick dilation.
3128
 *      (2) mindist is the minimum distance allowed between
3129
 *          local 3x3 minima and local 3x3 maxima, in an 8-connected sense.
3130
 *          mindist == 1 keeps all pixels found in step 1.
3131
 *          mindist == 0 removes all pixels from each mask that are
3132
 *          both a local 3x3 minimum and a local 3x3 maximum.
3133
 *          mindist == 1 removes any local 3x3 minimum pixel that touches a
3134
 *          local 3x3 maximum pixel, and likewise for the local maxima.
3135
 *          To make the decision, visualize each local 3x3 minimum pixel
3136
 *          as being surrounded by a square of size (2 * mindist + 1)
3137
 *          on each side, such that no local 3x3 maximum pixel is within
3138
 *          that square; and v.v.
3139
 *      (3) The generated masks can be used as markers for further operations.
3140
 * </pre>
3141
 */
3142
l_ok
3143
pixSelectedLocalExtrema(PIX     *pixs,
3144
                        l_int32  mindist,
3145
                        PIX    **ppixmin,
3146
                        PIX    **ppixmax)
3147
0
{
3148
0
PIX  *pixmin, *pixmax, *pixt, *pixtmin, *pixtmax;
3149
3150
0
    if (!pixs || pixGetDepth(pixs) != 8)
3151
0
        return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1);
3152
0
    if (!ppixmin || !ppixmax)
3153
0
        return ERROR_INT("&pixmin and &pixmax not both defined", __func__, 1);
3154
3155
0
    pixt = pixErodeGray(pixs, 3, 3);
3156
0
    pixmin = pixFindEqualValues(pixs, pixt);
3157
0
    pixDestroy(&pixt);
3158
0
    pixt = pixDilateGray(pixs, 3, 3);
3159
0
    pixmax = pixFindEqualValues(pixs, pixt);
3160
0
    pixDestroy(&pixt);
3161
3162
        /* Remove all points that are within the prescribed distance
3163
         * from each other. */
3164
0
    if (mindist < 0) {  /* remove no points */
3165
0
        *ppixmin = pixmin;
3166
0
        *ppixmax = pixmax;
3167
0
    } else if (mindist == 0) {  /* remove points belonging to both sets */
3168
0
        pixt = pixAnd(NULL, pixmin, pixmax);
3169
0
        *ppixmin = pixSubtract(pixmin, pixmin, pixt);
3170
0
        *ppixmax = pixSubtract(pixmax, pixmax, pixt);
3171
0
        pixDestroy(&pixt);
3172
0
    } else {
3173
0
        pixtmin = pixDilateBrick(NULL, pixmin,
3174
0
                                 2 * mindist + 1, 2 * mindist + 1);
3175
0
        pixtmax = pixDilateBrick(NULL, pixmax,
3176
0
                                 2 * mindist + 1, 2 * mindist + 1);
3177
0
        *ppixmin = pixSubtract(pixmin, pixmin, pixtmax);
3178
0
        *ppixmax = pixSubtract(pixmax, pixmax, pixtmin);
3179
0
        pixDestroy(&pixtmin);
3180
0
        pixDestroy(&pixtmax);
3181
0
    }
3182
0
    return 0;
3183
0
}
3184
3185
3186
/*!
3187
 * \brief   pixFindEqualValues()
3188
 *
3189
 * \param[in]    pixs1    8 bpp
3190
 * \param[in]    pixs2    8 bpp
3191
 * \return  pixd 1 bpp mask, or NULL on error
3192
 *
3193
 * <pre>
3194
 * Notes:
3195
 *      (1) The two images are aligned at the UL corner, and the returned
3196
 *          image has ON pixels where the pixels in pixs1 and pixs2
3197
 *          have equal values.
3198
 * </pre>
3199
 */
3200
PIX *
3201
pixFindEqualValues(PIX  *pixs1,
3202
                   PIX  *pixs2)
3203
0
{
3204
0
l_int32    w1, h1, w2, h2, w, h;
3205
0
l_int32    i, j, val1, val2, wpls1, wpls2, wpld;
3206
0
l_uint32  *datas1, *datas2, *datad, *lines1, *lines2, *lined;
3207
0
PIX       *pixd;
3208
3209
0
    if (!pixs1 || pixGetDepth(pixs1) != 8)
3210
0
        return (PIX *)ERROR_PTR("pixs1 undefined or not 8 bpp", __func__, NULL);
3211
0
    if (!pixs2 || pixGetDepth(pixs2) != 8)
3212
0
        return (PIX *)ERROR_PTR("pixs2 undefined or not 8 bpp", __func__, NULL);
3213
0
    pixGetDimensions(pixs1, &w1, &h1, NULL);
3214
0
    pixGetDimensions(pixs2, &w2, &h2, NULL);
3215
0
    w = L_MIN(w1, w2);
3216
0
    h = L_MIN(h1, h2);
3217
0
    pixd = pixCreate(w, h, 1);
3218
0
    datas1 = pixGetData(pixs1);
3219
0
    datas2 = pixGetData(pixs2);
3220
0
    datad = pixGetData(pixd);
3221
0
    wpls1 = pixGetWpl(pixs1);
3222
0
    wpls2 = pixGetWpl(pixs2);
3223
0
    wpld = pixGetWpl(pixd);
3224
3225
0
    for (i = 0; i < h; i++) {
3226
0
        lines1 = datas1 + i * wpls1;
3227
0
        lines2 = datas2 + i * wpls2;
3228
0
        lined = datad + i * wpld;
3229
0
        for (j = 0; j < w; j++) {
3230
0
            val1 = GET_DATA_BYTE(lines1, j);
3231
0
            val2 = GET_DATA_BYTE(lines2, j);
3232
0
            if (val1 == val2)
3233
0
                SET_DATA_BIT(lined, j);
3234
0
        }
3235
0
    }
3236
3237
0
    return pixd;
3238
0
}
3239
3240
3241
/*-----------------------------------------------------------------------*
3242
 *             Selection of minima in mask connected components          *
3243
 *-----------------------------------------------------------------------*/
3244
/*!
3245
 * \brief   pixSelectMinInConnComp()
3246
 *
3247
 * \param[in]    pixs    8 bpp
3248
 * \param[in]    pixm    1 bpp
3249
 * \param[out]   ppta    pta of min pixel locations
3250
 * \param[out]   pnav    [optional] numa of minima values
3251
 * \return  0 if OK, 1 on error.
3252
 *
3253
 * <pre>
3254
 * Notes:
3255
 *      (1) For each 8 connected component in pixm, this finds
3256
 *          a pixel in pixs that has the lowest value, and saves
3257
 *          it in a Pta.  If several pixels in pixs have the same
3258
 *          minimum value, it picks the first one found.
3259
 *      (2) For a mask pixm of true local minima, all pixels in each
3260
 *          connected component have the same value in pixs, so it is
3261
 *          fastest to select one of them using a special seedfill
3262
 *          operation.  Not yet implemented.
3263
 * </pre>
3264
 */
3265
l_ok
3266
pixSelectMinInConnComp(PIX    *pixs,
3267
                       PIX    *pixm,
3268
                       PTA   **ppta,
3269
                       NUMA  **pnav)
3270
0
{
3271
0
l_int32    bx, by, bw, bh, i, j, c, n;
3272
0
l_int32    xs, ys, minx, miny, wpls, wplt, val, minval;
3273
0
l_uint32  *datas, *datat, *lines, *linet;
3274
0
BOXA      *boxa;
3275
0
NUMA      *nav;
3276
0
PIX       *pixt, *pixs2, *pixm2;
3277
0
PIXA      *pixa;
3278
0
PTA       *pta;
3279
3280
0
    if (!ppta)
3281
0
        return ERROR_INT("&pta not defined", __func__, 1);
3282
0
    *ppta = NULL;
3283
0
    if (pnav) *pnav = NULL;
3284
0
    if (!pixs || pixGetDepth(pixs) != 8)
3285
0
        return ERROR_INT("pixs undefined or not 8 bpp", __func__, 1);
3286
0
    if (!pixm || pixGetDepth(pixm) != 1)
3287
0
        return ERROR_INT("pixm undefined or not 1 bpp", __func__, 1);
3288
3289
        /* Crop to the min size if necessary */
3290
0
    if (pixCropToMatch(pixs, pixm, &pixs2, &pixm2)) {
3291
0
        pixDestroy(&pixs2);
3292
0
        pixDestroy(&pixm2);
3293
0
        return ERROR_INT("cropping failure", __func__, 1);
3294
0
    }
3295
3296
        /* Find value and location of min value pixel in each component */
3297
0
    boxa = pixConnComp(pixm2, &pixa, 8);
3298
0
    n = boxaGetCount(boxa);
3299
0
    pta = ptaCreate(n);
3300
0
    *ppta = pta;
3301
0
    nav = numaCreate(n);
3302
0
    datas = pixGetData(pixs2);
3303
0
    wpls = pixGetWpl(pixs2);
3304
0
    for (c = 0; c < n; c++) {
3305
0
        pixt = pixaGetPix(pixa, c, L_CLONE);
3306
0
        boxaGetBoxGeometry(boxa, c, &bx, &by, &bw, &bh);
3307
0
        if (bw == 1 && bh == 1) {
3308
0
            ptaAddPt(pta, bx, by);
3309
0
            numaAddNumber(nav, GET_DATA_BYTE(datas + by * wpls, bx));
3310
0
            pixDestroy(&pixt);
3311
0
            continue;
3312
0
        }
3313
0
        datat = pixGetData(pixt);
3314
0
        wplt = pixGetWpl(pixt);
3315
0
        minx = miny = 1000000;
3316
0
        minval = 256;
3317
0
        for (i = 0; i < bh; i++) {
3318
0
            ys = by + i;
3319
0
            lines = datas + ys * wpls;
3320
0
            linet = datat + i * wplt;
3321
0
            for (j = 0; j < bw; j++) {
3322
0
                xs = bx + j;
3323
0
                if (GET_DATA_BIT(linet, j)) {
3324
0
                    val = GET_DATA_BYTE(lines, xs);
3325
0
                    if (val < minval) {
3326
0
                        minval = val;
3327
0
                        minx = xs;
3328
0
                        miny = ys;
3329
0
                    }
3330
0
                }
3331
0
            }
3332
0
        }
3333
0
        ptaAddPt(pta, minx, miny);
3334
0
        numaAddNumber(nav, GET_DATA_BYTE(datas + miny * wpls, minx));
3335
0
        pixDestroy(&pixt);
3336
0
    }
3337
3338
0
    boxaDestroy(&boxa);
3339
0
    pixaDestroy(&pixa);
3340
0
    if (pnav)
3341
0
        *pnav = nav;
3342
0
    else
3343
0
        numaDestroy(&nav);
3344
0
    pixDestroy(&pixs2);
3345
0
    pixDestroy(&pixm2);
3346
0
    return 0;
3347
0
}
3348
3349
3350
/*-----------------------------------------------------------------------*
3351
 *            Removal of seeded connected components from a mask         *
3352
 *-----------------------------------------------------------------------*/
3353
/*!
3354
 * \brief   pixRemoveSeededComponents()
3355
 *
3356
 * \param[in]    pixd          [optional]; can be null or equal to pixm; 1 bpp
3357
 * \param[in]    pixs          1 bpp seed
3358
 * \param[in]    pixm          1 bpp filling mask
3359
 * \param[in]    connectivity  4 or 8
3360
 * \param[in]    bordersize    amount of border clearing
3361
 * \return  pixd, or NULL on error
3362
 *
3363
 * <pre>
3364
 * Notes:
3365
 *      (1) This removes each component in pixm for which there is
3366
 *          at least one seed in pixs.  If pixd == NULL, this returns
3367
 *          the result in a new pixd.  Otherwise, it is an in-place
3368
 *          operation on pixm.  In no situation is pixs altered,
3369
 *          because we do the filling with a copy of pixs.
3370
 *      (2) If bordersize > 0, it also clears all pixels within a
3371
 *          distance %bordersize of the edge of pixd.  This is here
3372
 *          because pixLocalExtrema() typically finds local minima
3373
 *          at the border.  Use %bordersize >= 2 to remove these.
3374
 * </pre>
3375
 */
3376
PIX *
3377
pixRemoveSeededComponents(PIX     *pixd,
3378
                          PIX     *pixs,
3379
                          PIX     *pixm,
3380
                          l_int32  connectivity,
3381
                          l_int32  bordersize)
3382
0
{
3383
0
PIX  *pixt;
3384
3385
0
    if (!pixs || pixGetDepth(pixs) != 1)
3386
0
        return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, pixd);
3387
0
    if (!pixm || pixGetDepth(pixm) != 1)
3388
0
        return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, pixd);
3389
0
    if (pixd && pixd != pixm)
3390
0
        return (PIX *)ERROR_PTR("operation not inplace", __func__, pixd);
3391
3392
0
    pixt = pixCopy(NULL, pixs);
3393
0
    pixSeedfillBinary(pixt, pixt, pixm, connectivity);
3394
0
    pixd = pixXor(pixd, pixm, pixt);
3395
0
    if (bordersize > 0)
3396
0
        pixSetOrClearBorder(pixd, bordersize, bordersize, bordersize,
3397
0
                            bordersize, PIX_CLR);
3398
0
    pixDestroy(&pixt);
3399
0
    return pixd;
3400
0
}