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 pix3.c
29  * <pre>
30  *
31  *    This file has these operations:
32  *
33  *      (1) Mask-directed operations
34  *      (2) Full-image bit-logical operations
35  *      (3) Foreground pixel counting operations on 1 bpp images
36  *      (4) Average and variance of pixel values
37  *      (5) Mirrored tiling of a smaller image
38  *
39  *
40  *    Masked operations
41  *           l_int32     pixSetMasked()
42  *           l_int32     pixSetMaskedGeneral()
43  *           l_int32     pixCombineMasked()
44  *           l_int32     pixCombineMaskedGeneral()
45  *           l_int32     pixPaintThroughMask()
46  *           PIX        *pixPaintSelfThroughMask()
47  *           PIX        *pixMakeMaskFromVal()
48  *           PIX        *pixMakeMaskFromLUT()
49  *           PIX        *pixMakeArbMaskFromRGB()
50  *           PIX        *pixSetUnderTransparency()
51  *           PIX        *pixMakeAlphaFromMask()
52  *           l_int32     pixGetColorNearMaskBoundary()
53  *
54  *    One and two-image boolean operations on arbitrary depth images
55  *           PIX        *pixInvert()
56  *           PIX        *pixOr()
57  *           PIX        *pixAnd()
58  *           PIX        *pixXor()
59  *           PIX        *pixSubtract()
60  *
61  *    Foreground pixel counting in 1 bpp images
62  *           l_int32     pixZero()
63  *           l_int32     pixForegroundFraction()
64  *           NUMA       *pixaCountPixels()
65  *           l_int32     pixCountPixels()
66  *           l_int32     pixCountPixelsInRect()
67  *           NUMA       *pixCountByRow()
68  *           NUMA       *pixCountByColumn()
69  *           NUMA       *pixCountPixelsByRow()
70  *           NUMA       *pixCountPixelsByColumn()
71  *           l_int32     pixCountPixelsInRow()
72  *           NUMA       *pixGetMomentByColumn()
73  *           l_int32     pixThresholdPixelSum()
74  *           l_int32    *makePixelSumTab8()
75  *           l_int32    *makePixelCentroidTab8()
76  *
77  *    Average of pixel values in gray images
78  *           NUMA       *pixAverageByRow()
79  *           NUMA       *pixAverageByColumn()
80  *           l_int32     pixAverageInRect()
81  *
82  *    Variance of pixel values in gray images
83  *           NUMA       *pixVarianceByRow()
84  *           NUMA       *pixVarianceByColumn()
85  *           l_int32     pixVarianceInRect()
86  *
87  *    Average of absolute value of pixel differences in gray images
88  *           NUMA       *pixAbsDiffByRow()
89  *           NUMA       *pixAbsDiffByColumn()
90  *           l_int32     pixAbsDiffInRect()
91  *           l_int32     pixAbsDiffOnLine()
92  *
93  *    Count of pixels with specific value
94  *           l_int32     pixCountArbInRect()
95  *
96  *    Mirrored tiling
97  *           PIX        *pixMirroredTiling()
98  *
99  *    Representative tile near but outside region
100  *           l_int32     pixFindRepCloseTile()
101  *
102  *    Static helper function
103  *           static BOXA    *findTileRegionsForSearch()
104  * </pre>
105  */
106 
107 #include <string.h>
108 #include <math.h>
109 #include "allheaders.h"
110 
111 static BOXA *findTileRegionsForSearch(BOX *box, l_int32 w, l_int32 h,
112                                       l_int32 searchdir, l_int32 mindist,
113                                       l_int32 tsize, l_int32 ntiles);
114 
115 #ifndef  NO_CONSOLE_IO
116 #define   EQUAL_SIZE_WARNING      0
117 #endif  /* ~NO_CONSOLE_IO */
118 
119 
120 /*-------------------------------------------------------------*
121  *                        Masked operations                    *
122  *-------------------------------------------------------------*/
123 /*!
124  * \brief   pixSetMasked()
125  *
126  * \param[in]    pixd 1, 2, 4, 8, 16 or 32 bpp; or colormapped
127  * \param[in]    pixm [optional] 1 bpp mask; no operation if NULL
128  * \param[in]    val value to set at each masked pixel
129  * \return  0 if OK; 1 on error
130  *
131  * <pre>
132  * Notes:
133  *      (1) In-place operation.
134  *      (2) NOTE: For cmapped images, this calls pixSetMaskedCmap().
135  *          %val must be the 32-bit color representation of the RGB pixel.
136  *          It is not the index into the colormap!
137  *      (2) If pixm == NULL, a warning is given.
138  *      (3) This is an implicitly aligned operation, where the UL
139  *          corners of pixd and pixm coincide.  A warning is
140  *          issued if the two image sizes differ significantly,
141  *          but the operation proceeds.
142  *      (4) Each pixel in pixd that co-locates with an ON pixel
143  *          in pixm is set to the specified input value.
144  *          Other pixels in pixd are not changed.
145  *      (5) You can visualize this as painting the color through
146  *          the mask, as a stencil.
147  *      (6) If you do not want to have the UL corners aligned,
148  *          use the function pixSetMaskedGeneral(), which requires
149  *          you to input the UL corner of pixm relative to pixd.
150  *      (7) Implementation details: see comments in pixPaintThroughMask()
151  *          for when we use rasterop to do the painting.
152  * </pre>
153  */
154 l_int32
pixSetMasked(PIX * pixd,PIX * pixm,l_uint32 val)155 pixSetMasked(PIX      *pixd,
156              PIX      *pixm,
157              l_uint32  val)
158 {
159 l_int32    wd, hd, wm, hm, w, h, d, wpld, wplm;
160 l_int32    i, j, rval, gval, bval;
161 l_uint32  *datad, *datam, *lined, *linem;
162 
163     PROCNAME("pixSetMasked");
164 
165     if (!pixd)
166         return ERROR_INT("pixd not defined", procName, 1);
167     if (!pixm) {
168         L_WARNING("no mask; nothing to do\n", procName);
169         return 0;
170     }
171     if (pixGetColormap(pixd)) {
172         extractRGBValues(val, &rval, &gval, &bval);
173         return pixSetMaskedCmap(pixd, pixm, 0, 0, rval, gval, bval);
174     }
175 
176     if (pixGetDepth(pixm) != 1)
177         return ERROR_INT("pixm not 1 bpp", procName, 1);
178     d = pixGetDepth(pixd);
179     if (d == 1)
180         val &= 1;
181     else if (d == 2)
182         val &= 3;
183     else if (d == 4)
184         val &= 0x0f;
185     else if (d == 8)
186         val &= 0xff;
187     else if (d == 16)
188         val &= 0xffff;
189     else if (d != 32)
190         return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
191     pixGetDimensions(pixm, &wm, &hm, NULL);
192 
193         /* If d == 1, use rasterop; it's about 25x faster */
194     if (d == 1) {
195         if (val == 0) {
196             PIX *pixmi = pixInvert(NULL, pixm);
197             pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmi, 0, 0);
198             pixDestroy(&pixmi);
199         } else {  /* val == 1 */
200             pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixm, 0, 0);
201         }
202         return 0;
203     }
204 
205         /* For d < 32, use rasterop for val == 0 (black); ~3x faster. */
206     if (d < 32 && val == 0) {
207         PIX *pixmd = pixUnpackBinary(pixm, d, 1);
208         pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmd, 0, 0);
209         pixDestroy(&pixmd);
210         return 0;
211     }
212 
213         /* For d < 32, use rasterop for val == maxval (white); ~3x faster. */
214     if (d < 32 && val == ((1 << d) - 1)) {
215         PIX *pixmd = pixUnpackBinary(pixm, d, 0);
216         pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixmd, 0, 0);
217         pixDestroy(&pixmd);
218         return 0;
219     }
220 
221     pixGetDimensions(pixd, &wd, &hd, &d);
222     w = L_MIN(wd, wm);
223     h = L_MIN(hd, hm);
224     if (L_ABS(wd - wm) > 7 || L_ABS(hd - hm) > 7)  /* allow a small tolerance */
225         L_WARNING("pixd and pixm sizes differ\n", procName);
226 
227     datad = pixGetData(pixd);
228     datam = pixGetData(pixm);
229     wpld = pixGetWpl(pixd);
230     wplm = pixGetWpl(pixm);
231     for (i = 0; i < h; i++) {
232         lined = datad + i * wpld;
233         linem = datam + i * wplm;
234         for (j = 0; j < w; j++) {
235             if (GET_DATA_BIT(linem, j)) {
236                 switch(d)
237                 {
238                 case 2:
239                     SET_DATA_DIBIT(lined, j, val);
240                     break;
241                 case 4:
242                     SET_DATA_QBIT(lined, j, val);
243                     break;
244                 case 8:
245                     SET_DATA_BYTE(lined, j, val);
246                     break;
247                 case 16:
248                     SET_DATA_TWO_BYTES(lined, j, val);
249                     break;
250                 case 32:
251                     *(lined + j) = val;
252                     break;
253                 default:
254                     return ERROR_INT("shouldn't get here", procName, 1);
255                 }
256             }
257         }
258     }
259 
260     return 0;
261 }
262 
263 
264 /*!
265  * \brief   pixSetMaskedGeneral()
266  *
267  * \param[in]    pixd 8, 16 or 32 bpp
268  * \param[in]    pixm [optional] 1 bpp mask; no operation if null
269  * \param[in]    val value to set at each masked pixel
270  * \param[in]    x, y location of UL corner of pixm relative to pixd;
271  *                    can be negative
272  * \return  0 if OK; 1 on error
273  *
274  * <pre>
275  * Notes:
276  *      (1) This is an in-place operation.
277  *      (2) Alignment is explicit.  If you want the UL corners of
278  *          the two images to be aligned, use pixSetMasked().
279  *      (3) A typical use would be painting through the foreground
280  *          of a small binary mask pixm, located somewhere on a
281  *          larger pixd.  Other pixels in pixd are not changed.
282  *      (4) You can visualize this as painting the color through
283  *          the mask, as a stencil.
284  *      (5) This uses rasterop to handle clipping and different depths of pixd.
285  *      (6) If pixd has a colormap, you should call pixPaintThroughMask().
286  *      (7) Why is this function here, if pixPaintThroughMask() does the
287  *          same thing, and does it more generally?  I've retained it here
288  *          to show how one can paint through a mask using only full
289  *          image rasterops, rather than pixel peeking in pixm and poking
290  *          in pixd.  It's somewhat baroque, but I found it amusing.
291  * </pre>
292  */
293 l_int32
pixSetMaskedGeneral(PIX * pixd,PIX * pixm,l_uint32 val,l_int32 x,l_int32 y)294 pixSetMaskedGeneral(PIX      *pixd,
295                     PIX      *pixm,
296                     l_uint32  val,
297                     l_int32   x,
298                     l_int32   y)
299 {
300 l_int32    wm, hm, d;
301 PIX       *pixmu, *pixc;
302 
303     PROCNAME("pixSetMaskedGeneral");
304 
305     if (!pixd)
306         return ERROR_INT("pixd not defined", procName, 1);
307     if (!pixm)  /* nothing to do */
308         return 0;
309 
310     d = pixGetDepth(pixd);
311     if (d != 8 && d != 16 && d != 32)
312         return ERROR_INT("pixd not 8, 16 or 32 bpp", procName, 1);
313     if (pixGetDepth(pixm) != 1)
314         return ERROR_INT("pixm not 1 bpp", procName, 1);
315 
316         /* Unpack binary to depth d, with inversion:  1 --> 0, 0 --> 0xff... */
317     if ((pixmu = pixUnpackBinary(pixm, d, 1)) == NULL)
318         return ERROR_INT("pixmu not made", procName, 1);
319 
320         /* Clear stenciled pixels in pixd */
321     pixGetDimensions(pixm, &wm, &hm, NULL);
322     pixRasterop(pixd, x, y, wm, hm, PIX_SRC & PIX_DST, pixmu, 0, 0);
323 
324         /* Generate image with requisite color */
325     if ((pixc = pixCreateTemplate(pixmu)) == NULL) {
326         pixDestroy(&pixmu);
327         return ERROR_INT("pixc not made", procName, 1);
328     }
329     pixSetAllArbitrary(pixc, val);
330 
331         /* Invert stencil mask, and paint color color into stencil */
332     pixInvert(pixmu, pixmu);
333     pixAnd(pixmu, pixmu, pixc);
334 
335         /* Finally, repaint stenciled pixels, with val, in pixd */
336     pixRasterop(pixd, x, y, wm, hm, PIX_SRC | PIX_DST, pixmu, 0, 0);
337 
338     pixDestroy(&pixmu);
339     pixDestroy(&pixc);
340     return 0;
341 }
342 
343 
344 /*!
345  * \brief   pixCombineMasked()
346  *
347  * \param[in]    pixd 1 bpp, 8 bpp gray or 32 bpp rgb; no cmap
348  * \param[in]    pixs 1 bpp, 8 bpp gray or 32 bpp rgb; no cmap
349  * \param[in]    pixm [optional] 1 bpp mask; no operation if NULL
350  * \return  0 if OK; 1 on error
351  *
352  * <pre>
353  * Notes:
354  *      (1) In-place operation; pixd is changed.
355  *      (2) This sets each pixel in pixd that co-locates with an ON
356  *          pixel in pixm to the corresponding value of pixs.
357  *      (3) pixs and pixd must be the same depth and not colormapped.
358  *      (4) All three input pix are aligned at the UL corner, and the
359  *          operation is clipped to the intersection of all three images.
360  *      (5) If pixm == NULL, it's a no-op.
361  *      (6) Implementation: see notes in pixCombineMaskedGeneral().
362  *          For 8 bpp selective masking, you might guess that it
363  *          would be faster to generate an 8 bpp version of pixm,
364  *          using pixConvert1To8(pixm, 0, 255), and then use a
365  *          general combine operation
366  *               d = (d & ~m) | (s & m)
367  *          on a word-by-word basis.  Not always.  The word-by-word
368  *          combine takes a time that is independent of the mask data.
369  *          If the mask is relatively sparse, the byte-check method
370  *          is actually faster!
371  * </pre>
372  */
373 l_int32
pixCombineMasked(PIX * pixd,PIX * pixs,PIX * pixm)374 pixCombineMasked(PIX  *pixd,
375                  PIX  *pixs,
376                  PIX  *pixm)
377 {
378 l_int32    w, h, d, ws, hs, ds, wm, hm, dm, wmin, hmin;
379 l_int32    wpl, wpls, wplm, i, j, val;
380 l_uint32  *data, *datas, *datam, *line, *lines, *linem;
381 PIX       *pixt;
382 
383     PROCNAME("pixCombineMasked");
384 
385     if (!pixm)  /* nothing to do */
386         return 0;
387     if (!pixd)
388         return ERROR_INT("pixd not defined", procName, 1);
389     if (!pixs)
390         return ERROR_INT("pixs not defined", procName, 1);
391     pixGetDimensions(pixd, &w, &h, &d);
392     pixGetDimensions(pixs, &ws, &hs, &ds);
393     pixGetDimensions(pixm, &wm, &hm, &dm);
394     if (d != ds)
395         return ERROR_INT("pixs and pixd depths differ", procName, 1);
396     if (dm != 1)
397         return ERROR_INT("pixm not 1 bpp", procName, 1);
398     if (d != 1 && d != 8 && d != 32)
399         return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1);
400     if (pixGetColormap(pixd) || pixGetColormap(pixs))
401         return ERROR_INT("pixs and/or pixd is cmapped", procName, 1);
402 
403         /* For d = 1, use rasterop.  pixt is the part from pixs, under
404          * the fg of pixm, that is to be combined with pixd.  We also
405          * use pixt to remove all fg of pixd that is under the fg of pixm.
406          * Then pixt and pixd are combined by ORing. */
407     wmin = L_MIN(w, L_MIN(ws, wm));
408     hmin = L_MIN(h, L_MIN(hs, hm));
409     if (d == 1) {
410         pixt = pixAnd(NULL, pixs, pixm);
411         pixRasterop(pixd, 0, 0, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
412                     pixm, 0, 0);
413         pixRasterop(pixd, 0, 0, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
414         pixDestroy(&pixt);
415         return 0;
416     }
417 
418     data = pixGetData(pixd);
419     datas = pixGetData(pixs);
420     datam = pixGetData(pixm);
421     wpl = pixGetWpl(pixd);
422     wpls = pixGetWpl(pixs);
423     wplm = pixGetWpl(pixm);
424     if (d == 8) {
425         for (i = 0; i < hmin; i++) {
426             line = data + i * wpl;
427             lines = datas + i * wpls;
428             linem = datam + i * wplm;
429             for (j = 0; j < wmin; j++) {
430                 if (GET_DATA_BIT(linem, j)) {
431                    val = GET_DATA_BYTE(lines, j);
432                    SET_DATA_BYTE(line, j, val);
433                 }
434             }
435         }
436     } else {  /* d == 32 */
437         for (i = 0; i < hmin; i++) {
438             line = data + i * wpl;
439             lines = datas + i * wpls;
440             linem = datam + i * wplm;
441             for (j = 0; j < wmin; j++) {
442                 if (GET_DATA_BIT(linem, j))
443                    line[j] = lines[j];
444             }
445         }
446     }
447 
448     return 0;
449 }
450 
451 
452 /*!
453  * \brief   pixCombineMaskedGeneral()
454  *
455  * \param[in]    pixd 1 bpp, 8 bpp gray or 32 bpp rgb
456  * \param[in]    pixs 1 bpp, 8 bpp gray or 32 bpp rgb
457  * \param[in]    pixm [optional] 1 bpp mask
458  * \param[in]    x, y origin of pixs and pixm relative to pixd; can be negative
459  * \return  0 if OK; 1 on error
460  *
461  * <pre>
462  * Notes:
463  *      (1) In-place operation; pixd is changed.
464  *      (2) This is a generalized version of pixCombinedMasked(), where
465  *          the source and mask can be placed at the same (arbitrary)
466  *          location relative to pixd.
467  *      (3) pixs and pixd must be the same depth and not colormapped.
468  *      (4) The UL corners of both pixs and pixm are aligned with
469  *          the point (x, y) of pixd, and the operation is clipped to
470  *          the intersection of all three images.
471  *      (5) If pixm == NULL, it's a no-op.
472  *      (6) Implementation.  There are two ways to do these.  In the first,
473  *          we use rasterop, ORing the part of pixs under the mask
474  *          with pixd (which has been appropriately cleared there first).
475  *          In the second, the mask is used one pixel at a time to
476  *          selectively replace pixels of pixd with those of pixs.
477  *          Here, we use rasterop for 1 bpp and pixel-wise replacement
478  *          for 8 and 32 bpp.  To use rasterop for 8 bpp, for example,
479  *          we must first generate an 8 bpp version of the mask.
480  *          The code is simple:
481  *
482  *             Pix *pixm8 = pixConvert1To8(NULL, pixm, 0, 255);
483  *             Pix *pixt = pixAnd(NULL, pixs, pixm8);
484  *             pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
485  *                         pixm8, 0, 0);
486  *             pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST,
487  *                         pixt, 0, 0);
488  *             pixDestroy(&pixt);
489  *             pixDestroy(&pixm8);
490  * </pre>
491  */
492 l_int32
pixCombineMaskedGeneral(PIX * pixd,PIX * pixs,PIX * pixm,l_int32 x,l_int32 y)493 pixCombineMaskedGeneral(PIX      *pixd,
494                         PIX      *pixs,
495                         PIX      *pixm,
496                         l_int32   x,
497                         l_int32   y)
498 {
499 l_int32    d, w, h, ws, hs, ds, wm, hm, dm, wmin, hmin;
500 l_int32    wpl, wpls, wplm, i, j, val;
501 l_uint32  *data, *datas, *datam, *line, *lines, *linem;
502 PIX       *pixt;
503 
504     PROCNAME("pixCombineMaskedGeneral");
505 
506     if (!pixm)  /* nothing to do */
507         return 0;
508     if (!pixd)
509         return ERROR_INT("pixd not defined", procName, 1);
510     if (!pixs)
511         return ERROR_INT("pixs not defined", procName, 1);
512     pixGetDimensions(pixd, &w, &h, &d);
513     pixGetDimensions(pixs, &ws, &hs, &ds);
514     pixGetDimensions(pixm, &wm, &hm, &dm);
515     if (d != ds)
516         return ERROR_INT("pixs and pixd depths differ", procName, 1);
517     if (dm != 1)
518         return ERROR_INT("pixm not 1 bpp", procName, 1);
519     if (d != 1 && d != 8 && d != 32)
520         return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1);
521     if (pixGetColormap(pixd) || pixGetColormap(pixs))
522         return ERROR_INT("pixs and/or pixd is cmapped", procName, 1);
523 
524         /* For d = 1, use rasterop.  pixt is the part from pixs, under
525          * the fg of pixm, that is to be combined with pixd.  We also
526          * use pixt to remove all fg of pixd that is under the fg of pixm.
527          * Then pixt and pixd are combined by ORing. */
528     wmin = L_MIN(ws, wm);
529     hmin = L_MIN(hs, hm);
530     if (d == 1) {
531         pixt = pixAnd(NULL, pixs, pixm);
532         pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
533                     pixm, 0, 0);
534         pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
535         pixDestroy(&pixt);
536         return 0;
537     }
538 
539     wpl = pixGetWpl(pixd);
540     data = pixGetData(pixd);
541     wpls = pixGetWpl(pixs);
542     datas = pixGetData(pixs);
543     wplm = pixGetWpl(pixm);
544     datam = pixGetData(pixm);
545 
546     for (i = 0; i < hmin; i++) {
547         if (y + i < 0 || y + i >= h) continue;
548         line = data + (y + i) * wpl;
549         lines = datas + i * wpls;
550         linem = datam + i * wplm;
551         for (j = 0; j < wmin; j++) {
552             if (x + j < 0 || x + j >= w) continue;
553             if (GET_DATA_BIT(linem, j)) {
554                 switch (d)
555                 {
556                 case 8:
557                     val = GET_DATA_BYTE(lines, j);
558                     SET_DATA_BYTE(line, x + j, val);
559                     break;
560                 case 32:
561                     *(line + x + j) = *(lines + j);
562                     break;
563                 default:
564                     return ERROR_INT("shouldn't get here", procName, 1);
565                 }
566             }
567         }
568     }
569 
570     return 0;
571 }
572 
573 
574 /*!
575  * \brief   pixPaintThroughMask()
576  *
577  * \param[in]    pixd 1, 2, 4, 8, 16 or 32 bpp; or colormapped
578  * \param[in]    pixm [optional] 1 bpp mask
579  * \param[in]    x, y origin of pixm relative to pixd; can be negative
580  * \param[in]    val pixel value to set at each masked pixel
581  * \return  0 if OK; 1 on error
582  *
583  * <pre>
584  * Notes:
585  *      (1) In-place operation.  Calls pixSetMaskedCmap() for colormapped
586  *          images.
587  *      (2) For 1, 2, 4, 8 and 16 bpp gray, we take the appropriate
588  *          number of least significant bits of val.
589  *      (3) If pixm == NULL, it's a no-op.
590  *      (4) The mask origin is placed at (x,y) on pixd, and the
591  *          operation is clipped to the intersection of rectangles.
592  *      (5) For rgb, the components in val are in the canonical locations,
593  *          with red in location COLOR_RED, etc.
594  *      (6) Implementation detail 1:
595  *          For painting with val == 0 or val == maxval, you can use rasterop.
596  *          If val == 0, invert the mask so that it's 0 over the region
597  *          into which you want to write, and use PIX_SRC & PIX_DST to
598  *          clear those pixels.  To write with val = maxval (all 1's),
599  *          use PIX_SRC | PIX_DST to set all bits under the mask.
600  *      (7) Implementation detail 2:
601  *          The rasterop trick can be used for depth > 1 as well.
602  *          For val == 0, generate the mask for depth d from the binary
603  *          mask using
604  *              pixmd = pixUnpackBinary(pixm, d, 1);
605  *          and use pixRasterop() with PIX_MASK.  For val == maxval,
606  *              pixmd = pixUnpackBinary(pixm, d, 0);
607  *          and use pixRasterop() with PIX_PAINT.
608  *          But note that if d == 32 bpp, it is about 3x faster to use
609  *          the general implementation (not pixRasterop()).
610  *      (8) Implementation detail 3:
611  *          It might be expected that the switch in the inner loop will
612  *          cause large branching delays and should be avoided.
613  *          This is not the case, because the entrance is always the
614  *          same and the compiler can correctly predict the jump.
615  * </pre>
616  */
617 l_int32
pixPaintThroughMask(PIX * pixd,PIX * pixm,l_int32 x,l_int32 y,l_uint32 val)618 pixPaintThroughMask(PIX      *pixd,
619                     PIX      *pixm,
620                     l_int32   x,
621                     l_int32   y,
622                     l_uint32  val)
623 {
624 l_int32    d, w, h, wm, hm, wpl, wplm, i, j, rval, gval, bval;
625 l_uint32  *data, *datam, *line, *linem;
626 
627     PROCNAME("pixPaintThroughMask");
628 
629     if (!pixm)  /* nothing to do */
630         return 0;
631     if (!pixd)
632         return ERROR_INT("pixd not defined", procName, 1);
633     if (pixGetColormap(pixd)) {
634         extractRGBValues(val, &rval, &gval, &bval);
635         return pixSetMaskedCmap(pixd, pixm, x, y, rval, gval, bval);
636     }
637 
638     if (pixGetDepth(pixm) != 1)
639         return ERROR_INT("pixm not 1 bpp", procName, 1);
640     d = pixGetDepth(pixd);
641     if (d == 1)
642         val &= 1;
643     else if (d == 2)
644         val &= 3;
645     else if (d == 4)
646         val &= 0x0f;
647     else if (d == 8)
648         val &= 0xff;
649     else if (d == 16)
650         val &= 0xffff;
651     else if (d != 32)
652         return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
653     pixGetDimensions(pixm, &wm, &hm, NULL);
654 
655         /* If d == 1, use rasterop; it's about 25x faster. */
656     if (d == 1) {
657         if (val == 0) {
658             PIX *pixmi = pixInvert(NULL, pixm);
659             pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmi, 0, 0);
660             pixDestroy(&pixmi);
661         } else {  /* val == 1 */
662             pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixm, 0, 0);
663         }
664         return 0;
665     }
666 
667         /* For d < 32, use rasterop if val == 0 (black); ~3x faster. */
668     if (d < 32 && val == 0) {
669         PIX *pixmd = pixUnpackBinary(pixm, d, 1);
670         pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmd, 0, 0);
671         pixDestroy(&pixmd);
672         return 0;
673     }
674 
675         /* For d < 32, use rasterop if val == maxval (white); ~3x faster. */
676     if (d < 32 && val == ((1 << d) - 1)) {
677         PIX *pixmd = pixUnpackBinary(pixm, d, 0);
678         pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixmd, 0, 0);
679         pixDestroy(&pixmd);
680         return 0;
681     }
682 
683         /* All other cases */
684     pixGetDimensions(pixd, &w, &h, NULL);
685     wpl = pixGetWpl(pixd);
686     data = pixGetData(pixd);
687     wplm = pixGetWpl(pixm);
688     datam = pixGetData(pixm);
689     for (i = 0; i < hm; i++) {
690         if (y + i < 0 || y + i >= h) continue;
691         line = data + (y + i) * wpl;
692         linem = datam + i * wplm;
693         for (j = 0; j < wm; j++) {
694             if (x + j < 0 || x + j >= w) continue;
695             if (GET_DATA_BIT(linem, j)) {
696                 switch (d)
697                 {
698                 case 2:
699                     SET_DATA_DIBIT(line, x + j, val);
700                     break;
701                 case 4:
702                     SET_DATA_QBIT(line, x + j, val);
703                     break;
704                 case 8:
705                     SET_DATA_BYTE(line, x + j, val);
706                     break;
707                 case 16:
708                     SET_DATA_TWO_BYTES(line, x + j, val);
709                     break;
710                 case 32:
711                     *(line + x + j) = val;
712                     break;
713                 default:
714                     return ERROR_INT("shouldn't get here", procName, 1);
715                 }
716             }
717         }
718     }
719 
720     return 0;
721 }
722 
723 
724 /*!
725  * \brief   pixPaintSelfThroughMask()
726  *
727  * \param[in]    pixd 8 bpp gray or 32 bpp rgb; not colormapped
728  * \param[in]    pixm 1 bpp mask
729  * \param[in]    x, y origin of pixm relative to pixd; must not be negative
730  * \param[in]    searchdir L_HORIZ, L_VERT or L_BOTH_DIRECTIONS
731  * \param[in]    mindist min distance of nearest tile edge to box; >= 0
732  * \param[in]    tilesize requested size for tiling; may be reduced
733  * \param[in]    ntiles number of tiles tested in each row/column
734  * \param[in]    distblend distance outside the fg used for blending with pixs
735  * \return  0 if OK; 1 on error
736  *
737  * <pre>
738  * Notes:
739  *      (1) In-place operation; pixd is changed.
740  *      (2) If pixm == NULL, it's a no-op.
741  *      (3) The mask origin is placed at (x,y) on pixd, and the
742  *          operation is clipped to the intersection of pixd and the
743  *          fg of the mask.
744  *      (4) %tsize is the the requested size for tiling.  The actual
745  *          actual size for each c.c. will be bounded by the minimum
746  *          dimension of the c.c.
747  *      (5) For %mindist, %searchdir and %ntiles, see pixFindRepCloseTile().
748  *          They determine the set of possible tiles that can be used
749  *          to build a larger mirrored tile to paint onto pixd through
750  *          the c.c. of pixm.
751  *      (6) %distblend is used for alpha blending.  It is only applied
752  *          if there is exactly one c.c. in the mask.  Use distblend == 0
753  *          to skip blending and just paint through the 1 bpp mask.
754  *      (7) To apply blending to more than 1 component, call this function
755  *          repeatedly with %pixm, %x and %y representing one component of
756  *          the mask each time.  This would be done as follows, for an
757  *          underlying image pixs and mask pixm of components to fill:
758  *              Boxa *boxa = pixConnComp(pixm, &pixa, 8);
759  *              n = boxaGetCount(boxa);
760  *              for (i = 0; i < n; i++) {
761  *                  Pix *pix = pixaGetPix(pixa, i, L_CLONE);
762  *                  Box *box = pixaGetBox(pixa, i, L_CLONE);
763  *                  boxGetGeometry(box, &bx, &by, &bw, &bh);
764  *                  pixPaintSelfThroughMask(pixs, pix, bx, by, searchdir,
765  *                                     mindist, tilesize, ntiles, distblend);
766  *                  pixDestroy(&pix);
767  *                  boxDestroy(&box);
768  *              }
769  *              pixaDestroy(&pixa);
770  *              boxaDestroy(&boxa);
771  *      (8) If no tiles can be found, this falls back to estimating the
772  *          color near the boundary of the region to be textured.
773  *      (9) This can be used to replace the pixels in some regions of
774  *          an image by selected neighboring pixels.  The mask represents
775  *          the pixels to be replaced.  For each connected component in
776  *          the mask, this function selects up to two tiles of neighboring
777  *          pixels to be used for replacement of pixels represented by
778  *          the component (i.e., under the FG of that component in the mask).
779  *          After selection, mirror replication is used to generate an
780  *          image that is large enough to cover the component.  Alpha
781  *          blending can also be used outside of the component, but near the
782  *          edge, to blur the transition between painted and original pixels.
783  * </pre>
784  */
785 l_int32
pixPaintSelfThroughMask(PIX * pixd,PIX * pixm,l_int32 x,l_int32 y,l_int32 searchdir,l_int32 mindist,l_int32 tilesize,l_int32 ntiles,l_int32 distblend)786 pixPaintSelfThroughMask(PIX      *pixd,
787                         PIX      *pixm,
788                         l_int32   x,
789                         l_int32   y,
790                         l_int32   searchdir,
791                         l_int32   mindist,
792                         l_int32   tilesize,
793                         l_int32   ntiles,
794                         l_int32   distblend)
795 {
796 l_int32   w, h, d, wm, hm, dm, i, n, bx, by, bw, bh, edgeblend, retval, minside;
797 l_uint32  pixval;
798 BOX      *box, *boxv, *boxh;
799 BOXA     *boxa;
800 PIX      *pixf, *pixv, *pixh, *pix1, *pix2, *pix3, *pix4, *pix5;
801 PIXA     *pixa;
802 
803     PROCNAME("pixPaintSelfThroughMask");
804 
805     if (!pixm)  /* nothing to do */
806         return 0;
807     if (!pixd)
808         return ERROR_INT("pixd not defined", procName, 1);
809     if (pixGetColormap(pixd) != NULL)
810         return ERROR_INT("pixd has colormap", procName, 1);
811     pixGetDimensions(pixd, &w, &h, &d);
812     if (d != 8 && d != 32)
813         return ERROR_INT("pixd not 8 or 32 bpp", procName, 1);
814     pixGetDimensions(pixm, &wm, &hm, &dm);
815     if (dm != 1)
816         return ERROR_INT("pixm not 1 bpp", procName, 1);
817     if (x < 0 || y < 0)
818         return ERROR_INT("x and y must be non-negative", procName, 1);
819     if (searchdir != L_HORIZ && searchdir != L_VERT &&
820         searchdir != L_BOTH_DIRECTIONS)
821         return ERROR_INT("invalid searchdir", procName, 1);
822     if (tilesize < 2)
823         return ERROR_INT("tilesize must be >= 2", procName, 1);
824     if (distblend < 0)
825         return ERROR_INT("distblend must be >= 0", procName, 1);
826 
827         /* Embed mask in full sized mask */
828     if (wm < w || hm < h) {
829         pixf = pixCreate(w, h, 1);
830         pixRasterop(pixf, x, y, wm, hm, PIX_SRC, pixm, 0, 0);
831     } else {
832         pixf = pixCopy(NULL, pixm);
833     }
834 
835         /* Get connected components of mask */
836     boxa = pixConnComp(pixf, &pixa, 8);
837     if ((n = pixaGetCount(pixa)) == 0) {
838         L_WARNING("no fg in mask\n", procName);
839         pixDestroy(&pixf);
840         pixaDestroy(&pixa);
841         boxaDestroy(&boxa);
842         return 1;
843     }
844     boxaDestroy(&boxa);
845 
846         /* For each c.c., generate one or two representative tiles for
847          * texturizing and apply through the mask.  The input 'tilesize'
848          * is the requested value.  Note that if there is exactly one
849          * component, and blending at the edge is requested, an alpha mask
850          * is generated, which is larger than the bounding box of the c.c. */
851     edgeblend = (n == 1 && distblend > 0) ? 1 : 0;
852     if (distblend > 0 && n > 1)
853         L_WARNING("%d components; can not blend at edges\n", procName, n);
854     retval = 0;
855     for (i = 0; i < n; i++) {
856         if (edgeblend) {
857             pix1 = pixMakeAlphaFromMask(pixf, distblend, &box);
858         } else {
859             pix1 = pixaGetPix(pixa, i, L_CLONE);
860             box = pixaGetBox(pixa, i, L_CLONE);
861         }
862         boxGetGeometry(box, &bx, &by, &bw, &bh);
863         minside = L_MIN(bw, bh);
864 
865         boxh = boxv = NULL;
866         if (searchdir == L_HORIZ || searchdir == L_BOTH_DIRECTIONS) {
867             pixFindRepCloseTile(pixd, box, L_HORIZ, mindist,
868                                 L_MIN(minside, tilesize), ntiles, &boxh, 0);
869         }
870         if (searchdir == L_VERT || searchdir == L_BOTH_DIRECTIONS) {
871             pixFindRepCloseTile(pixd, box, L_VERT, mindist,
872                                 L_MIN(minside, tilesize), ntiles, &boxv, 0);
873         }
874         if (!boxh && !boxv) {
875             L_WARNING("tile region not selected; paint color near boundary\n",
876                       procName);
877             pixDestroy(&pix1);
878             pix1 = pixaGetPix(pixa, i, L_CLONE);
879             pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL);
880             retval = pixGetColorNearMaskBoundary(pixd, pixm, box, distblend,
881                                                  &pixval, 0);
882             pixSetMaskedGeneral(pixd, pix1, pixval, bx, by);
883             pixDestroy(&pix1);
884             boxDestroy(&box);
885             continue;
886         }
887 
888             /* Extract the selected squares from pixd */
889         pixh = (boxh) ? pixClipRectangle(pixd, boxh, NULL) : NULL;
890         pixv = (boxv) ? pixClipRectangle(pixd, boxv, NULL) : NULL;
891         if (pixh && pixv)
892             pix2 = pixBlend(pixh, pixv, 0, 0, 0.5);
893         else if (pixh)
894             pix2 = pixClone(pixh);
895         else  /* pixv */
896             pix2 = pixClone(pixv);
897         pixDestroy(&pixh);
898         pixDestroy(&pixv);
899         boxDestroy(&boxh);
900         boxDestroy(&boxv);
901 
902             /* Generate an image the size of the b.b. of the c.c.,
903              * possibly extended by the blending distance, which
904              * is then either painted through the c.c. mask or
905              * blended using the alpha mask for that c.c.  */
906         pix3 = pixMirroredTiling(pix2, bw, bh);
907         if (edgeblend) {
908             pix4 = pixClipRectangle(pixd, box, NULL);
909             pix5 = pixBlendWithGrayMask(pix4, pix3, pix1, 0, 0);
910             pixRasterop(pixd, bx, by, bw, bh, PIX_SRC, pix5, 0, 0);
911             pixDestroy(&pix4);
912             pixDestroy(&pix5);
913         } else {
914             pixCombineMaskedGeneral(pixd, pix3, pix1, bx, by);
915         }
916         pixDestroy(&pix1);
917         pixDestroy(&pix2);
918         pixDestroy(&pix3);
919         boxDestroy(&box);
920     }
921 
922     pixaDestroy(&pixa);
923     pixDestroy(&pixf);
924     return retval;
925 }
926 
927 
928 /*!
929  * \brief   pixMakeMaskFromVal()
930  *
931  * \param[in]    pixs 2, 4 or 8 bpp; can be colormapped
932  * \param[in]    val  pixel value
933  * \return  pixd 1 bpp mask, or NULL on error
934  *
935  * <pre>
936  * Notes:
937  *      (1) This generates a 1 bpp mask image, where a 1 is written in
938  *          the mask for each pixel in pixs that has a value %val.
939  *      (2) If no pixels have the value, an empty mask is generated.
940  * </pre>
941  */
942 PIX *
pixMakeMaskFromVal(PIX * pixs,l_int32 val)943 pixMakeMaskFromVal(PIX     *pixs,
944                    l_int32  val)
945 {
946 l_int32    w, h, d, i, j, sval, wpls, wpld;
947 l_uint32  *datas, *datad, *lines, *lined;
948 PIX       *pixd;
949 
950     PROCNAME("pixMakeMaskFromVal");
951 
952     if (!pixs)
953         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
954     pixGetDimensions(pixs, &w, &h, &d);
955     if (d != 2 && d != 4 && d != 8)
956         return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", procName, NULL);
957 
958     pixd = pixCreate(w, h, 1);
959     pixCopyResolution(pixd, pixs);
960     pixCopyInputFormat(pixd, pixs);
961     datas = pixGetData(pixs);
962     datad = pixGetData(pixd);
963     wpls = pixGetWpl(pixs);
964     wpld = pixGetWpl(pixd);
965     for (i = 0; i < h; i++) {
966         lines = datas + i * wpls;
967         lined = datad + i * wpld;
968         for (j = 0; j < w; j++) {
969             if (d == 2)
970                 sval = GET_DATA_DIBIT(lines, j);
971             else if (d == 4)
972                 sval = GET_DATA_QBIT(lines, j);
973             else  /* d == 8 */
974                 sval = GET_DATA_BYTE(lines, j);
975             if (sval == val)
976                 SET_DATA_BIT(lined, j);
977         }
978     }
979 
980     return pixd;
981 }
982 
983 
984 /*!
985  * \brief   pixMakeMaskFromLUT()
986  *
987  * \param[in]    pixs 2, 4 or 8 bpp; can be colormapped
988  * \param[in]    tab 256-entry LUT; 1 means to write to mask
989  * \return  pixd 1 bpp mask, or NULL on error
990  *
991  * <pre>
992  * Notes:
993  *      (1) This generates a 1 bpp mask image, where a 1 is written in
994  *          the mask for each pixel in pixs that has a value corresponding
995  *          to a 1 in the LUT.
996  *      (2) The LUT should be of size 256.
997  * </pre>
998  */
999 PIX *
pixMakeMaskFromLUT(PIX * pixs,l_int32 * tab)1000 pixMakeMaskFromLUT(PIX      *pixs,
1001                    l_int32  *tab)
1002 {
1003 l_int32    w, h, d, i, j, val, wpls, wpld;
1004 l_uint32  *datas, *datad, *lines, *lined;
1005 PIX       *pixd;
1006 
1007     PROCNAME("pixMakeMaskFromLUT");
1008 
1009     if (!pixs)
1010         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1011     if (!tab)
1012         return (PIX *)ERROR_PTR("tab not defined", procName, NULL);
1013     pixGetDimensions(pixs, &w, &h, &d);
1014     if (d != 2 && d != 4 && d != 8)
1015         return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", procName, NULL);
1016 
1017     pixd = pixCreate(w, h, 1);
1018     pixCopyResolution(pixd, pixs);
1019     pixCopyInputFormat(pixd, pixs);
1020     datas = pixGetData(pixs);
1021     datad = pixGetData(pixd);
1022     wpls = pixGetWpl(pixs);
1023     wpld = pixGetWpl(pixd);
1024     for (i = 0; i < h; i++) {
1025         lines = datas + i * wpls;
1026         lined = datad + i * wpld;
1027         for (j = 0; j < w; j++) {
1028             if (d == 2)
1029                 val = GET_DATA_DIBIT(lines, j);
1030             else if (d == 4)
1031                 val = GET_DATA_QBIT(lines, j);
1032             else  /* d == 8 */
1033                 val = GET_DATA_BYTE(lines, j);
1034             if (tab[val] == 1)
1035                 SET_DATA_BIT(lined, j);
1036         }
1037     }
1038 
1039     return pixd;
1040 }
1041 
1042 
1043 /*!
1044  * \brief   pixMakeArbMaskFromRGB()
1045  *
1046  * \param[in]    pixs        32 bpp RGB
1047  * \param[in]    rc, gc, bc  arithmetic factors; can be negative
1048  * \param[in]    thresh      lower threshold on weighted sum of components
1049  * \return  pixd 1 bpp mask, or NULL on error
1050  *
1051  * <pre>
1052  * Notes:
1053  *      (1) This generates a 1 bpp mask image, where a 1 is written in
1054  *          the mask for each pixel in pixs that satisfies
1055  *               rc * rval + gc * gval + bc * bval > thresh
1056  *          where rval is the red component, etc.
1057  *      (2) Unlike with pixConvertToGray(), there are no constraints
1058  *          on the color coefficients, which can be negative.  For
1059  *          example, a mask that discriminates against red and in favor
1060  *          of blue will have rc < 0.0 and bc > 0.0.
1061  *      (3) To make the result independent of intensity (the 'V' in HSV),
1062  *          select coefficients so that @thresh = 0.  Then the result
1063  *          is not changed when all components are multiplied by the
1064  *          same constant (as long as nothing saturates).  This can be
1065  *          useful if, for example, the illumination is not uniform.
1066  * </pre>
1067  */
1068 PIX *
pixMakeArbMaskFromRGB(PIX * pixs,l_float32 rc,l_float32 gc,l_float32 bc,l_float32 thresh)1069 pixMakeArbMaskFromRGB(PIX       *pixs,
1070                       l_float32  rc,
1071                       l_float32  gc,
1072                       l_float32  bc,
1073                       l_float32  thresh)
1074 {
1075 PIX  *pix1, *pix2;
1076 
1077     PROCNAME("pixMakeArbMaskFromRGB");
1078 
1079     if (!pixs || pixGetDepth(pixs) != 32)
1080         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
1081     if (thresh >= 255.0) thresh = 254.0;  /* avoid 8 bit overflow */
1082 
1083     if ((pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc)) == NULL)
1084         return (PIX *)ERROR_PTR("pix1 not made", procName, NULL);
1085     pix2 = pixThresholdToBinary(pix1, thresh + 1);
1086     pixInvert(pix2, pix2);
1087     pixDestroy(&pix1);
1088     return pix2;
1089 }
1090 
1091 
1092 /*!
1093  * \brief   pixSetUnderTransparency()
1094  *
1095  * \param[in]    pixs 32 bpp rgba
1096  * \param[in]    val 32 bit unsigned color to use where alpha == 0
1097  * \param[in]    debug displays layers of pixs
1098  * \return  pixd 32 bpp rgba, or NULL on error
1099  *
1100  * <pre>
1101  * Notes:
1102  *      (1) This sets the r, g and b components under every fully
1103  *          transparent alpha component to %val.  The alpha components
1104  *          are unchanged.
1105  *      (2) Full transparency is denoted by alpha == 0.  Setting
1106  *          all pixels to a constant %val where alpha is transparent
1107  *          can improve compressibility by reducing the entropy.
1108  *      (3) The visual result depends on how the image is displayed.
1109  *          (a) For display devices that respect the use of the alpha
1110  *              layer, this will not affect the appearance.
1111  *          (b) For typical leptonica operations, alpha is ignored,
1112  *              so there will be a change in appearance because this
1113  *              resets the rgb values in the fully transparent region.
1114  *      (4) pixRead() and pixWrite() will, by default, read and write
1115  *          4-component (rgba) pix in png format.  To ignore the alpha
1116  *          component after reading, or omit it on writing, pixSetSpp(..., 3).
1117  *      (5) Here are some examples:
1118  *          * To convert all fully transparent pixels in a 4 component
1119  *            (rgba) png file to white:
1120  *              pixs = pixRead(<infile>);
1121  *              pixd = pixSetUnderTransparency(pixs, 0xffffff00, 0);
1122  *          * To write pixd with the alpha component:
1123  *              pixWrite(<outfile>, pixd, IFF_PNG);
1124  *          * To write and rgba image without the alpha component, first do:
1125  *              pixSetSpp(pixd, 3);
1126  *            If you later want to use the alpha, spp must be reset to 4.
1127  *          * (fancier) To remove the alpha by blending the image over
1128  *            a white background:
1129  *              pixRemoveAlpha()
1130  *            This changes all pixel values where the alpha component is
1131  *            not opaque (255).
1132  *      (6) Caution.  rgb images in leptonica typically have value 0 in
1133  *          the alpha channel, which is fully transparent.  If spp for
1134  *          such an image were changed from 3 to 4, the image becomes
1135  *          fully transparent, and this function will set each pixel to %val.
1136  *          If you really want to set every pixel to the same value,
1137  *          use pixSetAllArbitrary().
1138  *      (7) This is useful for compressing an RGBA image where the part
1139  *          of the image that is fully transparent is random junk; compression
1140  *          is typically improved by setting that region to a constant.
1141  *          For rendering as a 3 component RGB image over a uniform
1142  *          background of arbitrary color, use pixAlphaBlendUniform().
1143  * </pre>
1144  */
1145 PIX *
pixSetUnderTransparency(PIX * pixs,l_uint32 val,l_int32 debug)1146 pixSetUnderTransparency(PIX      *pixs,
1147                         l_uint32  val,
1148                         l_int32   debug)
1149 {
1150 PIX  *pixg, *pixm, *pixt, *pixd;
1151 
1152     PROCNAME("pixSetUnderTransparency");
1153 
1154     if (!pixs || pixGetDepth(pixs) != 32)
1155         return (PIX *)ERROR_PTR("pixs not defined or not 32 bpp",
1156                                 procName, NULL);
1157 
1158     if (pixGetSpp(pixs) != 4) {
1159         L_WARNING("no alpha channel; returning a copy\n", procName);
1160         return pixCopy(NULL, pixs);
1161     }
1162 
1163         /* Make a mask from the alpha component with ON pixels
1164          * wherever the alpha component is fully transparent (0).
1165          * The hard way:
1166          *     l_int32 *lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1167          *     lut[0] = 1;
1168          *     pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
1169          *     pixm = pixMakeMaskFromLUT(pixg, lut);
1170          *     LEPT_FREE(lut);
1171          * But there's an easier way to set pixels in a mask where
1172          * the alpha component is 0 ...  */
1173     pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
1174     pixm = pixThresholdToBinary(pixg, 1);
1175 
1176     if (debug) {
1177         pixt = pixDisplayLayersRGBA(pixs, 0xffffff00, 600);
1178         pixDisplay(pixt, 0, 0);
1179         pixDestroy(&pixt);
1180     }
1181 
1182     pixd = pixCopy(NULL, pixs);
1183     pixSetMasked(pixd, pixm, (val & 0xffffff00));
1184     pixDestroy(&pixg);
1185     pixDestroy(&pixm);
1186     return pixd;
1187 }
1188 
1189 
1190 /*!
1191  * \brief   pixMakeAlphaFromMask()
1192  *
1193  * \param[in]    pixs 1 bpp
1194  * \param[in]    dist blending distance; typically 10 - 30
1195  * \param[out]   pbox [optional]  use NULL to get the full size
1196  * \return  pixd (8 bpp gray, or NULL on error
1197  *
1198  * <pre>
1199  * Notes:
1200  *      (1) This generates a 8 bpp alpha layer that is opaque (256)
1201  *          over the FG of pixs, and goes transparent linearly away
1202  *          from the FG pixels, decaying to 0 (transparent) is an
1203  *          8-connected distance given by %dist.  If %dist == 0,
1204  *          this does a simple conversion from 1 to 8 bpp.
1205  *      (2) If &box == NULL, this returns an alpha mask that is the
1206  *          full size of pixs.  Otherwise, the returned mask pixd covers
1207  *          just the FG pixels of pixs, expanded by %dist in each
1208  *          direction (if possible), and the returned box gives the
1209  *          location of the returned mask relative to pixs.
1210  *      (3) This is useful for painting through a mask and allowing
1211  *          blending of the painted image with an underlying image
1212  *          in the mask background for pixels near foreground mask pixels.
1213  *          For example, with an underlying rgb image pix1, an overlaying
1214  *          image rgb pix2, binary mask pixm, and dist > 0, this
1215  *          blending is achieved with:
1216  *              pix3 = pixMakeAlphaFromMask(pixm, dist, &box);
1217  *              boxGetGeometry(box, &x, &y, NULL, NULL);
1218  *              pix4 = pixBlendWithGrayMask(pix1, pix2, pix3, x, y);
1219  * </pre>
1220  */
1221 PIX *
pixMakeAlphaFromMask(PIX * pixs,l_int32 dist,BOX ** pbox)1222 pixMakeAlphaFromMask(PIX     *pixs,
1223                      l_int32  dist,
1224                      BOX    **pbox)
1225 {
1226 l_int32  w, h;
1227 BOX     *box1, *box2;
1228 PIX     *pix1, *pixd;
1229 
1230     PROCNAME("pixMakeAlphaFromMask");
1231 
1232     if (pbox) *pbox = NULL;
1233     if (!pixs || pixGetDepth(pixs) != 1)
1234         return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
1235     if (dist < 0)
1236         return (PIX *)ERROR_PTR("dist must be >= 0", procName, NULL);
1237 
1238         /* If requested, extract just the region to be affected by the mask */
1239     if (pbox) {
1240         pixClipToForeground(pixs, NULL, &box1);
1241         if (!box1) {
1242             L_WARNING("no ON pixels in mask\n", procName);
1243             return pixCreateTemplate(pixs);  /* all background (0) */
1244         }
1245 
1246         boxAdjustSides(box1, box1, -dist, dist, -dist, dist);
1247         pixGetDimensions(pixs, &w, &h, NULL);
1248         box2 = boxClipToRectangle(box1, w, h);
1249         *pbox = box2;
1250         pix1 = pixClipRectangle(pixs, box2, NULL);
1251         boxDestroy(&box1);
1252     } else {
1253         pix1 = pixCopy(NULL, pixs);
1254     }
1255 
1256     if (dist == 0) {
1257         pixd = pixConvert1To8(NULL, pix1, 0, 255);
1258         pixDestroy(&pix1);
1259         return pixd;
1260     }
1261 
1262         /* Blur the boundary of the input mask */
1263     pixInvert(pix1, pix1);
1264     pixd = pixDistanceFunction(pix1, 8, 8, L_BOUNDARY_FG);
1265     pixMultConstantGray(pixd, 256.0 / dist);
1266     pixInvert(pixd, pixd);
1267     pixDestroy(&pix1);
1268     return pixd;
1269 }
1270 
1271 
1272 /*!
1273  * \brief   pixGetColorNearMaskBoundary()
1274  *
1275  * \param[in]    pixs 32 bpp rgb
1276  * \param[in]    pixm 1 bpp mask, full image
1277  * \param[in]    box region of mask; typically b.b. of a component
1278  * \param[in]    dist distance into BG from mask boundary to use
1279  * \param[out]   pval average pixel value
1280  * \param[in]    debug 1 to output mask images
1281  * \return  0 if OK, 1 on error.
1282  *
1283  * <pre>
1284  * Notes:
1285  *      (1) This finds the average color in a set of pixels that are
1286  *          roughly a distance %dist from the c.c. boundary and in the
1287  *          background of the mask image.
1288  * </pre>
1289  */
1290 l_int32
pixGetColorNearMaskBoundary(PIX * pixs,PIX * pixm,BOX * box,l_int32 dist,l_uint32 * pval,l_int32 debug)1291 pixGetColorNearMaskBoundary(PIX       *pixs,
1292                             PIX       *pixm,
1293                             BOX       *box,
1294                             l_int32    dist,
1295                             l_uint32  *pval,
1296                             l_int32    debug)
1297 {
1298 char       op[64];
1299 l_int32    empty, bx, by;
1300 l_float32  rval, gval, bval;
1301 BOX       *box1, *box2;
1302 PIX       *pix1, *pix2, *pix3;
1303 
1304     PROCNAME("pixGetColorNearMaskBoundary");
1305 
1306     if (!pval)
1307         return ERROR_INT("&pval not defined", procName, 1);
1308     *pval = 0xffffff00;  /* white */
1309     if (!pixs || pixGetDepth(pixs) != 32)
1310         return ERROR_INT("pixs undefined or not 32 bpp", procName, 1);
1311     if (!pixm || pixGetDepth(pixm) != 1)
1312         return ERROR_INT("pixm undefined or not 1 bpp", procName, 1);
1313     if (!box)
1314         return ERROR_INT("box not defined", procName, 1);
1315     if (dist < 0)
1316         return ERROR_INT("dist must be >= 0", procName, 1);
1317 
1318         /* Clip mask piece, expanded beyond %box by (%dist + 5) on each side.
1319          * box1 is the region requested; box2 is the actual region retrieved,
1320          * which is clipped to %pixm */
1321     box1 = boxAdjustSides(NULL, box, -dist - 5, dist + 5, -dist - 5, dist + 5);
1322     pix1 = pixClipRectangle(pixm, box1, &box2);
1323 
1324         /* Expand FG by %dist into the BG */
1325     if (dist == 0) {
1326         pix2 = pixCopy(NULL, pix1);
1327     } else {
1328         snprintf(op, sizeof(op), "d%d.%d", 2 * dist, 2 * dist);
1329         pix2 = pixMorphSequence(pix1, op, 0);
1330     }
1331 
1332         /* Expand again by 5 pixels on all sides (dilate 11x11) and XOR,
1333          * getting the annulus of FG pixels between %dist and %dist + 5 */
1334     pix3 = pixCopy(NULL, pix2);
1335     pixDilateBrick(pix3, pix3, 11, 11);
1336     pixXor(pix3, pix3, pix2);
1337     pixZero(pix3, &empty);
1338     if (!empty) {
1339             /* Scan the same region in %pixs, to get average under FG in pix3 */
1340         boxGetGeometry(box2, &bx, &by, NULL, NULL);
1341         pixGetAverageMaskedRGB(pixs, pix3, bx, by, 1, L_MEAN_ABSVAL,
1342                                &rval, &gval, &bval);
1343         composeRGBPixel((l_int32)(rval + 0.5), (l_int32)(gval + 0.5),
1344                         (l_int32)(bval + 0.5), pval);
1345     } else {
1346         L_WARNING("no pixels found\n", procName);
1347     }
1348 
1349     if (debug) {
1350         lept_rmdir("masknear");  /* erase previous images */
1351         lept_mkdir("masknear");
1352         pixWriteDebug("/tmp/masknear/input.png", pix1, IFF_PNG);
1353         pixWriteDebug("/tmp/masknear/adjusted.png", pix2, IFF_PNG);
1354         pixWriteDebug("/tmp/masknear/outerfive.png", pix3, IFF_PNG);
1355         fprintf(stderr, "Input box; with adjusted sides; clipped\n");
1356         boxPrintStreamInfo(stderr, box);
1357         boxPrintStreamInfo(stderr, box1);
1358         boxPrintStreamInfo(stderr, box2);
1359     }
1360 
1361     pixDestroy(&pix1);
1362     pixDestroy(&pix2);
1363     pixDestroy(&pix3);
1364     boxDestroy(&box1);
1365     boxDestroy(&box2);
1366     return 0;
1367 }
1368 
1369 
1370 /*-------------------------------------------------------------*
1371  *    One and two-image boolean ops on arbitrary depth images  *
1372  *-------------------------------------------------------------*/
1373 /*!
1374  * \brief   pixInvert()
1375  *
1376  * \param[in]    pixd  [optional]; this can be null, equal to pixs,
1377  *                     or different from pixs
1378  * \param[in]    pixs
1379  * \return  pixd, or NULL on error
1380  *
1381  * <pre>
1382  * Notes:
1383  *      (1) This inverts pixs, for all pixel depths.
1384  *      (2) There are 3 cases:
1385  *           (a) pixd == null,   ~src --> new pixd
1386  *           (b) pixd == pixs,   ~src --> src  (in-place)
1387  *           (c) pixd != pixs,   ~src --> input pixd
1388  *      (3) For clarity, if the case is known, use these patterns:
1389  *           (a) pixd = pixInvert(NULL, pixs);
1390  *           (b) pixInvert(pixs, pixs);
1391  *           (c) pixInvert(pixd, pixs);
1392  * </pre>
1393  */
1394 PIX *
pixInvert(PIX * pixd,PIX * pixs)1395 pixInvert(PIX  *pixd,
1396           PIX  *pixs)
1397 {
1398     PROCNAME("pixInvert");
1399 
1400     if (!pixs)
1401         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1402 
1403         /* Prepare pixd for in-place operation */
1404     if ((pixd = pixCopy(pixd, pixs)) == NULL)
1405         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1406 
1407     pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1408                 PIX_NOT(PIX_DST), NULL, 0, 0);   /* invert pixd */
1409 
1410     return pixd;
1411 }
1412 
1413 
1414 /*!
1415  * \brief   pixOr()
1416  *
1417  * \param[in]    pixd  [optional]; this can be null, equal to pixs1,
1418  *                     different from pixs1
1419  * \param[in]    pixs1 can be == pixd
1420  * \param[in]    pixs2 must be != pixd
1421  * \return  pixd always
1422  *
1423  * <pre>
1424  * Notes:
1425  *      (1) This gives the union of two images with equal depth,
1426  *          aligning them to the the UL corner.  pixs1 and pixs2
1427  *          need not have the same width and height.
1428  *      (2) There are 3 cases:
1429  *            (a) pixd == null,   (src1 | src2) --> new pixd
1430  *            (b) pixd == pixs1,  (src1 | src2) --> src1  (in-place)
1431  *            (c) pixd != pixs1,  (src1 | src2) --> input pixd
1432  *      (3) For clarity, if the case is known, use these patterns:
1433  *            (a) pixd = pixOr(NULL, pixs1, pixs2);
1434  *            (b) pixOr(pixs1, pixs1, pixs2);
1435  *            (c) pixOr(pixd, pixs1, pixs2);
1436  *      (4) The size of the result is determined by pixs1.
1437  *      (5) The depths of pixs1 and pixs2 must be equal.
1438  *      (6) Note carefully that the order of pixs1 and pixs2 only matters
1439  *          for the in-place case.  For in-place, you must have
1440  *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
1441  *          result: the copy puts pixs1 image data in pixs2, and
1442  *          the rasterop is then between pixs2 and pixs2 (a no-op).
1443  * </pre>
1444  */
1445 PIX *
pixOr(PIX * pixd,PIX * pixs1,PIX * pixs2)1446 pixOr(PIX  *pixd,
1447       PIX  *pixs1,
1448       PIX  *pixs2)
1449 {
1450     PROCNAME("pixOr");
1451 
1452     if (!pixs1)
1453         return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1454     if (!pixs2)
1455         return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1456     if (pixd == pixs2)
1457         return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
1458     if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1459         return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
1460 
1461 #if  EQUAL_SIZE_WARNING
1462     if (!pixSizesEqual(pixs1, pixs2))
1463         L_WARNING("pixs1 and pixs2 not equal sizes\n", procName);
1464 #endif  /* EQUAL_SIZE_WARNING */
1465 
1466         /* Prepare pixd to be a copy of pixs1 */
1467     if ((pixd = pixCopy(pixd, pixs1)) == NULL)
1468         return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
1469 
1470         /* src1 | src2 --> dest */
1471     pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1472                 PIX_SRC | PIX_DST, pixs2, 0, 0);
1473 
1474     return pixd;
1475 }
1476 
1477 
1478 /*!
1479  * \brief   pixAnd()
1480  *
1481  * \param[in]    pixd  [optional]; this can be null, equal to pixs1,
1482  *                     different from pixs1
1483  * \param[in]    pixs1 can be == pixd
1484  * \param[in]    pixs2 must be != pixd
1485  * \return  pixd always
1486  *
1487  * <pre>
1488  * Notes:
1489  *      (1) This gives the intersection of two images with equal depth,
1490  *          aligning them to the the UL corner.  pixs1 and pixs2
1491  *          need not have the same width and height.
1492  *      (2) There are 3 cases:
1493  *            (a) pixd == null,   (src1 & src2) --> new pixd
1494  *            (b) pixd == pixs1,  (src1 & src2) --> src1  (in-place)
1495  *            (c) pixd != pixs1,  (src1 & src2) --> input pixd
1496  *      (3) For clarity, if the case is known, use these patterns:
1497  *            (a) pixd = pixAnd(NULL, pixs1, pixs2);
1498  *            (b) pixAnd(pixs1, pixs1, pixs2);
1499  *            (c) pixAnd(pixd, pixs1, pixs2);
1500  *      (4) The size of the result is determined by pixs1.
1501  *      (5) The depths of pixs1 and pixs2 must be equal.
1502  *      (6) Note carefully that the order of pixs1 and pixs2 only matters
1503  *          for the in-place case.  For in-place, you must have
1504  *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
1505  *          result: the copy puts pixs1 image data in pixs2, and
1506  *          the rasterop is then between pixs2 and pixs2 (a no-op).
1507  * </pre>
1508  */
1509 PIX *
pixAnd(PIX * pixd,PIX * pixs1,PIX * pixs2)1510 pixAnd(PIX  *pixd,
1511        PIX  *pixs1,
1512        PIX  *pixs2)
1513 {
1514     PROCNAME("pixAnd");
1515 
1516     if (!pixs1)
1517         return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1518     if (!pixs2)
1519         return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1520     if (pixd == pixs2)
1521         return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
1522     if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1523         return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
1524 
1525 #if  EQUAL_SIZE_WARNING
1526     if (!pixSizesEqual(pixs1, pixs2))
1527         L_WARNING("pixs1 and pixs2 not equal sizes\n", procName);
1528 #endif  /* EQUAL_SIZE_WARNING */
1529 
1530         /* Prepare pixd to be a copy of pixs1 */
1531     if ((pixd = pixCopy(pixd, pixs1)) == NULL)
1532         return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
1533 
1534         /* src1 & src2 --> dest */
1535     pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1536                 PIX_SRC & PIX_DST, pixs2, 0, 0);
1537 
1538     return pixd;
1539 }
1540 
1541 
1542 /*!
1543  * \brief   pixXor()
1544  *
1545  * \param[in]    pixd  [optional]; this can be null, equal to pixs1,
1546  *                     different from pixs1
1547  * \param[in]    pixs1 can be == pixd
1548  * \param[in]    pixs2 must be != pixd
1549  * \return  pixd always
1550  *
1551  * <pre>
1552  * Notes:
1553  *      (1) This gives the XOR of two images with equal depth,
1554  *          aligning them to the the UL corner.  pixs1 and pixs2
1555  *          need not have the same width and height.
1556  *      (2) There are 3 cases:
1557  *            (a) pixd == null,   (src1 ^ src2) --> new pixd
1558  *            (b) pixd == pixs1,  (src1 ^ src2) --> src1  (in-place)
1559  *            (c) pixd != pixs1,  (src1 ^ src2) --> input pixd
1560  *      (3) For clarity, if the case is known, use these patterns:
1561  *            (a) pixd = pixXor(NULL, pixs1, pixs2);
1562  *            (b) pixXor(pixs1, pixs1, pixs2);
1563  *            (c) pixXor(pixd, pixs1, pixs2);
1564  *      (4) The size of the result is determined by pixs1.
1565  *      (5) The depths of pixs1 and pixs2 must be equal.
1566  *      (6) Note carefully that the order of pixs1 and pixs2 only matters
1567  *          for the in-place case.  For in-place, you must have
1568  *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
1569  *          result: the copy puts pixs1 image data in pixs2, and
1570  *          the rasterop is then between pixs2 and pixs2 (a no-op).
1571  * </pre>
1572  */
1573 PIX *
pixXor(PIX * pixd,PIX * pixs1,PIX * pixs2)1574 pixXor(PIX  *pixd,
1575        PIX  *pixs1,
1576        PIX  *pixs2)
1577 {
1578     PROCNAME("pixXor");
1579 
1580     if (!pixs1)
1581         return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1582     if (!pixs2)
1583         return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1584     if (pixd == pixs2)
1585         return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
1586     if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1587         return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
1588 
1589 #if  EQUAL_SIZE_WARNING
1590     if (!pixSizesEqual(pixs1, pixs2))
1591         L_WARNING("pixs1 and pixs2 not equal sizes\n", procName);
1592 #endif  /* EQUAL_SIZE_WARNING */
1593 
1594         /* Prepare pixd to be a copy of pixs1 */
1595     if ((pixd = pixCopy(pixd, pixs1)) == NULL)
1596         return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
1597 
1598         /* src1 ^ src2 --> dest */
1599     pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1600                 PIX_SRC ^ PIX_DST, pixs2, 0, 0);
1601 
1602     return pixd;
1603 }
1604 
1605 
1606 /*!
1607  * \brief   pixSubtract()
1608  *
1609  * \param[in]    pixd  [optional]; this can be null, equal to pixs1,
1610  *                     equal to pixs2, or different from both pixs1 and pixs2
1611  * \param[in]    pixs1 can be == pixd
1612  * \param[in]    pixs2 can be == pixd
1613  * \return  pixd always
1614  *
1615  * <pre>
1616  * Notes:
1617  *      (1) This gives the set subtraction of two images with equal depth,
1618  *          aligning them to the the UL corner.  pixs1 and pixs2
1619  *          need not have the same width and height.
1620  *      (2) Source pixs2 is always subtracted from source pixs1.
1621  *          The result is
1622  *                  pixs1 \ pixs2 = pixs1 & (~pixs2)
1623  *      (3) There are 4 cases:
1624  *            (a) pixd == null,   (src1 - src2) --> new pixd
1625  *            (b) pixd == pixs1,  (src1 - src2) --> src1  (in-place)
1626  *            (c) pixd == pixs2,  (src1 - src2) --> src2  (in-place)
1627  *            (d) pixd != pixs1 && pixd != pixs2),
1628  *                                 (src1 - src2) --> input pixd
1629  *      (4) For clarity, if the case is known, use these patterns:
1630  *            (a) pixd = pixSubtract(NULL, pixs1, pixs2);
1631  *            (b) pixSubtract(pixs1, pixs1, pixs2);
1632  *            (c) pixSubtract(pixs2, pixs1, pixs2);
1633  *            (d) pixSubtract(pixd, pixs1, pixs2);
1634  *      (5) The size of the result is determined by pixs1.
1635  *      (6) The depths of pixs1 and pixs2 must be equal.
1636  * </pre>
1637  */
1638 PIX *
pixSubtract(PIX * pixd,PIX * pixs1,PIX * pixs2)1639 pixSubtract(PIX  *pixd,
1640             PIX  *pixs1,
1641             PIX  *pixs2)
1642 {
1643 l_int32  w, h;
1644 
1645     PROCNAME("pixSubtract");
1646 
1647     if (!pixs1)
1648         return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1649     if (!pixs2)
1650         return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1651     if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1652         return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
1653 
1654 #if  EQUAL_SIZE_WARNING
1655     if (!pixSizesEqual(pixs1, pixs2))
1656         L_WARNING("pixs1 and pixs2 not equal sizes\n", procName);
1657 #endif  /* EQUAL_SIZE_WARNING */
1658 
1659     pixGetDimensions(pixs1, &w, &h, NULL);
1660     if (!pixd) {
1661         pixd = pixCopy(NULL, pixs1);
1662         pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1663             pixs2, 0, 0);   /* src1 & (~src2)  */
1664     } else if (pixd == pixs1) {
1665         pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1666             pixs2, 0, 0);   /* src1 & (~src2)  */
1667     } else if (pixd == pixs2) {
1668         pixRasterop(pixd, 0, 0, w, h, PIX_NOT(PIX_DST) & PIX_SRC,
1669             pixs1, 0, 0);   /* src1 & (~src2)  */
1670     } else  { /* pixd != pixs1 && pixd != pixs2 */
1671         pixCopy(pixd, pixs1);  /* sizes pixd to pixs1 if unequal */
1672         pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1673             pixs2, 0, 0);   /* src1 & (~src2)  */
1674     }
1675 
1676     return pixd;
1677 }
1678 
1679 
1680 /*-------------------------------------------------------------*
1681  *                         Pixel counting                      *
1682  *-------------------------------------------------------------*/
1683 /*!
1684  * \brief   pixZero()
1685  *
1686  * \param[in]    pix all depths; colormap OK
1687  * \param[out]   pempty  1 if all bits in image data field are 0;
1688  *                       0 otherwise
1689  * \return  0 if OK; 1 on error
1690  *
1691  * <pre>
1692  * Notes:
1693  *      (1) For a binary image, if there are no fg (black) pixels, empty = 1.
1694  *      (2) For a grayscale image, if all pixels are black (0), empty = 1.
1695  *      (3) For an RGB image, if all 4 components in every pixel is 0,
1696  *          empty = 1.
1697  *      (4) For a colormapped image, pixel values are 0.  The colormap
1698  *          is ignored.
1699  * </pre>
1700  */
1701 l_int32
pixZero(PIX * pix,l_int32 * pempty)1702 pixZero(PIX      *pix,
1703         l_int32  *pempty)
1704 {
1705 l_int32    w, h, wpl, i, j, fullwords, endbits;
1706 l_uint32   endmask;
1707 l_uint32  *data, *line;
1708 
1709     PROCNAME("pixZero");
1710 
1711     if (!pempty)
1712         return ERROR_INT("&empty not defined", procName, 1);
1713     *pempty = 1;
1714     if (!pix)
1715         return ERROR_INT("pix not defined", procName, 1);
1716 
1717     w = pixGetWidth(pix) * pixGetDepth(pix);  /* in bits */
1718     h = pixGetHeight(pix);
1719     wpl = pixGetWpl(pix);
1720     data = pixGetData(pix);
1721     fullwords = w / 32;
1722     endbits = w & 31;
1723     endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits));
1724 
1725     for (i = 0; i < h; i++) {
1726         line = data + wpl * i;
1727         for (j = 0; j < fullwords; j++)
1728             if (*line++) {
1729                 *pempty = 0;
1730                 return 0;
1731             }
1732         if (endbits) {
1733             if (*line & endmask) {
1734                 *pempty = 0;
1735                 return 0;
1736             }
1737         }
1738     }
1739 
1740     return 0;
1741 }
1742 
1743 
1744 /*!
1745  * \brief   pixForegroundFraction()
1746  *
1747  * \param[in]    pix 1 bpp
1748  * \param[out]   pfract fraction of ON pixels
1749  * \return  0 if OK; 1 on error
1750  */
1751 l_int32
pixForegroundFraction(PIX * pix,l_float32 * pfract)1752 pixForegroundFraction(PIX        *pix,
1753                       l_float32  *pfract)
1754 {
1755 l_int32  w, h, count;
1756 
1757     PROCNAME("pixForegroundFraction");
1758 
1759     if (!pfract)
1760         return ERROR_INT("&fract not defined", procName, 1);
1761     *pfract = 0.0;
1762     if (!pix || pixGetDepth(pix) != 1)
1763         return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
1764 
1765     pixCountPixels(pix, &count, NULL);
1766     pixGetDimensions(pix, &w, &h, NULL);
1767     *pfract = (l_float32)count / (l_float32)(w * h);
1768     return 0;
1769 }
1770 
1771 
1772 /*!
1773  * \brief   pixaCountPixels()
1774  *
1775  * \param[in]    pixa array of 1 bpp pix
1776  * \return  na of ON pixels in each pix, or NULL on error
1777  */
1778 NUMA *
pixaCountPixels(PIXA * pixa)1779 pixaCountPixels(PIXA  *pixa)
1780 {
1781 l_int32   d, i, n, count;
1782 l_int32  *tab;
1783 NUMA     *na;
1784 PIX      *pix;
1785 
1786     PROCNAME("pixaCountPixels");
1787 
1788     if (!pixa)
1789         return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
1790 
1791     if ((n = pixaGetCount(pixa)) == 0)
1792         return numaCreate(1);
1793 
1794     pix = pixaGetPix(pixa, 0, L_CLONE);
1795     d = pixGetDepth(pix);
1796     pixDestroy(&pix);
1797     if (d != 1)
1798         return (NUMA *)ERROR_PTR("pixa not 1 bpp", procName, NULL);
1799 
1800     if ((na = numaCreate(n)) == NULL)
1801         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
1802     tab = makePixelSumTab8();
1803     for (i = 0; i < n; i++) {
1804         pix = pixaGetPix(pixa, i, L_CLONE);
1805         pixCountPixels(pix, &count, tab);
1806         numaAddNumber(na, count);
1807         pixDestroy(&pix);
1808     }
1809 
1810     LEPT_FREE(tab);
1811     return na;
1812 }
1813 
1814 
1815 /*!
1816  * \brief   pixCountPixels()
1817  *
1818  * \param[in]    pixs     1 bpp
1819  * \param[out]   pcount   count of ON pixels
1820  * \param[in]    tab8     [optional] 8-bit pixel lookup table
1821  * \return  0 if OK; 1 on error
1822  */
1823 l_int32
pixCountPixels(PIX * pixs,l_int32 * pcount,l_int32 * tab8)1824 pixCountPixels(PIX      *pixs,
1825                l_int32  *pcount,
1826                l_int32  *tab8)
1827 {
1828 l_uint32   endmask;
1829 l_int32    w, h, wpl, i, j;
1830 l_int32    fullwords, endbits, sum;
1831 l_int32   *tab;
1832 l_uint32  *data;
1833 
1834     PROCNAME("pixCountPixels");
1835 
1836     if (!pcount)
1837         return ERROR_INT("&count not defined", procName, 1);
1838     *pcount = 0;
1839     if (!pixs || pixGetDepth(pixs) != 1)
1840         return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
1841 
1842     tab = (tab8) ? tab8 : makePixelSumTab8();
1843     pixGetDimensions(pixs, &w, &h, NULL);
1844     wpl = pixGetWpl(pixs);
1845     data = pixGetData(pixs);
1846     fullwords = w >> 5;
1847     endbits = w & 31;
1848     endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits));
1849 
1850     sum = 0;
1851     for (i = 0; i < h; i++, data += wpl) {
1852         for (j = 0; j < fullwords; j++) {
1853             l_uint32 word = data[j];
1854             if (word) {
1855                 sum += tab[word & 0xff] +
1856                        tab[(word >> 8) & 0xff] +
1857                        tab[(word >> 16) & 0xff] +
1858                        tab[(word >> 24) & 0xff];
1859             }
1860         }
1861         if (endbits) {
1862             l_uint32 word = data[j] & endmask;
1863             if (word) {
1864                 sum += tab[word & 0xff] +
1865                        tab[(word >> 8) & 0xff] +
1866                        tab[(word >> 16) & 0xff] +
1867                        tab[(word >> 24) & 0xff];
1868             }
1869         }
1870     }
1871     *pcount = sum;
1872 
1873     if (!tab8) LEPT_FREE(tab);
1874     return 0;
1875 }
1876 
1877 
1878 /*!
1879  * \brief   pixCountPixelsInRect()
1880  *
1881  * \param[in]    pixs     1 bpp
1882  * \param[in]    box      (can be null)
1883  * \param[out]   pcount   count of ON pixels
1884  * \param[in]    tab8     [optional] 8-bit pixel lookup table
1885  * \return  0 if OK; 1 on error
1886  */
1887 l_int32
pixCountPixelsInRect(PIX * pixs,BOX * box,l_int32 * pcount,l_int32 * tab8)1888 pixCountPixelsInRect(PIX      *pixs,
1889                      BOX      *box,
1890                      l_int32  *pcount,
1891                      l_int32  *tab8)
1892 {
1893 l_int32  bx, by, bw, bh;
1894 PIX     *pix1;
1895 
1896     PROCNAME("pixCountPixelsInRect");
1897 
1898     if (!pcount)
1899         return ERROR_INT("&count not defined", procName, 1);
1900     *pcount = 0;
1901     if (!pixs || pixGetDepth(pixs) != 1)
1902         return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
1903 
1904     if (box) {
1905         boxGetGeometry(box, &bx, &by, &bw, &bh);
1906         pix1 = pixCreate(bw, bh, 1);
1907         pixRasterop(pix1, 0, 0, bw, bh, PIX_SRC, pixs, bx, by);
1908         pixCountPixels(pix1, pcount, tab8);
1909         pixDestroy(&pix1);
1910     } else {
1911         pixCountPixels(pixs, pcount, tab8);
1912     }
1913 
1914     return 0;
1915 }
1916 
1917 
1918 /*!
1919  * \brief   pixCountByRow()
1920  *
1921  * \param[in]    pix 1 bpp
1922  * \param[in]    box [optional] clipping box for count; can be null
1923  * \return  na of number of ON pixels by row, or NULL on error
1924  *
1925  * <pre>
1926  * Notes:
1927  *      (1) To resample for a bin size different from 1, use
1928  *          numaUniformSampling() on the result of this function.
1929  * </pre>
1930  */
1931 NUMA *
pixCountByRow(PIX * pix,BOX * box)1932 pixCountByRow(PIX      *pix,
1933               BOX      *box)
1934 {
1935 l_int32    i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh;
1936 l_uint32  *line, *data;
1937 NUMA      *na;
1938 
1939     PROCNAME("pixCountByRow");
1940 
1941     if (!pix || pixGetDepth(pix) != 1)
1942         return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
1943     if (!box)
1944         return pixCountPixelsByRow(pix, NULL);
1945 
1946     pixGetDimensions(pix, &w, &h, NULL);
1947     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
1948                                  &bw, &bh) == 1)
1949         return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL);
1950 
1951     if ((na = numaCreate(bh)) == NULL)
1952         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
1953     numaSetParameters(na, ystart, 1);
1954     data = pixGetData(pix);
1955     wpl = pixGetWpl(pix);
1956     for (i = ystart; i < yend; i++) {
1957         count = 0;
1958         line = data + i * wpl;
1959         for (j = xstart; j < xend; j++) {
1960             if (GET_DATA_BIT(line, j))
1961                 count++;
1962         }
1963         numaAddNumber(na, count);
1964     }
1965 
1966     return na;
1967 }
1968 
1969 
1970 /*!
1971  * \brief   pixCountByColumn()
1972  *
1973  * \param[in]    pix 1 bpp
1974  * \param[in]    box [optional] clipping box for count; can be null
1975  * \return  na of number of ON pixels by column, or NULL on error
1976  *
1977  * <pre>
1978  * Notes:
1979  *      (1) To resample for a bin size different from 1, use
1980  *          numaUniformSampling() on the result of this function.
1981  * </pre>
1982  */
1983 NUMA *
pixCountByColumn(PIX * pix,BOX * box)1984 pixCountByColumn(PIX      *pix,
1985                  BOX      *box)
1986 {
1987 l_int32    i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh;
1988 l_uint32  *line, *data;
1989 NUMA      *na;
1990 
1991     PROCNAME("pixCountByColumn");
1992 
1993     if (!pix || pixGetDepth(pix) != 1)
1994         return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
1995     if (!box)
1996         return pixCountPixelsByColumn(pix);
1997 
1998     pixGetDimensions(pix, &w, &h, NULL);
1999     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2000                                  &bw, &bh) == 1)
2001         return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL);
2002 
2003     if ((na = numaCreate(bw)) == NULL)
2004         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2005     numaSetParameters(na, xstart, 1);
2006     data = pixGetData(pix);
2007     wpl = pixGetWpl(pix);
2008     for (j = xstart; j < xend; j++) {
2009         count = 0;
2010         for (i = ystart; i < yend; i++) {
2011             line = data + i * wpl;
2012             if (GET_DATA_BIT(line, j))
2013                 count++;
2014         }
2015         numaAddNumber(na, count);
2016     }
2017 
2018     return na;
2019 }
2020 
2021 
2022 /*!
2023  * \brief   pixCountPixelsByRow()
2024  *
2025  * \param[in]    pix 1 bpp
2026  * \param[in]    tab8  [optional] 8-bit pixel lookup table
2027  * \return  na of counts, or NULL on error
2028  */
2029 NUMA *
pixCountPixelsByRow(PIX * pix,l_int32 * tab8)2030 pixCountPixelsByRow(PIX      *pix,
2031                     l_int32  *tab8)
2032 {
2033 l_int32   h, i, count;
2034 l_int32  *tab;
2035 NUMA     *na;
2036 
2037     PROCNAME("pixCountPixelsByRow");
2038 
2039     if (!pix || pixGetDepth(pix) != 1)
2040         return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
2041 
2042     h = pixGetHeight(pix);
2043     if ((na = numaCreate(h)) == NULL)
2044         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2045 
2046     tab = (tab8) ? tab8 : makePixelSumTab8();
2047     for (i = 0; i < h; i++) {
2048         pixCountPixelsInRow(pix, i, &count, tab);
2049         numaAddNumber(na, count);
2050     }
2051 
2052     if (!tab8) LEPT_FREE(tab);
2053     return na;
2054 }
2055 
2056 
2057 /*!
2058  * \brief   pixCountPixelsByColumn()
2059  *
2060  * \param[in]    pix 1 bpp
2061  * \return  na of counts in each column, or NULL on error
2062  */
2063 NUMA *
pixCountPixelsByColumn(PIX * pix)2064 pixCountPixelsByColumn(PIX  *pix)
2065 {
2066 l_int32     i, j, w, h, wpl;
2067 l_uint32   *line, *data;
2068 l_float32  *array;
2069 NUMA       *na;
2070 
2071     PROCNAME("pixCountPixelsByColumn");
2072 
2073     if (!pix || pixGetDepth(pix) != 1)
2074         return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
2075 
2076     pixGetDimensions(pix, &w, &h, NULL);
2077     if ((na = numaCreate(w)) == NULL)
2078         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2079     numaSetCount(na, w);
2080     array = numaGetFArray(na, L_NOCOPY);
2081     data = pixGetData(pix);
2082     wpl = pixGetWpl(pix);
2083     for (i = 0; i < h; i++) {
2084         line = data + wpl * i;
2085         for (j = 0; j < w; j++) {
2086             if (GET_DATA_BIT(line, j))
2087                 array[j] += 1.0;
2088         }
2089     }
2090 
2091     return na;
2092 }
2093 
2094 
2095 /*!
2096  * \brief   pixCountPixelsInRow()
2097  *
2098  * \param[in]    pix 1 bpp
2099  * \param[in]    row number
2100  * \param[out]   pcount sum of ON pixels in raster line
2101  * \param[in]    tab8  [optional] 8-bit pixel lookup table
2102  * \return  0 if OK; 1 on error
2103  */
2104 l_int32
pixCountPixelsInRow(PIX * pix,l_int32 row,l_int32 * pcount,l_int32 * tab8)2105 pixCountPixelsInRow(PIX      *pix,
2106                     l_int32   row,
2107                     l_int32  *pcount,
2108                     l_int32  *tab8)
2109 {
2110 l_uint32   word, endmask;
2111 l_int32    j, w, h, wpl;
2112 l_int32    fullwords, endbits, sum;
2113 l_int32   *tab;
2114 l_uint32  *line;
2115 
2116     PROCNAME("pixCountPixelsInRow");
2117 
2118     if (!pcount)
2119         return ERROR_INT("&count not defined", procName, 1);
2120     *pcount = 0;
2121     if (!pix || pixGetDepth(pix) != 1)
2122         return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
2123 
2124     pixGetDimensions(pix, &w, &h, NULL);
2125     if (row < 0 || row >= h)
2126         return ERROR_INT("row out of bounds", procName, 1);
2127     wpl = pixGetWpl(pix);
2128     line = pixGetData(pix) + row * wpl;
2129     fullwords = w >> 5;
2130     endbits = w & 31;
2131     endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits));
2132 
2133     tab = (tab8) ? tab8 : makePixelSumTab8();
2134     sum = 0;
2135     for (j = 0; j < fullwords; j++) {
2136         word = line[j];
2137         if (word) {
2138             sum += tab[word & 0xff] +
2139                    tab[(word >> 8) & 0xff] +
2140                    tab[(word >> 16) & 0xff] +
2141                    tab[(word >> 24) & 0xff];
2142         }
2143     }
2144     if (endbits) {
2145         word = line[j] & endmask;
2146         if (word) {
2147             sum += tab[word & 0xff] +
2148                    tab[(word >> 8) & 0xff] +
2149                    tab[(word >> 16) & 0xff] +
2150                    tab[(word >> 24) & 0xff];
2151         }
2152     }
2153     *pcount = sum;
2154 
2155     if (!tab8) LEPT_FREE(tab);
2156     return 0;
2157 }
2158 
2159 
2160 /*!
2161  * \brief   pixGetMomentByColumn()
2162  *
2163  * \param[in]    pix 1 bpp
2164  * \param[in]    order of moment, either 1 or 2
2165  * \return  na of first moment of fg pixels, by column, or NULL on error
2166  */
2167 NUMA *
pixGetMomentByColumn(PIX * pix,l_int32 order)2168 pixGetMomentByColumn(PIX     *pix,
2169                      l_int32  order)
2170 {
2171 l_int32     i, j, w, h, wpl;
2172 l_uint32   *line, *data;
2173 l_float32  *array;
2174 NUMA       *na;
2175 
2176     PROCNAME("pixGetMomentByColumn");
2177 
2178     if (!pix || pixGetDepth(pix) != 1)
2179         return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
2180     if (order != 1 && order != 2)
2181         return (NUMA *)ERROR_PTR("order of moment not 1 or 2", procName, NULL);
2182 
2183     pixGetDimensions(pix, &w, &h, NULL);
2184     if ((na = numaCreate(w)) == NULL)
2185         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2186     numaSetCount(na, w);
2187     array = numaGetFArray(na, L_NOCOPY);
2188     data = pixGetData(pix);
2189     wpl = pixGetWpl(pix);
2190     for (i = 0; i < h; i++) {
2191         line = data + wpl * i;
2192         for (j = 0; j < w; j++) {
2193             if (GET_DATA_BIT(line, j)) {
2194                 if (order == 1)
2195                     array[j] += i;
2196                 else  /* order == 2 */
2197                     array[j] += i * i;
2198             }
2199         }
2200     }
2201 
2202     return na;
2203 }
2204 
2205 
2206 /*!
2207  * \brief   pixThresholdPixelSum()
2208  *
2209  * \param[in]    pix 1 bpp
2210  * \param[in]    thresh threshold
2211  * \param[out]   pabove 1 if above threshold;
2212  *                      0 if equal to or less than threshold
2213  * \param[in]    tab8  [optional] 8-bit pixel lookup table
2214  * \return  0 if OK; 1 on error
2215  *
2216  * <pre>
2217  * Notes:
2218  *      (1) This sums the ON pixels and returns immediately if the count
2219  *          goes above threshold.  It is therefore more efficient
2220  *          for matching images (by running this function on the xor of
2221  *          the 2 images) than using pixCountPixels(), which counts all
2222  *          pixels before returning.
2223  * </pre>
2224  */
2225 l_int32
pixThresholdPixelSum(PIX * pix,l_int32 thresh,l_int32 * pabove,l_int32 * tab8)2226 pixThresholdPixelSum(PIX      *pix,
2227                      l_int32   thresh,
2228                      l_int32  *pabove,
2229                      l_int32  *tab8)
2230 {
2231 l_uint32   word, endmask;
2232 l_int32   *tab;
2233 l_int32    w, h, wpl, i, j;
2234 l_int32    fullwords, endbits, sum;
2235 l_uint32  *line, *data;
2236 
2237     PROCNAME("pixThresholdPixelSum");
2238 
2239     if (!pabove)
2240         return ERROR_INT("&above not defined", procName, 1);
2241     *pabove = 0;
2242     if (!pix || pixGetDepth(pix) != 1)
2243         return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
2244 
2245     tab = (tab8) ? tab8 : makePixelSumTab8();
2246     pixGetDimensions(pix, &w, &h, NULL);
2247     wpl = pixGetWpl(pix);
2248     data = pixGetData(pix);
2249     fullwords = w >> 5;
2250     endbits = w & 31;
2251     endmask = 0xffffffff << (32 - endbits);
2252 
2253     sum = 0;
2254     for (i = 0; i < h; i++) {
2255         line = data + wpl * i;
2256         for (j = 0; j < fullwords; j++) {
2257             word = line[j];
2258             if (word) {
2259                 sum += tab[word & 0xff] +
2260                        tab[(word >> 8) & 0xff] +
2261                        tab[(word >> 16) & 0xff] +
2262                        tab[(word >> 24) & 0xff];
2263             }
2264         }
2265         if (endbits) {
2266             word = line[j] & endmask;
2267             if (word) {
2268                 sum += tab[word & 0xff] +
2269                        tab[(word >> 8) & 0xff] +
2270                        tab[(word >> 16) & 0xff] +
2271                        tab[(word >> 24) & 0xff];
2272             }
2273         }
2274         if (sum > thresh) {
2275             *pabove = 1;
2276             if (!tab8) LEPT_FREE(tab);
2277             return 0;
2278         }
2279     }
2280 
2281     if (!tab8) LEPT_FREE(tab);
2282     return 0;
2283 }
2284 
2285 
2286 /*!
2287  * \brief   makePixelSumTab8()
2288  *
2289  * \return  table of 256 l_int32, or NULL on error
2290  *
2291  * <pre>
2292  * Notes:
2293  *      (1) This table of integers gives the number of 1 bits
2294  *          in the 8 bit index.
2295  * </pre>
2296  */
2297 l_int32 *
makePixelSumTab8(void)2298 makePixelSumTab8(void)
2299 {
2300 l_uint8   byte;
2301 l_int32   i;
2302 l_int32  *tab;
2303 
2304     PROCNAME("makePixelSumTab8");
2305 
2306     if ((tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL)
2307         return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
2308 
2309     for (i = 0; i < 256; i++) {
2310         byte = (l_uint8)i;
2311         tab[i] = (byte & 0x1) +
2312                  ((byte >> 1) & 0x1) +
2313                  ((byte >> 2) & 0x1) +
2314                  ((byte >> 3) & 0x1) +
2315                  ((byte >> 4) & 0x1) +
2316                  ((byte >> 5) & 0x1) +
2317                  ((byte >> 6) & 0x1) +
2318                  ((byte >> 7) & 0x1);
2319     }
2320 
2321     return tab;
2322 }
2323 
2324 
2325 /*!
2326  * \brief   makePixelCentroidTab8()
2327  *
2328  * \return  table of 256 l_int32, or NULL on error
2329  *
2330  * <pre>
2331  * Notes:
2332  *      (1) This table of integers gives the centroid weight of the 1 bits
2333  *          in the 8 bit index.  In other words, if sumtab is obtained by
2334  *          makePixelSumTab8, and centroidtab is obtained by
2335  *          makePixelCentroidTab8, then, for 1 <= i <= 255,
2336  *          centroidtab[i] / (float)sumtab[i]
2337  *          is the centroid of the 1 bits in the 8-bit index i, where the
2338  *          MSB is considered to have position 0 and the LSB is considered
2339  *          to have position 7.
2340  * </pre>
2341  */
2342 l_int32 *
makePixelCentroidTab8(void)2343 makePixelCentroidTab8(void)
2344 {
2345 l_int32   i;
2346 l_int32  *tab;
2347 
2348     PROCNAME("makePixelCentroidTab8");
2349 
2350     if ((tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL)
2351         return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
2352 
2353     tab[0] = 0;
2354     tab[1] = 7;
2355     for (i = 2; i < 4; i++) {
2356         tab[i] = tab[i - 2] + 6;
2357     }
2358     for (i = 4; i < 8; i++) {
2359         tab[i] = tab[i - 4] + 5;
2360     }
2361     for (i = 8; i < 16; i++) {
2362         tab[i] = tab[i - 8] + 4;
2363     }
2364     for (i = 16; i < 32; i++) {
2365         tab[i] = tab[i - 16] + 3;
2366     }
2367     for (i = 32; i < 64; i++) {
2368         tab[i] = tab[i - 32] + 2;
2369     }
2370     for (i = 64; i < 128; i++) {
2371         tab[i] = tab[i - 64] + 1;
2372     }
2373     for (i = 128; i < 256; i++) {
2374         tab[i] = tab[i - 128];
2375     }
2376 
2377     return tab;
2378 }
2379 
2380 
2381 /*-------------------------------------------------------------*
2382  *             Average of pixel values in gray images          *
2383  *-------------------------------------------------------------*/
2384 /*!
2385  * \brief   pixAverageByRow()
2386  *
2387  * \param[in]    pix 8 or 16 bpp; no colormap
2388  * \param[in]    box [optional] clipping box for sum; can be null
2389  * \param[in]    type L_WHITE_IS_MAX, L_BLACK_IS_MAX
2390  * \return  na of pixel averages by row, or NULL on error
2391  *
2392  * <pre>
2393  * Notes:
2394  *      (1) To resample for a bin size different from 1, use
2395  *          numaUniformSampling() on the result of this function.
2396  *      (2) If type == L_BLACK_IS_MAX, black pixels get the maximum
2397  *          value (0xff for 8 bpp, 0xffff for 16 bpp) and white get 0.
2398  * </pre>
2399  */
2400 NUMA *
pixAverageByRow(PIX * pix,BOX * box,l_int32 type)2401 pixAverageByRow(PIX     *pix,
2402                 BOX     *box,
2403                 l_int32  type)
2404 {
2405 l_int32    i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh;
2406 l_uint32  *line, *data;
2407 l_float64  norm, sum;
2408 NUMA      *na;
2409 
2410     PROCNAME("pixAverageByRow");
2411 
2412     if (!pix)
2413         return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
2414     pixGetDimensions(pix, &w, &h, &d);
2415     if (d != 8 && d != 16)
2416         return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL);
2417     if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX)
2418         return (NUMA *)ERROR_PTR("invalid type", procName, NULL);
2419     if (pixGetColormap(pix) != NULL)
2420         return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL);
2421 
2422     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2423                                  &bw, &bh) == 1)
2424         return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL);
2425 
2426     norm = 1. / (l_float32)bw;
2427     if ((na = numaCreate(bh)) == NULL)
2428         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2429     numaSetParameters(na, ystart, 1);
2430     data = pixGetData(pix);
2431     wpl = pixGetWpl(pix);
2432     for (i = ystart; i < yend; i++) {
2433         sum = 0.0;
2434         line = data + i * wpl;
2435         if (d == 8) {
2436             for (j = xstart; j < xend; j++)
2437                 sum += GET_DATA_BYTE(line, j);
2438             if (type == L_BLACK_IS_MAX)
2439                 sum = bw * 255 - sum;
2440         } else {  /* d == 16 */
2441             for (j = xstart; j < xend; j++)
2442                 sum += GET_DATA_TWO_BYTES(line, j);
2443             if (type == L_BLACK_IS_MAX)
2444                 sum = bw * 0xffff - sum;
2445         }
2446         numaAddNumber(na, (l_float32)(norm * sum));
2447     }
2448 
2449     return na;
2450 }
2451 
2452 
2453 /*!
2454  * \brief   pixAverageByColumn()
2455  *
2456  * \param[in]    pix 8 or 16 bpp; no colormap
2457  * \param[in]    box [optional] clipping box for sum; can be null
2458  * \param[in]    type L_WHITE_IS_MAX, L_BLACK_IS_MAX
2459  * \return  na of pixel averages by column, or NULL on error
2460  *
2461  * <pre>
2462  * Notes:
2463  *      (1) To resample for a bin size different from 1, use
2464  *          numaUniformSampling() on the result of this function.
2465  *      (2) If type == L_BLACK_IS_MAX, black pixels get the maximum
2466  *          value (0xff for 8 bpp, 0xffff for 16 bpp) and white get 0.
2467  * </pre>
2468  */
2469 NUMA *
pixAverageByColumn(PIX * pix,BOX * box,l_int32 type)2470 pixAverageByColumn(PIX     *pix,
2471                    BOX     *box,
2472                    l_int32  type)
2473 {
2474 l_int32     i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh;
2475 l_uint32   *line, *data;
2476 l_float32   norm, sum;
2477 NUMA       *na;
2478 
2479     PROCNAME("pixAverageByColumn");
2480 
2481     if (!pix)
2482         return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
2483     pixGetDimensions(pix, &w, &h, &d);
2484 
2485     if (d != 8 && d != 16)
2486         return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL);
2487     if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX)
2488         return (NUMA *)ERROR_PTR("invalid type", procName, NULL);
2489     if (pixGetColormap(pix) != NULL)
2490         return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL);
2491 
2492     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2493                                  &bw, &bh) == 1)
2494         return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL);
2495 
2496     if ((na = numaCreate(bw)) == NULL)
2497         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2498     numaSetParameters(na, xstart, 1);
2499     norm = 1. / (l_float32)bh;
2500     data = pixGetData(pix);
2501     wpl = pixGetWpl(pix);
2502     for (j = xstart; j < xend; j++) {
2503         sum = 0.0;
2504         if (d == 8) {
2505             for (i = ystart; i < yend; i++) {
2506                 line = data + i * wpl;
2507                 sum += GET_DATA_BYTE(line, j);
2508             }
2509             if (type == L_BLACK_IS_MAX)
2510                 sum = bh * 255 - sum;
2511         } else {  /* d == 16 */
2512             for (i = ystart; i < yend; i++) {
2513                 line = data + i * wpl;
2514                 sum += GET_DATA_TWO_BYTES(line, j);
2515             }
2516             if (type == L_BLACK_IS_MAX)
2517                 sum = bh * 0xffff - sum;
2518         }
2519         numaAddNumber(na, (l_float32)(norm * sum));
2520     }
2521 
2522     return na;
2523 }
2524 
2525 
2526 /*!
2527  * \brief   pixAverageInRect()
2528  *
2529  * \param[in]    pix 1, 2, 4, 8 bpp; not cmapped
2530  * \param[in]    box [optional] if null, use entire image
2531  * \param[out]   pave average of pixel values in region
2532  * \return  0 if OK; 1 on error
2533  */
2534 l_int32
pixAverageInRect(PIX * pix,BOX * box,l_float32 * pave)2535 pixAverageInRect(PIX        *pix,
2536                  BOX        *box,
2537                  l_float32  *pave)
2538 {
2539 l_int32    w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh;
2540 l_uint32  *data, *line;
2541 l_float64  ave;
2542 
2543     PROCNAME("pixAverageInRect");
2544 
2545     if (!pave)
2546         return ERROR_INT("&ave not defined", procName, 1);
2547     *pave = 0;
2548     if (!pix)
2549         return ERROR_INT("pix not defined", procName, 1);
2550     pixGetDimensions(pix, &w, &h, &d);
2551     if (d != 1 && d != 2 && d != 4 && d != 8)
2552         return ERROR_INT("pix not 1, 2, 4 or 8 bpp", procName, 1);
2553     if (pixGetColormap(pix) != NULL)
2554         return ERROR_INT("pix is colormapped", procName, 1);
2555 
2556     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2557                                  &bw, &bh) == 1)
2558         return ERROR_INT("invalid clipping box", procName, 1);
2559 
2560     wpl = pixGetWpl(pix);
2561     data = pixGetData(pix);
2562     ave = 0;
2563     for (i = ystart; i < yend; i++) {
2564         line = data + i * wpl;
2565         for (j = xstart; j < xend; j++) {
2566             if (d == 1)
2567                 ave += GET_DATA_BIT(line, j);
2568             else if (d == 2)
2569                 ave += GET_DATA_DIBIT(line, j);
2570             else if (d == 4)
2571                 ave += GET_DATA_QBIT(line, j);
2572             else  /* d == 8 */
2573                 ave += GET_DATA_BYTE(line, j);
2574         }
2575     }
2576 
2577     *pave = ave / ((l_float32)(bw) * bh);
2578     return 0;
2579 }
2580 
2581 
2582 /*------------------------------------------------------------------*
2583  *               Variance of pixel values in gray images            *
2584  *------------------------------------------------------------------*/
2585 /*!
2586  * \brief   pixVarianceByRow()
2587  *
2588  * \param[in]    pix 8 or 16 bpp; no colormap
2589  * \param[in]    box [optional] clipping box for variance; can be null
2590  * \return  na of rmsdev by row, or NULL on error
2591  *
2592  * <pre>
2593  * Notes:
2594  *      (1) To resample for a bin size different from 1, use
2595  *          numaUniformSampling() on the result of this function.
2596  *      (2) We are actually computing the RMS deviation in each row.
2597  *          This is the square root of the variance.
2598  * </pre>
2599  */
2600 NUMA *
pixVarianceByRow(PIX * pix,BOX * box)2601 pixVarianceByRow(PIX     *pix,
2602                  BOX     *box)
2603 {
2604 l_int32     i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val;
2605 l_uint32   *line, *data;
2606 l_float64   sum1, sum2, norm, ave, var, rootvar;
2607 NUMA       *na;
2608 
2609     PROCNAME("pixVarianceByRow");
2610 
2611     if (!pix)
2612         return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
2613     pixGetDimensions(pix, &w, &h, &d);
2614     if (d != 8 && d != 16)
2615         return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL);
2616     if (pixGetColormap(pix) != NULL)
2617         return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL);
2618 
2619     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2620                                  &bw, &bh) == 1)
2621         return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL);
2622 
2623     if ((na = numaCreate(bh)) == NULL)
2624         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2625     numaSetParameters(na, ystart, 1);
2626     norm = 1. / (l_float32)bw;
2627     data = pixGetData(pix);
2628     wpl = pixGetWpl(pix);
2629     for (i = ystart; i < yend; i++) {
2630         sum1 = sum2 = 0.0;
2631         line = data + i * wpl;
2632         for (j = xstart; j < xend; j++) {
2633             if (d == 8)
2634                 val = GET_DATA_BYTE(line, j);
2635             else  /* d == 16 */
2636                 val = GET_DATA_TWO_BYTES(line, j);
2637             sum1 += val;
2638             sum2 += (l_float64)(val) * val;
2639         }
2640         ave = norm * sum1;
2641         var = norm * sum2 - ave * ave;
2642         rootvar = sqrt(var);
2643         numaAddNumber(na, (l_float32)rootvar);
2644     }
2645 
2646     return na;
2647 }
2648 
2649 
2650 /*!
2651  * \brief   pixVarianceByColumn()
2652  *
2653  * \param[in]    pix 8 or 16 bpp; no colormap
2654  * \param[in]    box [optional] clipping box for variance; can be null
2655  * \return  na of rmsdev by column, or NULL on error
2656  *
2657  * <pre>
2658  * Notes:
2659  *      (1) To resample for a bin size different from 1, use
2660  *          numaUniformSampling() on the result of this function.
2661  *      (2) We are actually computing the RMS deviation in each row.
2662  *          This is the square root of the variance.
2663  * </pre>
2664  */
2665 NUMA *
pixVarianceByColumn(PIX * pix,BOX * box)2666 pixVarianceByColumn(PIX     *pix,
2667                     BOX     *box)
2668 {
2669 l_int32     i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val;
2670 l_uint32   *line, *data;
2671 l_float64   sum1, sum2, norm, ave, var, rootvar;
2672 NUMA       *na;
2673 
2674     PROCNAME("pixVarianceByColumn");
2675 
2676     if (!pix)
2677         return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
2678     pixGetDimensions(pix, &w, &h, &d);
2679     if (d != 8 && d != 16)
2680         return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", procName, NULL);
2681     if (pixGetColormap(pix) != NULL)
2682         return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL);
2683 
2684     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2685                                  &bw, &bh) == 1)
2686         return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL);
2687 
2688     if ((na = numaCreate(bw)) == NULL)
2689         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2690     numaSetParameters(na, xstart, 1);
2691     norm = 1. / (l_float32)bh;
2692     data = pixGetData(pix);
2693     wpl = pixGetWpl(pix);
2694     for (j = xstart; j < xend; j++) {
2695         sum1 = sum2 = 0.0;
2696         for (i = ystart; i < yend; i++) {
2697             line = data + wpl * i;
2698             if (d == 8)
2699                 val = GET_DATA_BYTE(line, j);
2700             else  /* d == 16 */
2701                 val = GET_DATA_TWO_BYTES(line, j);
2702             sum1 += val;
2703             sum2 += (l_float64)(val) * val;
2704         }
2705         ave = norm * sum1;
2706         var = norm * sum2 - ave * ave;
2707         rootvar = sqrt(var);
2708         numaAddNumber(na, (l_float32)rootvar);
2709     }
2710 
2711     return na;
2712 }
2713 
2714 
2715 /*!
2716  * \brief   pixVarianceInRect()
2717  *
2718  * \param[in]    pix 1, 2, 4, 8 bpp; not cmapped
2719  * \param[in]    box [optional] if null, use entire image
2720  * \param[out]   prootvar sqrt variance of pixel values in region
2721  * \return  0 if OK; 1 on error
2722  */
2723 l_int32
pixVarianceInRect(PIX * pix,BOX * box,l_float32 * prootvar)2724 pixVarianceInRect(PIX        *pix,
2725                   BOX        *box,
2726                   l_float32  *prootvar)
2727 {
2728 l_int32    w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val;
2729 l_uint32  *data, *line;
2730 l_float64  sum1, sum2, norm, ave, var;
2731 
2732     PROCNAME("pixVarianceInRect");
2733 
2734     if (!prootvar)
2735         return ERROR_INT("&rootvar not defined", procName, 1);
2736     *prootvar = 0.0;
2737     if (!pix)
2738         return ERROR_INT("pix not defined", procName, 1);
2739     pixGetDimensions(pix, &w, &h, &d);
2740     if (d != 1 && d != 2 && d != 4 && d != 8)
2741         return ERROR_INT("pix not 1, 2, 4 or 8 bpp", procName, 1);
2742     if (pixGetColormap(pix) != NULL)
2743         return ERROR_INT("pix is colormapped", procName, 1);
2744 
2745     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2746                                  &bw, &bh) == 1)
2747         return ERROR_INT("invalid clipping box", procName, 1);
2748 
2749     wpl = pixGetWpl(pix);
2750     data = pixGetData(pix);
2751     sum1 = sum2 = 0.0;
2752     for (i = ystart; i < yend; i++) {
2753         line = data + i * wpl;
2754         for (j = xstart; j < xend; j++) {
2755             if (d == 1) {
2756                 val = GET_DATA_BIT(line, j);
2757                 sum1 += val;
2758                 sum2 += (l_float64)(val) * val;
2759             } else if (d == 2) {
2760                 val = GET_DATA_DIBIT(line, j);
2761                 sum1 += val;
2762                 sum2 += (l_float64)(val) * val;
2763             } else if (d == 4) {
2764                 val = GET_DATA_QBIT(line, j);
2765                 sum1 += val;
2766                 sum2 += (l_float64)(val) * val;
2767             } else {  /* d == 8 */
2768                 val = GET_DATA_BYTE(line, j);
2769                 sum1 += val;
2770                 sum2 += (l_float64)(val) * val;
2771             }
2772         }
2773     }
2774     norm = 1.0 / ((l_float64)(bw) * bh);
2775     ave = norm * sum1;
2776     var = norm * sum2 - ave * ave;
2777     *prootvar = (l_float32)sqrt(var);
2778     return 0;
2779 }
2780 
2781 
2782 /*---------------------------------------------------------------------*
2783  *    Average of absolute value of pixel differences in gray images    *
2784  *---------------------------------------------------------------------*/
2785 /*!
2786  * \brief   pixAbsDiffByRow()
2787  *
2788  * \param[in]    pix 8 bpp; no colormap
2789  * \param[in]    box [optional] clipping box for region; can be null
2790  * \return  na of abs val pixel difference averages by row, or NULL on error
2791  *
2792  * <pre>
2793  * Notes:
2794  *      (1) This is an average over differences of adjacent pixels along
2795  *          each row.
2796  *      (2) To resample for a bin size different from 1, use
2797  *          numaUniformSampling() on the result of this function.
2798  * </pre>
2799  */
2800 NUMA *
pixAbsDiffByRow(PIX * pix,BOX * box)2801 pixAbsDiffByRow(PIX  *pix,
2802                 BOX  *box)
2803 {
2804 l_int32    i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1;
2805 l_uint32  *line, *data;
2806 l_float64  norm, sum;
2807 NUMA      *na;
2808 
2809     PROCNAME("pixAbsDiffByRow");
2810 
2811     if (!pix || pixGetDepth(pix) != 8)
2812         return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL);
2813     if (pixGetColormap(pix) != NULL)
2814         return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL);
2815 
2816     pixGetDimensions(pix, &w, &h, NULL);
2817     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2818                                  &bw, &bh) == 1)
2819         return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL);
2820     if (bw < 2)
2821         return (NUMA *)ERROR_PTR("row width must be >= 2", procName, NULL);
2822 
2823     norm = 1. / (l_float32)(bw - 1);
2824     if ((na = numaCreate(bh)) == NULL)
2825         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2826     numaSetParameters(na, ystart, 1);
2827     data = pixGetData(pix);
2828     wpl = pixGetWpl(pix);
2829     for (i = ystart; i < yend; i++) {
2830         sum = 0.0;
2831         line = data + i * wpl;
2832         val0 = GET_DATA_BYTE(line, xstart);
2833         for (j = xstart + 1; j < xend; j++) {
2834             val1 = GET_DATA_BYTE(line, j);
2835             sum += L_ABS(val1 - val0);
2836             val0 = val1;
2837         }
2838         numaAddNumber(na, (l_float32)(norm * sum));
2839     }
2840 
2841     return na;
2842 }
2843 
2844 
2845 /*!
2846  * \brief   pixAbsDiffByColumn()
2847  *
2848  * \param[in]    pix 8 bpp; no colormap
2849  * \param[in]    box [optional] clipping box for region; can be null
2850  * \return  na of abs val pixel difference averages by column,
2851  *              or NULL on error
2852  *
2853  * <pre>
2854  * Notes:
2855  *      (1) This is an average over differences of adjacent pixels along
2856  *          each column.
2857  *      (2) To resample for a bin size different from 1, use
2858  *          numaUniformSampling() on the result of this function.
2859  * </pre>
2860  */
2861 NUMA *
pixAbsDiffByColumn(PIX * pix,BOX * box)2862 pixAbsDiffByColumn(PIX  *pix,
2863                    BOX  *box)
2864 {
2865 l_int32    i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1;
2866 l_uint32  *line, *data;
2867 l_float64  norm, sum;
2868 NUMA      *na;
2869 
2870     PROCNAME("pixAbsDiffByColumn");
2871 
2872     if (!pix || pixGetDepth(pix) != 8)
2873         return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL);
2874     if (pixGetColormap(pix) != NULL)
2875         return (NUMA *)ERROR_PTR("pix colormapped", procName, NULL);
2876 
2877     pixGetDimensions(pix, &w, &h, NULL);
2878     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2879                                  &bw, &bh) == 1)
2880         return (NUMA *)ERROR_PTR("invalid clipping box", procName, NULL);
2881     if (bh < 2)
2882         return (NUMA *)ERROR_PTR("column height must be >= 2", procName, NULL);
2883 
2884     norm = 1. / (l_float32)(bh - 1);
2885     if ((na = numaCreate(bw)) == NULL)
2886         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
2887     numaSetParameters(na, xstart, 1);
2888     data = pixGetData(pix);
2889     wpl = pixGetWpl(pix);
2890     for (j = xstart; j < xend; j++) {
2891         sum = 0.0;
2892         line = data + ystart * wpl;
2893         val0 = GET_DATA_BYTE(line, j);
2894         for (i = ystart + 1; i < yend; i++) {
2895             line = data + i * wpl;
2896             val1 = GET_DATA_BYTE(line, j);
2897             sum += L_ABS(val1 - val0);
2898             val0 = val1;
2899         }
2900         numaAddNumber(na, (l_float32)(norm * sum));
2901     }
2902 
2903     return na;
2904 }
2905 
2906 
2907 /*!
2908  * \brief   pixAbsDiffInRect()
2909  *
2910  * \param[in]    pix 8 bpp; not cmapped
2911  * \param[in]    box [optional] if null, use entire image
2912  * \param[in]    dir differences along L_HORIZONTAL_LINE or L_VERTICAL_LINE
2913  * \param[out]   pabsdiff average of abs diff pixel values in region
2914  * \return  0 if OK; 1 on error
2915  *
2916  * <pre>
2917  * Notes:
2918  *      (1) This gives the average over the abs val of differences of
2919  *          adjacent pixels values, along either each
2920  *             row:     dir == L_HORIZONTAL_LINE
2921  *             column:  dir == L_VERTICAL_LINE
2922  * </pre>
2923  */
2924 l_int32
pixAbsDiffInRect(PIX * pix,BOX * box,l_int32 dir,l_float32 * pabsdiff)2925 pixAbsDiffInRect(PIX        *pix,
2926                  BOX        *box,
2927                  l_int32     dir,
2928                  l_float32  *pabsdiff)
2929 {
2930 l_int32    w, h, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val0, val1;
2931 l_uint32  *data, *line;
2932 l_float64  norm, sum;
2933 
2934     PROCNAME("pixAbsDiffInRect");
2935 
2936     if (!pabsdiff)
2937         return ERROR_INT("&absdiff not defined", procName, 1);
2938     *pabsdiff = 0.0;
2939     if (!pix || pixGetDepth(pix) != 8)
2940         return ERROR_INT("pix undefined or not 8 bpp", procName, 1);
2941     if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE)
2942         return ERROR_INT("invalid direction", procName, 1);
2943     if (pixGetColormap(pix) != NULL)
2944         return ERROR_INT("pix is colormapped", procName, 1);
2945 
2946     pixGetDimensions(pix, &w, &h, NULL);
2947     if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2948                                  &bw, &bh) == 1)
2949         return ERROR_INT("invalid clipping box", procName, 1);
2950 
2951     wpl = pixGetWpl(pix);
2952     data = pixGetData(pix);
2953     if (dir == L_HORIZONTAL_LINE) {
2954         norm = 1. / (l_float32)(bh * (bw - 1));
2955         sum = 0.0;
2956         for (i = ystart; i < yend; i++) {
2957             line = data + i * wpl;
2958             val0 = GET_DATA_BYTE(line, xstart);
2959             for (j = xstart + 1; j < xend; j++) {
2960                 val1 = GET_DATA_BYTE(line, j);
2961                 sum += L_ABS(val1 - val0);
2962                 val0 = val1;
2963             }
2964         }
2965     } else {  /* vertical line */
2966         norm = 1. / (l_float32)(bw * (bh - 1));
2967         sum = 0.0;
2968         for (j = xstart; j < xend; j++) {
2969             line = data + ystart * wpl;
2970             val0 = GET_DATA_BYTE(line, j);
2971             for (i = ystart + 1; i < yend; i++) {
2972                 line = data + i * wpl;
2973                 val1 = GET_DATA_BYTE(line, j);
2974                 sum += L_ABS(val1 - val0);
2975                 val0 = val1;
2976             }
2977         }
2978     }
2979     *pabsdiff = (l_float32)(norm * sum);
2980     return 0;
2981 }
2982 
2983 
2984 /*!
2985  * \brief   pixAbsDiffOnLine()
2986  *
2987  * \param[in]    pix 8 bpp; not cmapped
2988  * \param[in]    x1, y1 first point; x1 <= x2, y1 <= y2
2989  * \param[in]    x2, y2 first point
2990  * \param[out]   pabsdiff average of abs diff pixel values on line
2991  * \return  0 if OK; 1 on error
2992  *
2993  * <pre>
2994  * Notes:
2995  *      (1) This gives the average over the abs val of differences of
2996  *          adjacent pixels values, along a line that is either horizontal
2997  *          or vertical.
2998  *      (2) If horizontal, require x1 < x2; if vertical, require y1 < y2.
2999  * </pre>
3000  */
3001 l_int32
pixAbsDiffOnLine(PIX * pix,l_int32 x1,l_int32 y1,l_int32 x2,l_int32 y2,l_float32 * pabsdiff)3002 pixAbsDiffOnLine(PIX        *pix,
3003                  l_int32     x1,
3004                  l_int32     y1,
3005                  l_int32     x2,
3006                  l_int32     y2,
3007                  l_float32  *pabsdiff)
3008 {
3009 l_int32    w, h, i, j, dir, size, sum;
3010 l_uint32   val0, val1;
3011 
3012     PROCNAME("pixAbsDiffOnLine");
3013 
3014     if (!pabsdiff)
3015         return ERROR_INT("&absdiff not defined", procName, 1);
3016     *pabsdiff = 0.0;
3017     if (!pix || pixGetDepth(pix) != 8)
3018         return ERROR_INT("pix undefined or not 8 bpp", procName, 1);
3019     if (y1 == y2) {
3020         dir = L_HORIZONTAL_LINE;
3021     } else if (x1 == x2) {
3022         dir = L_VERTICAL_LINE;
3023     } else {
3024         return ERROR_INT("line is neither horiz nor vert", procName, 1);
3025     }
3026     if (pixGetColormap(pix) != NULL)
3027         return ERROR_INT("pix is colormapped", procName, 1);
3028 
3029     pixGetDimensions(pix, &w, &h, NULL);
3030     sum = 0;
3031     if (dir == L_HORIZONTAL_LINE) {
3032         x1 = L_MAX(x1, 0);
3033         x2 = L_MIN(x2, w - 1);
3034         if (x1 >= x2)
3035             return ERROR_INT("x1 >= x2", procName, 1);
3036         size = x2 - x1;
3037         pixGetPixel(pix, x1, y1, &val0);
3038         for (j = x1 + 1; j <= x2; j++) {
3039             pixGetPixel(pix, j, y1, &val1);
3040             sum += L_ABS((l_int32)val1 - (l_int32)val0);
3041             val0 = val1;
3042         }
3043     } else {  /* vertical */
3044         y1 = L_MAX(y1, 0);
3045         y2 = L_MIN(y2, h - 1);
3046         if (y1 >= y2)
3047             return ERROR_INT("y1 >= y2", procName, 1);
3048         size = y2 - y1;
3049         pixGetPixel(pix, x1, y1, &val0);
3050         for (i = y1 + 1; i <= y2; i++) {
3051             pixGetPixel(pix, x1, i, &val1);
3052             sum += L_ABS((l_int32)val1 - (l_int32)val0);
3053             val0 = val1;
3054         }
3055     }
3056     *pabsdiff = (l_float32)sum / (l_float32)size;
3057     return 0;
3058 }
3059 
3060 
3061 /*-------------------------------------------------------------*
3062  *              Count of pixels with specific value            *
3063  *-------------------------------------------------------------*/
3064 /*!
3065  * \brief   pixCountArbInRect()
3066  *
3067  * \param[in]    pixs 8 bpp, or colormapped
3068  * \param[in]    box [optional] over which count is made;
3069  *                    use entire image if NULL
3070  * \param[in]    val pixel value to count
3071  * \param[in]    factor subsampling factor; integer >= 1
3072  * \param[out]   pcount count; estimate it if factor > 1
3073  * \return  na histogram, or NULL on error
3074  *
3075  * <pre>
3076  * Notes:
3077  *      (1) If pixs is cmapped, %val is compared to the colormap index;
3078  *          otherwise, %val is compared to the grayscale value.
3079  *      (2) Set the subsampling %factor > 1 to reduce the amount of computation.
3080  *          If %factor > 1, multiply the count by %factor * %factor.
3081  * </pre>
3082  */
3083 l_int32
pixCountArbInRect(PIX * pixs,BOX * box,l_int32 val,l_int32 factor,l_int32 * pcount)3084 pixCountArbInRect(PIX      *pixs,
3085                   BOX      *box,
3086                   l_int32   val,
3087                   l_int32   factor,
3088                   l_int32  *pcount)
3089 {
3090 l_int32    i, j, bx, by, bw, bh, w, h, wpl, pixval;
3091 l_uint32  *data, *line;
3092 
3093     PROCNAME("pixCountArbInRect");
3094 
3095     if (!pcount)
3096         return ERROR_INT("&count not defined", procName, 1);
3097     *pcount = 0;
3098     if (!pixs)
3099         return ERROR_INT("pixs not defined", procName, 1);
3100     if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
3101         return ERROR_INT("pixs neither 8 bpp nor colormapped",
3102                                  procName, 1);
3103     if (factor < 1)
3104         return ERROR_INT("sampling factor < 1", procName, 1);
3105 
3106     pixGetDimensions(pixs, &w, &h, NULL);
3107     data = pixGetData(pixs);
3108     wpl = pixGetWpl(pixs);
3109 
3110     if (!box) {
3111         for (i = 0; i < h; i += factor) {
3112             line = data + i * wpl;
3113             for (j = 0; j < w; j += factor) {
3114                 pixval = GET_DATA_BYTE(line, j);
3115                 if (pixval == val) (*pcount)++;
3116             }
3117         }
3118     } else {
3119         boxGetGeometry(box, &bx, &by, &bw, &bh);
3120         for (i = 0; i < bh; i += factor) {
3121             if (by + i < 0 || by + i >= h) continue;
3122             line = data + (by + i) * wpl;
3123             for (j = 0; j < bw; j += factor) {
3124                 if (bx + j < 0 || bx + j >= w) continue;
3125                 pixval = GET_DATA_BYTE(line, bx + j);
3126                 if (pixval == val) (*pcount)++;
3127             }
3128         }
3129     }
3130 
3131     if (factor > 1)  /* assume pixel color is randomly distributed */
3132         *pcount = *pcount * factor * factor;
3133     return 0;
3134 }
3135 
3136 
3137 /*-------------------------------------------------------------*
3138  *              Mirrored tiling of a smaller image             *
3139  *-------------------------------------------------------------*/
3140 /*!
3141  * \brief   pixMirroredTiling()
3142  *
3143  * \param[in]    pixs 8 or 32 bpp, small tile; to be replicated
3144  * \param[in]    w, h dimensions of output pix
3145  * \return  pixd usually larger pix, mirror-tiled with pixs,
3146  *              or NULL on error
3147  *
3148  * <pre>
3149  * Notes:
3150  *      (1) This uses mirrored tiling, where each row alternates
3151  *          with LR flips and every column alternates with TB
3152  *          flips, such that the result is a tiling with identical
3153  *          2 x 2 tiles, each of which is composed of these transforms:
3154  *                  -----------------
3155  *                  | 1    |  LR    |
3156  *                  -----------------
3157  *                  | TB   |  LR/TB |
3158  *                  -----------------
3159  * </pre>
3160  */
3161 PIX *
pixMirroredTiling(PIX * pixs,l_int32 w,l_int32 h)3162 pixMirroredTiling(PIX     *pixs,
3163                   l_int32  w,
3164                   l_int32  h)
3165 {
3166 l_int32   wt, ht, d, i, j, nx, ny;
3167 PIX      *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix;
3168 
3169     PROCNAME("pixMirroredTiling");
3170 
3171     if (!pixs)
3172         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3173     pixGetDimensions(pixs, &wt, &ht, &d);
3174     if (wt <= 0 || ht <= 0)
3175         return (PIX *)ERROR_PTR("pixs size illegal", procName, NULL);
3176     if (d != 8 && d != 32)
3177         return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL);
3178 
3179     if ((pixd = pixCreate(w, h, d)) == NULL)
3180         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
3181     pixCopySpp(pixd, pixs);
3182 
3183     nx = (w + wt - 1) / wt;
3184     ny = (h + ht - 1) / ht;
3185     pixsfx = pixFlipLR(NULL, pixs);
3186     pixsfy = pixFlipTB(NULL, pixs);
3187     pixsfxy = pixFlipTB(NULL, pixsfx);
3188     for (i = 0; i < ny; i++) {
3189         for (j = 0; j < nx; j++) {
3190             pix = pixs;
3191             if ((i & 1) && !(j & 1))
3192                 pix = pixsfy;
3193             else if (!(i & 1) && (j & 1))
3194                 pix = pixsfx;
3195             else if ((i & 1) && (j & 1))
3196                 pix = pixsfxy;
3197             pixRasterop(pixd, j * wt, i * ht, wt, ht, PIX_SRC, pix, 0, 0);
3198         }
3199     }
3200 
3201     pixDestroy(&pixsfx);
3202     pixDestroy(&pixsfy);
3203     pixDestroy(&pixsfxy);
3204     return pixd;
3205 }
3206 
3207 
3208 /*!
3209  * \brief   pixFindRepCloseTile()
3210  *
3211  * \param[in]    pixs 32 bpp rgb
3212  * \param[in]    box region of pixs to search around
3213  * \param[in]    searchdir L_HORIZ or L_VERT; direction to search
3214  * \param[in]    mindist min distance of selected tile edge from box; >= 0
3215  * \param[in]    tsize tile size; > 1; even; typically ~50
3216  * \param[in]    ntiles number of tiles tested in each row/column
3217  * \param[out]   pboxtile region of best tile
3218  * \param[in]    debug 1 for debug output
3219  * \return  0 if OK, 1 on error
3220  *
3221  * <pre>
3222  * Notes:
3223  *      (1) This looks for one or two square tiles with conforming median
3224  *          intensity and low variance, that is outside but near the input box.
3225  *      (2) %mindist specifies the gap between the box and the
3226  *          potential tiles.  The tiles are given an overlap of 50%.
3227  *          %ntiles specifies the number of tiles that are tested
3228  *          beyond %mindist for each row or column.
3229  *      (3) For example, if %mindist = 20, %tilesize = 50 and %ntiles = 3,
3230  *          a horizontal search to the right will have 3 tiles in each row,
3231  *          with left edges at 20, 45 and 70 from the right edge of the
3232  *          input %box.  The number of rows of tiles is determined by
3233  *          the height of %box and %tsize, with the 50% overlap..
3234  * </pre>
3235  */
3236 l_int32
pixFindRepCloseTile(PIX * pixs,BOX * box,l_int32 searchdir,l_int32 mindist,l_int32 tsize,l_int32 ntiles,BOX ** pboxtile,l_int32 debug)3237 pixFindRepCloseTile(PIX     *pixs,
3238                     BOX     *box,
3239                     l_int32  searchdir,
3240                     l_int32  mindist,
3241                     l_int32  tsize,
3242                     l_int32  ntiles,
3243                     BOX    **pboxtile,
3244                     l_int32  debug)
3245 {
3246 l_int32    w, h, i, n, bestindex;
3247 l_float32  var_of_mean, median_of_mean, median_of_stdev, mean_val, stdev_val;
3248 l_float32  mindels, bestdelm, delm, dels, mean, stdev;
3249 BOXA      *boxa;
3250 NUMA      *namean, *nastdev;
3251 PIX       *pix, *pixg;
3252 PIXA      *pixa;
3253 
3254     PROCNAME("pixFindRepCloseTile");
3255 
3256     if (!pboxtile)
3257         return ERROR_INT("&boxtile not defined", procName, 1);
3258     *pboxtile = NULL;
3259     if (!pixs)
3260         return ERROR_INT("pixs not defined", procName, 1);
3261     if (!box)
3262         return ERROR_INT("box not defined", procName, 1);
3263     if (searchdir != L_HORIZ && searchdir != L_VERT)
3264         return ERROR_INT("invalid searchdir", procName, 1);
3265     if (mindist < 0)
3266         return ERROR_INT("mindist must be >= 0", procName, 1);
3267     if (tsize < 2)
3268         return ERROR_INT("tsize must be > 1", procName, 1);
3269     if (ntiles > 7) {
3270         L_WARNING("ntiles = %d; larger than suggested max of 7\n",
3271                   procName, ntiles);
3272     }
3273 
3274         /* Locate tile regions */
3275     pixGetDimensions(pixs, &w, &h, NULL);
3276     boxa = findTileRegionsForSearch(box, w, h, searchdir, mindist,
3277                                     tsize, ntiles);
3278     if (!boxa)
3279         return ERROR_INT("no tiles found", procName, 1);
3280 
3281         /* Generate the tiles and the mean and stdev of intensity */
3282     pixa = pixClipRectangles(pixs, boxa);
3283     n = pixaGetCount(pixa);
3284     namean = numaCreate(n);
3285     nastdev = numaCreate(n);
3286     for (i = 0; i < n; i++) {
3287         pix = pixaGetPix(pixa, i, L_CLONE);
3288         pixg = pixConvertRGBToGray(pix, 0.33, 0.34, 0.33);
3289         pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &mean);
3290         pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_STANDARD_DEVIATION, &stdev);
3291         numaAddNumber(namean, mean);
3292         numaAddNumber(nastdev, stdev);
3293         pixDestroy(&pix);
3294         pixDestroy(&pixg);
3295     }
3296 
3297         /* Find the median and variance of the averages.  We require
3298          * the best tile to have a mean pixel intensity within a standard
3299          * deviation of the median of mean intensities, and choose the
3300          * tile in that set with the smallest stdev of pixel intensities
3301          * (as a proxy for the tile with least visible structure).
3302          * The median of the stdev is used, for debugging, as a normalizing
3303          * factor for the stdev of intensities within a tile. */
3304     numaGetStatsUsingHistogram(namean, 256, NULL, NULL, NULL, &var_of_mean,
3305                                &median_of_mean, 0.0, NULL, NULL);
3306     numaGetStatsUsingHistogram(nastdev, 256, NULL, NULL, NULL, NULL,
3307                                &median_of_stdev, 0.0, NULL, NULL);
3308     mindels = 1000.0;
3309     bestdelm = 1000.0;
3310     bestindex = 0;
3311     for (i = 0; i < n; i++) {
3312         numaGetFValue(namean, i, &mean_val);
3313         numaGetFValue(nastdev, i, &stdev_val);
3314         if (var_of_mean == 0.0) {  /* uniform color; any box will do */
3315             delm = 0.0;  /* any value < 1.01 */
3316             dels = 1.0;  /* n'importe quoi */
3317         } else {
3318             delm = L_ABS(mean_val - median_of_mean) / sqrt(var_of_mean);
3319             dels = stdev_val / median_of_stdev;
3320         }
3321         if (delm < 1.01) {
3322             if (dels < mindels) {
3323                 if (debug) {
3324                     fprintf(stderr, "i = %d, mean = %7.3f, delm = %7.3f,"
3325                             " stdev = %7.3f, dels = %7.3f\n",
3326                             i, mean_val, delm, stdev_val, dels);
3327                 }
3328                 mindels = dels;
3329                 bestdelm = delm;
3330                 bestindex = i;
3331             }
3332         }
3333     }
3334     *pboxtile = boxaGetBox(boxa, bestindex, L_COPY);
3335 
3336     if (debug) {
3337         L_INFO("median of mean = %7.3f\n", procName, median_of_mean);
3338         L_INFO("standard dev of mean = %7.3f\n", procName, sqrt(var_of_mean));
3339         L_INFO("median of stdev = %7.3f\n", procName, median_of_stdev);
3340         L_INFO("best tile: index = %d\n", procName, bestindex);
3341         L_INFO("delta from median in units of stdev = %5.3f\n",
3342                procName, bestdelm);
3343         L_INFO("stdev as fraction of median stdev = %5.3f\n",
3344                procName, mindels);
3345     }
3346 
3347     numaDestroy(&namean);
3348     numaDestroy(&nastdev);
3349     pixaDestroy(&pixa);
3350     boxaDestroy(&boxa);
3351     return 0;
3352 }
3353 
3354 
3355 /*!
3356  * \brief   findTileRegionsForSearch()
3357  *
3358  * \param[in]    box region of Pix to search around
3359  * \param[in]    w, h dimensions of Pix
3360  * \param[in]    searchdir L_HORIZ or L_VERT; direction to search
3361  * \param[in]    mindist min distance of selected tile edge from box; >= 0
3362  * \param[in]    tsize tile size; > 1; even; typically ~50
3363  * \param[in]    ntiles number of tiles tested in each row/column
3364  * \return  boxa if OK, or NULL on error
3365  *
3366  * <pre>
3367  * Notes:
3368  *      (1) See calling function pixfindRepCloseTile().
3369  * </pre>
3370  */
3371 static BOXA *
findTileRegionsForSearch(BOX * box,l_int32 w,l_int32 h,l_int32 searchdir,l_int32 mindist,l_int32 tsize,l_int32 ntiles)3372 findTileRegionsForSearch(BOX     *box,
3373                          l_int32  w,
3374                          l_int32  h,
3375                          l_int32  searchdir,
3376                          l_int32  mindist,
3377                          l_int32  tsize,
3378                          l_int32  ntiles)
3379 {
3380 l_int32  bx, by, bw, bh, left, right, top, bot, i, j, nrows, ncols;
3381 l_int32  x0, y0, x, y, w_avail, w_needed, h_avail, h_needed, t_avail;
3382 BOX     *box1;
3383 BOXA    *boxa;
3384 
3385     PROCNAME("findTileRegionsForSearch");
3386 
3387     if (!box)
3388         return (BOXA *)ERROR_PTR("box not defined", procName, NULL);
3389     if (ntiles == 0)
3390         return (BOXA *)ERROR_PTR("no tiles requested", procName, NULL);
3391 
3392     boxGetGeometry(box, &bx, &by, &bw, &bh);
3393     if (searchdir == L_HORIZ) {
3394             /* Find the tile parameters for the search.  Note that the
3395              * tiles are overlapping by 50% in each direction. */
3396         left = bx;   /* distance to left of box */
3397         right = w - bx - bw + 1;   /* distance to right of box */
3398         w_avail = L_MAX(left, right) - mindist;
3399         if (tsize & 1) tsize++;  /* be sure it's even */
3400         if (w_avail < tsize) {
3401             L_ERROR("tsize = %d, w_avail = %d\n", procName, tsize, w_avail);
3402             return NULL;
3403         }
3404         w_needed = tsize + (ntiles - 1) * (tsize / 2);
3405         if (w_needed > w_avail) {
3406             t_avail = 1 + 2 * (w_avail - tsize) / tsize;
3407             L_WARNING("ntiles = %d; room for only %d\n", procName,
3408                       ntiles, t_avail);
3409             ntiles = t_avail;
3410             w_needed = tsize + (ntiles - 1) * (tsize / 2);
3411         }
3412         nrows = L_MAX(1, 1 + 2 * (bh - tsize) / tsize);
3413 
3414             /* Generate the tile regions to search */
3415         boxa = boxaCreate(0);
3416         if (left > right)  /* search to left */
3417             x0 = bx - w_needed;
3418         else  /* search to right */
3419             x0 = bx + bw + mindist;
3420         for (i = 0; i < nrows; i++) {
3421             y = by + i * tsize / 2;
3422             for (j = 0; j < ntiles; j++) {
3423                 x = x0 + j * tsize / 2;
3424                 box1 = boxCreate(x, y, tsize, tsize);
3425                 boxaAddBox(boxa, box1, L_INSERT);
3426             }
3427         }
3428     } else {  /* L_VERT */
3429             /* Find the tile parameters for the search */
3430         top = by;   /* distance above box */
3431         bot = h - by - bh + 1;   /* distance below box */
3432         h_avail = L_MAX(top, bot) - mindist;
3433         if (h_avail < tsize) {
3434             L_ERROR("tsize = %d, h_avail = %d\n", procName, tsize, h_avail);
3435             return NULL;
3436         }
3437         h_needed = tsize + (ntiles - 1) * (tsize / 2);
3438         if (h_needed > h_avail) {
3439             t_avail = 1 + 2 * (h_avail - tsize) / tsize;
3440             L_WARNING("ntiles = %d; room for only %d\n", procName,
3441                       ntiles, t_avail);
3442             ntiles = t_avail;
3443             h_needed = tsize + (ntiles - 1) * (tsize / 2);
3444         }
3445         ncols = L_MAX(1, 1 + 2 * (bw - tsize) / tsize);
3446 
3447             /* Generate the tile regions to search */
3448         boxa = boxaCreate(0);
3449         if (top > bot)  /* search above */
3450             y0 = by - h_needed;
3451         else  /* search below */
3452             y0 = by + bh + mindist;
3453         for (j = 0; j < ncols; j++) {
3454             x = bx + j * tsize / 2;
3455             for (i = 0; i < ntiles; i++) {
3456                 y = y0 + i * tsize / 2;
3457                 box1 = boxCreate(x, y, tsize, tsize);
3458                 boxaAddBox(boxa, box1, L_INSERT);
3459             }
3460         }
3461     }
3462     return boxa;
3463 }
3464