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, ¥d,
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, ¥d,
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, ¥d,
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, ¥d,
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, ¥d,
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, ¥d,
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, ¥d,
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, ¥d,
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, ¥d,
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, ¥d,
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, ¥d,
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