1 /*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 - This software is distributed in the hope that it will be
4 - useful, but with NO WARRANTY OF ANY KIND.
5 - No author or distributor accepts responsibility to anyone for the
6 - consequences of using this software, or for whether it serves any
7 - particular purpose or works at all, unless he or she says so in
8 - writing. Everyone is granted permission to copy, modify and
9 - redistribute this source code, for commercial or non-commercial
10 - purposes, with the following restrictions: (1) the origin of this
11 - source code must not be misrepresented; (2) modified versions must
12 - be plainly marked as such; and (3) this notice may not be removed
13 - or altered from any source or modified source distribution.
14 *====================================================================*/
15
16 /*
17 * pix3.c
18 *
19 * This file has these operations:
20 *
21 * (1) Mask-directed operations
22 * (2) Full-image bit-logical operations
23 * (3) Foreground pixel counting operations on 1 bpp images
24 * (4) Sum of pixel values
25 * (5) Mirrored tiling of a smaller image
26 *
27 *
28 * Masked operations
29 * l_int32 pixSetMasked()
30 * l_int32 pixSetMaskedGeneral()
31 * l_int32 pixCombineMasked()
32 * l_int32 pixCombineMaskedGeneral()
33 * l_int32 pixPaintThroughMask()
34 * PIX *pixPaintSelfThroughMask()
35 *
36 * One and two-image boolean operations on arbitrary depth images
37 * PIX *pixInvert()
38 * PIX *pixOr()
39 * PIX *pixAnd()
40 * PIX *pixXor()
41 * PIX *pixSubtract()
42 *
43 * Foreground pixel counting in 1 bpp images
44 * l_int32 pixZero()
45 * l_int32 pixCountPixels()
46 * NUMA *pixaCountPixels()
47 * l_int32 pixCountPixelsInRow()
48 * NUMA *pixCountPixelsByRow()
49 * l_int32 pixThresholdPixels()
50 * l_int32 *makePixelSumTab8()
51 * l_int32 *makePixelCentroidTab8()
52 *
53 * Sum of pixel values
54 * l_int32 pixSumPixelValues()
55 *
56 * Mirrored tiling
57 * PIX *pixMirroredTiling()
58 *
59 * Static helper function
60 * static l_int32 findTilePatchCenter()
61 */
62
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include "allheaders.h"
67
68 static l_int32 findTilePatchCenter(PIX *pixs, BOX *box, l_int32 dir,
69 l_uint32 targdist, l_uint32 *pdist,
70 l_int32 *pxc, l_int32 *pyc);
71
72 #ifndef NO_CONSOLE_IO
73 #define EQUAL_SIZE_WARNING 0
74 #endif /* ~NO_CONSOLE_IO */
75
76
77 /*-------------------------------------------------------------*
78 * Masked operations *
79 *-------------------------------------------------------------*/
80 /*!
81 * pixSetMasked()
82 *
83 * Input: pixd (1, 2, 4, 8, 16 or 32 bpp; or colormapped)
84 * pixm (<optional> 1 bpp mask; no operation if NULL)
85 * val (value to set at each masked pixel)
86 * Return: 0 if OK; 1 on error
87 *
88 * Notes:
89 * (1) In-place operation. Calls pixSetMaskedCmap() for colormapped
90 * images.
91 * (2) If pixm == NULL, a warning is given.
92 * (3) It is an implicitly aligned operation, where the UL
93 * corners of pixd and pixm coincide. A warning is
94 * issued if the two image sizes differ significantly,
95 * but the operation proceeds.
96 * (4) Each pixel in pixd that co-locates with an ON pixel
97 * in pixm is set to the specified input value.
98 * Other pixels in pixd are not changed.
99 * (5) You can visualize this as painting the color through
100 * the mask, as a stencil.
101 * (6) If you do not want to have the UL corners aligned,
102 * use the function pixSetMaskedGeneral(), which requires
103 * you to input the UL corner of pixm relative to pixd.
104 * (7) Implementation details: see comments in pixPaintThroughMask()
105 * for when we use rasterop to do the painting.
106 */
107 l_int32
pixSetMasked(PIX * pixd,PIX * pixm,l_uint32 val)108 pixSetMasked(PIX *pixd,
109 PIX *pixm,
110 l_uint32 val)
111 {
112 l_int32 wd, hd, wm, hm, w, h, d, wpld, wplm;
113 l_int32 i, j, rval, gval, bval;
114 l_uint32 *datad, *datam, *lined, *linem;
115
116 PROCNAME("pixSetMasked");
117
118 if (!pixd)
119 return ERROR_INT("pixd not defined", procName, 1);
120 if (!pixm) {
121 L_WARNING("no mask; nothing to do", procName);
122 return 0;
123 }
124 if (pixGetColormap(pixd)) {
125 extractRGBValues(val, &rval, &gval, &bval);
126 return pixSetMaskedCmap(pixd, pixm, 0, 0, rval, gval, bval);
127 }
128
129 if (pixGetDepth(pixm) != 1)
130 return ERROR_INT("pixm not 1 bpp", procName, 1);
131 d = pixGetDepth(pixd);
132 if (d == 1)
133 val &= 1;
134 else if (d == 2)
135 val &= 3;
136 else if (d == 4)
137 val &= 0x0f;
138 else if (d == 8)
139 val &= 0xff;
140 else if (d == 16)
141 val &= 0xffff;
142 else if (d != 32)
143 return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
144 pixGetDimensions(pixm, &wm, &hm, NULL);
145
146 /* If d == 1, use rasterop; it's about 25x faster */
147 if (d == 1) {
148 if (val == 0) {
149 PIX *pixmi = pixInvert(NULL, pixm);
150 pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmi, 0, 0);
151 pixDestroy(&pixmi);
152 }
153 else /* val == 1 */
154 pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixm, 0, 0);
155 return 0;
156 }
157
158 /* For d < 32, use rasterop for val == 0 (black); ~3x faster. */
159 if (d < 32 && val == 0) {
160 PIX *pixmd = pixUnpackBinary(pixm, d, 1);
161 pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmd, 0, 0);
162 pixDestroy(&pixmd);
163 return 0;
164 }
165
166 /* For d < 32, use rasterop for val == maxval (white); ~3x faster. */
167 if (d < 32 && val == ((1 << d) - 1)) {
168 PIX *pixmd = pixUnpackBinary(pixm, d, 0);
169 pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixmd, 0, 0);
170 pixDestroy(&pixmd);
171 return 0;
172 }
173
174 pixGetDimensions(pixd, &wd, &hd, &d);
175 w = L_MIN(wd, wm);
176 h = L_MIN(hd, hm);
177 if (L_ABS(wd - wm) > 7 || L_ABS(hd - hm) > 7) /* allow a small tolerance */
178 L_WARNING("pixd and pixm sizes differ", procName);
179
180 datad = pixGetData(pixd);
181 datam = pixGetData(pixm);
182 wpld = pixGetWpl(pixd);
183 wplm = pixGetWpl(pixm);
184 for (i = 0; i < h; i++) {
185 lined = datad + i * wpld;
186 linem = datam + i * wplm;
187 for (j = 0; j < w; j++) {
188 if (GET_DATA_BIT(linem, j)) {
189 switch(d)
190 {
191 case 2:
192 SET_DATA_DIBIT(lined, j, val);
193 break;
194 case 4:
195 SET_DATA_QBIT(lined, j, val);
196 break;
197 case 8:
198 SET_DATA_BYTE(lined, j, val);
199 break;
200 case 16:
201 SET_DATA_TWO_BYTES(lined, j, val);
202 break;
203 case 32:
204 *(lined + j) = val;
205 break;
206 default:
207 return ERROR_INT("shouldn't get here", procName, 1);
208 }
209 }
210 }
211 }
212
213 return 0;
214 }
215
216
217 /*!
218 * pixSetMaskedGeneral()
219 *
220 * Input: pixd (8, 16 or 32 bpp)
221 * pixm (<optional> 1 bpp mask; no operation if null)
222 * val (value to set at each masked pixel)
223 * x, y (location of UL corner of pixm relative to pixd;
224 * can be negative)
225 * Return: 0 if OK; 1 on error
226 *
227 * Notes:
228 * (1) This is an in-place operation.
229 * (2) Alignment is explicit. If you want the UL corners of
230 * the two images to be aligned, use pixSetMasked().
231 * (3) A typical use would be painting through the foreground
232 * of a small binary mask pixm, located somewhere on a
233 * larger pixd. Other pixels in pixd are not changed.
234 * (4) You can visualize this as painting the color through
235 * the mask, as a stencil.
236 * (5) This uses rasterop to handle clipping and different depths of pixd.
237 * (6) If pixd has a colormap, you should call pixPaintThroughMask().
238 * (7) Why is this function here, if pixPaintThroughMask() does the
239 * same thing, and does it more generally? I've retained it here
240 * to show how one can paint through a mask using only full
241 * image rasterops, rather than pixel peeking in pixm and poking
242 * in pixd. It's somewhat baroque, but I found it amusing.
243 */
244 l_int32
pixSetMaskedGeneral(PIX * pixd,PIX * pixm,l_uint32 val,l_int32 x,l_int32 y)245 pixSetMaskedGeneral(PIX *pixd,
246 PIX *pixm,
247 l_uint32 val,
248 l_int32 x,
249 l_int32 y)
250 {
251 l_int32 wm, hm, d;
252 PIX *pixmu, *pixc;
253
254 PROCNAME("pixSetMaskedGeneral");
255
256 if (!pixd)
257 return ERROR_INT("pixd not defined", procName, 1);
258 if (!pixm) /* nothing to do */
259 return 0;
260
261 d = pixGetDepth(pixd);
262 if (d != 8 && d != 16 && d != 32)
263 return ERROR_INT("pixd not 8, 16 or 32 bpp", procName, 1);
264 if (pixGetDepth(pixm) != 1)
265 return ERROR_INT("pixm not 1 bpp", procName, 1);
266
267 /* Unpack binary to depth d, with inversion: 1 --> 0, 0 --> 0xff... */
268 if ((pixmu = pixUnpackBinary(pixm, d, 1)) == NULL)
269 return ERROR_INT("pixmu not made", procName, 1);
270
271 /* Clear stenciled pixels in pixd */
272 pixGetDimensions(pixm, &wm, &hm, NULL);
273 pixRasterop(pixd, x, y, wm, hm, PIX_SRC & PIX_DST, pixmu, 0, 0);
274
275 /* Generate image with requisite color */
276 if ((pixc = pixCreateTemplate(pixmu)) == NULL)
277 return ERROR_INT("pixc not made", procName, 1);
278 pixSetAllArbitrary(pixc, val);
279
280 /* Invert stencil mask, and paint color color into stencil */
281 pixInvert(pixmu, pixmu);
282 pixAnd(pixmu, pixmu, pixc);
283
284 /* Finally, repaint stenciled pixels, with val, in pixd */
285 pixRasterop(pixd, x, y, wm, hm, PIX_SRC | PIX_DST, pixmu, 0, 0);
286
287 pixDestroy(&pixmu);
288 pixDestroy(&pixc);
289 return 0;
290 }
291
292
293 /*!
294 * pixCombineMasked()
295 *
296 * Input: pixd (1 bpp, 8 bpp gray or 32 bpp rgb; no cmap)
297 * pixs (1 bpp, 8 bpp gray or 32 bpp rgb; no cmap)
298 * pixm (<optional> 1 bpp mask; no operation if NULL)
299 * Return: 0 if OK; 1 on error
300 *
301 * Notes:
302 * (1) In-place operation; pixd is changed.
303 * (2) This sets each pixel in pixd that co-locates with an ON
304 * pixel in pixm to the corresponding value of pixs.
305 * (3) pixs and pixd must be the same depth and not colormapped.
306 * (4) All three input pix are aligned at the UL corner, and the
307 * operation is clipped to the intersection of all three images.
308 * (5) If pixm == NULL, it's a no-op.
309 * (6) Implementation: see notes in pixCombineMaskedGeneral().
310 * For 8 bpp selective masking, you might guess that it
311 * would be faster to generate an 8 bpp version of pixm,
312 * using pixConvert1To8(pixm, 0, 255), and then use a
313 * general combine operation
314 * d = (d & ~m) | (s & m)
315 * on a word-by-word basis. Not always. The word-by-word
316 * combine takes a time that is independent of the mask data.
317 * If the mask is relatively sparse, the byte-check method
318 * is actually faster!
319 */
320 l_int32
pixCombineMasked(PIX * pixd,PIX * pixs,PIX * pixm)321 pixCombineMasked(PIX *pixd,
322 PIX *pixs,
323 PIX *pixm)
324 {
325 l_int32 w, h, d, ws, hs, ds, wm, hm, dm, wmin, hmin;
326 l_int32 wpl, wpls, wplm, i, j, val;
327 l_uint32 *data, *datas, *datam, *line, *lines, *linem;
328 PIX *pixt;
329
330 PROCNAME("pixCombineMasked");
331
332 if (!pixm) /* nothing to do */
333 return 0;
334 if (!pixd)
335 return ERROR_INT("pixd not defined", procName, 1);
336 if (!pixs)
337 return ERROR_INT("pixs not defined", procName, 1);
338 pixGetDimensions(pixd, &w, &h, &d);
339 pixGetDimensions(pixs, &ws, &hs, &ds);
340 pixGetDimensions(pixm, &wm, &hm, &dm);
341 if (d != ds)
342 return ERROR_INT("pixs and pixd depths differ", procName, 1);
343 if (dm != 1)
344 return ERROR_INT("pixm not 1 bpp", procName, 1);
345 if (d != 1 && d != 8 && d != 32)
346 return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1);
347 if (pixGetColormap(pixd) || pixGetColormap(pixs))
348 return ERROR_INT("pixs and/or pixd is cmapped", procName, 1);
349
350 /* For d = 1, use rasterop. pixt is the part from pixs, under
351 * the fg of pixm, that is to be combined with pixd. We also
352 * use pixt to remove all fg of pixd that is under the fg of pixm.
353 * Then pixt and pixd are combined by ORing. */
354 wmin = L_MIN(w, L_MIN(ws, wm));
355 hmin = L_MIN(h, L_MIN(hs, hm));
356 if (d == 1) {
357 pixt = pixAnd(NULL, pixs, pixm);
358 pixRasterop(pixd, 0, 0, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
359 pixm, 0, 0);
360 pixRasterop(pixd, 0, 0, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
361 pixDestroy(&pixt);
362 return 0;
363 }
364
365 data = pixGetData(pixd);
366 datas = pixGetData(pixs);
367 datam = pixGetData(pixm);
368 wpl = pixGetWpl(pixd);
369 wpls = pixGetWpl(pixs);
370 wplm = pixGetWpl(pixm);
371 if (d == 8) {
372 for (i = 0; i < hmin; i++) {
373 line = data + i * wpl;
374 lines = datas + i * wpls;
375 linem = datam + i * wplm;
376 for (j = 0; j < wmin; j++) {
377 if (GET_DATA_BIT(linem, j)) {
378 val = GET_DATA_BYTE(lines, j);
379 SET_DATA_BYTE(line, j, val);
380 }
381 }
382 }
383 }
384 else { /* d == 32 */
385 for (i = 0; i < hmin; i++) {
386 line = data + i * wpl;
387 lines = datas + i * wpls;
388 linem = datam + i * wplm;
389 for (j = 0; j < wmin; j++) {
390 if (GET_DATA_BIT(linem, j))
391 line[j] = lines[j];
392 }
393 }
394 }
395
396 return 0;
397 }
398
399
400 /*!
401 * pixCombineMaskedGeneral()
402 *
403 * Input: pixd (1 bpp, 8 bpp gray or 32 bpp rgb)
404 * pixs (1 bpp, 8 bpp gray or 32 bpp rgb)
405 * pixm (<optional> 1 bpp mask)
406 * x, y (origin of pixs and pixm relative to pixd; can be negative)
407 * Return: 0 if OK; 1 on error
408 *
409 * Notes:
410 * (1) In-place operation; pixd is changed.
411 * (2) This is a generalized version of pixCombinedMasked(), where
412 * the source and mask can be placed at the same (arbitrary)
413 * location relative to pixd.
414 * (3) pixs and pixd must be the same depth and not colormapped.
415 * (4) The UL corners of both pixs and pixm are aligned with
416 * the point (x, y) of pixd, and the operation is clipped to
417 * the intersection of all three images.
418 * (5) If pixm == NULL, it's a no-op.
419 * (6) Implementation. There are two ways to do these. In the first,
420 * we use rasterop, ORing the part of pixs under the mask
421 * with pixd (which has been appropriately cleared there first).
422 * In the second, the mask is used one pixel at a time to
423 * selectively replace pixels of pixd with those of pixs.
424 * Here, we use rasterop for 1 bpp and pixel-wise replacement
425 * for 8 and 32 bpp. To use rasterop for 8 bpp, for example,
426 * we must first generate an 8 bpp version of the mask.
427 * The code is simple:
428 *
429 * Pix *pixm8 = pixConvert1To8(NULL, pixm, 0, 255);
430 * Pix *pixt = pixAnd(NULL, pixs, pixm8);
431 * pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
432 * pixm8, 0, 0);
433 * pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST,
434 * pixt, 0, 0);
435 * pixDestroy(&pixt);
436 * pixDestroy(&pixm8);
437 */
438 l_int32
pixCombineMaskedGeneral(PIX * pixd,PIX * pixs,PIX * pixm,l_int32 x,l_int32 y)439 pixCombineMaskedGeneral(PIX *pixd,
440 PIX *pixs,
441 PIX *pixm,
442 l_int32 x,
443 l_int32 y)
444 {
445 l_int32 d, w, h, ws, hs, ds, wm, hm, dm, wmin, hmin;
446 l_int32 wpl, wpls, wplm, i, j, val;
447 l_uint32 *data, *datas, *datam, *line, *lines, *linem;
448 PIX *pixt;
449
450 PROCNAME("pixCombineMaskedGeneral");
451
452 if (!pixm) /* nothing to do */
453 return 0;
454 if (!pixd)
455 return ERROR_INT("pixd not defined", procName, 1);
456 if (!pixs)
457 return ERROR_INT("pixs not defined", procName, 1);
458 pixGetDimensions(pixd, &w, &h, &d);
459 pixGetDimensions(pixs, &ws, &hs, &ds);
460 pixGetDimensions(pixm, &wm, &hm, &dm);
461 if (d != ds)
462 return ERROR_INT("pixs and pixd depths differ", procName, 1);
463 if (dm != 1)
464 return ERROR_INT("pixm not 1 bpp", procName, 1);
465 if (d != 1 && d != 8 && d != 32)
466 return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1);
467 if (pixGetColormap(pixd) || pixGetColormap(pixs))
468 return ERROR_INT("pixs and/or pixd is cmapped", procName, 1);
469
470 /* For d = 1, use rasterop. pixt is the part from pixs, under
471 * the fg of pixm, that is to be combined with pixd. We also
472 * use pixt to remove all fg of pixd that is under the fg of pixm.
473 * Then pixt and pixd are combined by ORing. */
474 wmin = L_MIN(ws, wm);
475 hmin = L_MIN(hs, hm);
476 if (d == 1) {
477 pixt = pixAnd(NULL, pixs, pixm);
478 pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
479 pixm, 0, 0);
480 pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
481 pixDestroy(&pixt);
482 return 0;
483 }
484
485 wpl = pixGetWpl(pixd);
486 data = pixGetData(pixd);
487 wpls = pixGetWpl(pixs);
488 datas = pixGetData(pixs);
489 wplm = pixGetWpl(pixm);
490 datam = pixGetData(pixm);
491
492 for (i = 0; i < hmin; i++) {
493 if (y + i < 0 || y + i >= h) continue;
494 line = data + (y + i) * wpl;
495 lines = datas + i * wpls;
496 linem = datam + i * wplm;
497 for (j = 0; j < wmin; j++) {
498 if (x + j < 0 || x + j >= w) continue;
499 if (GET_DATA_BIT(linem, j)) {
500 switch (d)
501 {
502 case 8:
503 val = GET_DATA_BYTE(lines, j);
504 SET_DATA_BYTE(line, x + j, val);
505 break;
506 case 32:
507 *(line + x + j) = *(lines + j);
508 break;
509 default:
510 return ERROR_INT("shouldn't get here", procName, 1);
511 }
512 }
513 }
514 }
515
516 return 0;
517 }
518
519
520 /*!
521 * pixPaintThroughMask()
522 *
523 * Input: pixd (1, 2, 4, 8, 16 or 32 bpp; or colormapped)
524 * pixm (<optional> 1 bpp mask)
525 * x, y (origin of pixm relative to pixd; can be negative)
526 * val (pixel value to set at each masked pixel)
527 * Return: 0 if OK; 1 on error
528 *
529 * Notes:
530 * (1) In-place operation. Calls pixSetMaskedCmap() for colormapped
531 * images.
532 * (2) For 1, 2, 4, 8 and 16 bpp gray, we take the appropriate
533 * number of least significant bits of val.
534 * (3) If pixm == NULL, it's a no-op.
535 * (4) The mask origin is placed at (x,y) on pixd, and the
536 * operation is clipped to the intersection of rectangles.
537 * (5) For rgb, the components in val are in the canonical locations,
538 * with red in location COLOR_RED, etc.
539 * (6) Implementation detail 1:
540 * For painting with val == 0 or val == maxval, you can use rasterop.
541 * If val == 0, invert the mask so that it's 0 over the region
542 * into which you want to write, and use PIX_SRC & PIX_DST to
543 * clear those pixels. To write with val = maxval (all 1's),
544 * use PIX_SRC | PIX_DST to set all bits under the mask.
545 * (7) Implementation detail 2:
546 * The rasterop trick can be used for depth > 1 as well.
547 * For val == 0, generate the mask for depth d from the binary
548 * mask using
549 * pixmd = pixUnpackBinary(pixm, d, 1);
550 * and use pixRasterop() with PIX_MASK. For val == maxval,
551 * pixmd = pixUnpackBinary(pixm, d, 0);
552 * and use pixRasterop() with PIX_PAINT.
553 * But note that if d == 32 bpp, it is about 3x faster to use
554 * the general implementation (not pixRasterop()).
555 * (8) Implementation detail 3:
556 * It might be expected that the switch in the inner loop will
557 * cause large branching delays and should be avoided.
558 * This is not the case, because the entrance is always the
559 * same and the compiler can correctly predict the jump.
560 */
561 l_int32
pixPaintThroughMask(PIX * pixd,PIX * pixm,l_int32 x,l_int32 y,l_uint32 val)562 pixPaintThroughMask(PIX *pixd,
563 PIX *pixm,
564 l_int32 x,
565 l_int32 y,
566 l_uint32 val)
567 {
568 l_int32 d, w, h, wm, hm, wpl, wplm, i, j, rval, gval, bval;
569 l_uint32 *data, *datam, *line, *linem;
570
571 PROCNAME("pixPaintThroughMask");
572
573 if (!pixd)
574 return ERROR_INT("pixd not defined", procName, 1);
575 if (!pixm) /* nothing to do */
576 return 0;
577 if (pixGetColormap(pixd)) {
578 extractRGBValues(val, &rval, &gval, &bval);
579 return pixSetMaskedCmap(pixd, pixm, x, y, rval, gval, bval);
580 }
581
582 if (pixGetDepth(pixm) != 1)
583 return ERROR_INT("pixm not 1 bpp", procName, 1);
584 d = pixGetDepth(pixd);
585 if (d == 1)
586 val &= 1;
587 else if (d == 2)
588 val &= 3;
589 else if (d == 4)
590 val &= 0x0f;
591 else if (d == 8)
592 val &= 0xff;
593 else if (d == 16)
594 val &= 0xffff;
595 else if (d != 32)
596 return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
597 pixGetDimensions(pixm, &wm, &hm, NULL);
598
599 /* If d == 1, use rasterop; it's about 25x faster. */
600 if (d == 1) {
601 if (val == 0) {
602 PIX *pixmi = pixInvert(NULL, pixm);
603 pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmi, 0, 0);
604 pixDestroy(&pixmi);
605 }
606 else /* val == 1 */
607 pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixm, 0, 0);
608 return 0;
609 }
610
611 /* For d < 32, use rasterop if val == 0 (black); ~3x faster. */
612 if (d < 32 && val == 0) {
613 PIX *pixmd = pixUnpackBinary(pixm, d, 1);
614 pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmd, 0, 0);
615 pixDestroy(&pixmd);
616 return 0;
617 }
618
619 /* For d < 32, use rasterop if val == maxval (white); ~3x faster. */
620 if (d < 32 && val == ((1 << d) - 1)) {
621 PIX *pixmd = pixUnpackBinary(pixm, d, 0);
622 pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixmd, 0, 0);
623 pixDestroy(&pixmd);
624 return 0;
625 }
626
627 /* All other cases */
628 pixGetDimensions(pixd, &w, &h, NULL);
629 wpl = pixGetWpl(pixd);
630 data = pixGetData(pixd);
631 wplm = pixGetWpl(pixm);
632 datam = pixGetData(pixm);
633 for (i = 0; i < hm; i++) {
634 if (y + i < 0 || y + i >= h) continue;
635 line = data + (y + i) * wpl;
636 linem = datam + i * wplm;
637 for (j = 0; j < wm; j++) {
638 if (x + j < 0 || x + j >= w) continue;
639 if (GET_DATA_BIT(linem, j)) {
640 switch (d)
641 {
642 case 2:
643 SET_DATA_DIBIT(line, x + j, val);
644 break;
645 case 4:
646 SET_DATA_QBIT(line, x + j, val);
647 break;
648 case 8:
649 SET_DATA_BYTE(line, x + j, val);
650 break;
651 case 16:
652 SET_DATA_TWO_BYTES(line, x + j, val);
653 break;
654 case 32:
655 *(line + x + j) = val;
656 break;
657 default:
658 return ERROR_INT("shouldn't get here", procName, 1);
659 }
660 }
661 }
662 }
663
664 return 0;
665 }
666
667
668 /*!
669 * pixPaintSelfThroughMask()
670 *
671 * Input: pixd (8 bpp gray or 32 bpp rgb; not colormapped)
672 * pixm (1 bpp mask)
673 * x, y (origin of pixm relative to pixd; must not be negative)
674 * tilesize (requested size for tiling)
675 * searchdir (L_HORIZ, L_VERT)
676 * Return: 0 if OK; 1 on error
677 *
678 * Notes:
679 * (1) In-place operation; pixd is changed.
680 * (2) If pixm == NULL, it's a no-op.
681 * (3) The mask origin is placed at (x,y) on pixd, and the
682 * operation is clipped to the intersection of pixd and the
683 * fg of the mask.
684 * (4) The tilesize is the the requested size for tiling. The
685 * actual size for each c.c. will be bounded by the minimum
686 * dimension of the c.c. and the distance at which the tile
687 * center is located.
688 * (5) searchdir is the direction with respect to the b.b. of each
689 * mask component, from which the square patch is chosen and
690 * tiled onto the image, clipped by the mask component.
691 * (6) Specifically, a mirrored tiling, generated from pixd,
692 * is used to construct the pixels that are painted onto
693 * pixd through pixm.
694 */
695 l_int32
pixPaintSelfThroughMask(PIX * pixd,PIX * pixm,l_int32 x,l_int32 y,l_int32 tilesize,l_int32 searchdir)696 pixPaintSelfThroughMask(PIX *pixd,
697 PIX *pixm,
698 l_int32 x,
699 l_int32 y,
700 l_int32 tilesize,
701 l_int32 searchdir)
702 {
703 l_int32 w, h, d, wm, hm, dm, i, n, xc, yc, bx, by, bw, bh;
704 l_int32 depth, cctilesize;
705 l_uint32 dist, minside, retval;
706 BOX *box, *boxt;
707 BOXA *boxa;
708 PIX *pix, *pixf, *pixdf, *pixt, *pixc;
709 PIXA *pixa;
710
711 PROCNAME("pixPaintSelfThroughMask");
712
713 if (!pixm) /* nothing to do */
714 return 0;
715 if (!pixd)
716 return ERROR_INT("pixd not defined", procName, 1);
717 if (pixGetColormap(pixd) != NULL)
718 return ERROR_INT("pixd has colormap", procName, 1);
719 pixGetDimensions(pixd, &w, &h, &d);
720 if (d != 8 && d != 32)
721 return ERROR_INT("pixd not 8 or 32 bpp", procName, 1);
722 pixGetDimensions(pixm, &wm, &hm, &dm);
723 if (dm != 1)
724 return ERROR_INT("pixm not 1 bpp", procName, 1);
725 if (x < 0 || y < 0)
726 return ERROR_INT("x and y must be non-negative", procName, 1);
727 if (tilesize < 1)
728 return ERROR_INT("tilesize must be >= 1", procName, 1);
729 if (searchdir != L_HORIZ && searchdir != L_VERT)
730 return ERROR_INT("searchdir not in {L_HORIZ, L_VERT}", procName, 1);
731
732 /* Embed mask in full sized mask */
733 if (wm < w || hm < h) {
734 pixf = pixCreate(w, h, 1);
735 pixRasterop(pixf, x, y, wm, hm, PIX_SRC, pixm, 0, 0);
736 }
737 else
738 pixf = pixClone(pixm);
739
740 /* Get connected components of mask */
741 boxa = pixConnComp(pixf, &pixa, 8);
742 if ((n = pixaGetCount(pixa)) == 0) {
743 L_WARNING("no fg in mask", procName);
744 pixDestroy(&pixf);
745 pixaDestroy(&pixa);
746 boxaDestroy(&boxa);
747 return 1;
748 }
749
750 /* Get distance function for the mask */
751 pixInvert(pixf, pixf);
752 depth = (tilesize < 256) ? 8 : 16;
753 pixdf = pixDistanceFunction(pixf, 4, depth, L_BOUNDARY_BG);
754 pixDestroy(&pixf);
755
756 /* For each c.c., generate a representative tile for texturizing
757 * and apply it through the mask. The input 'tilesize' is the
758 * requested value. findTilePatchCenter() returns the distance
759 * at which this patch can safely be found. */
760 retval = 0;
761 for (i = 0; i < n; i++) {
762 pix = pixaGetPix(pixa, i, L_CLONE);
763 box = pixaGetBox(pixa, i, L_CLONE);
764 boxGetGeometry(box, &bx, &by, &bw, &bh);
765 minside = L_MIN(bw, bh);
766
767 findTilePatchCenter(pixdf, box, searchdir, L_MIN(minside, tilesize),
768 &dist, &xc, &yc);
769 cctilesize = L_MIN(tilesize, dist); /* for this c.c. */
770 if (cctilesize < 1) {
771 L_WARNING("region not found!", procName);
772 pixDestroy(&pix);
773 boxDestroy(&box);
774 retval = 1;
775 continue;
776 }
777
778 /* Extract the selected square from pixd, and generate
779 * an image the size of the b.b. of the c.c., which
780 * is then painted through the c.c. mask. */
781 boxt = boxCreate(L_MAX(0, xc - dist / 2), L_MAX(0, yc - dist / 2),
782 cctilesize, cctilesize);
783 pixt = pixClipRectangle(pixd, boxt, NULL);
784 pixc = pixMirroredTiling(pixt, bw, bh);
785 pixCombineMaskedGeneral(pixd, pixc, pix, bx, by);
786 pixDestroy(&pix);
787 pixDestroy(&pixt);
788 pixDestroy(&pixc);
789 boxDestroy(&box);
790 boxDestroy(&boxt);
791 }
792
793 pixDestroy(&pixdf);
794 pixaDestroy(&pixa);
795 boxaDestroy(&boxa);
796 return retval;
797 }
798
799
800 /*-------------------------------------------------------------*
801 * One and two-image boolean ops on arbitrary depth images *
802 *-------------------------------------------------------------*/
803 /*!
804 * pixInvert()
805 *
806 * Input: pixd (<optional>; this can be null, equal to pixs,
807 * or different from pixs)
808 * pixs
809 * Return: pixd, or null on error
810 *
811 * Notes:
812 * (1) This inverts pixs, for all pixel depths.
813 * (2) There are 3 cases:
814 * (a) pixd == null, ~src --> new pixd
815 * (b) pixd == pixs, ~src --> src (in-place)
816 * (c) pixd != pixs, ~src --> input pixd
817 * (3) For clarity, if the case is known, use these patterns:
818 * (a) pixd = pixInvert(NULL, pixs);
819 * (b) pixInvert(pixs, pixs);
820 * (c) pixInvert(pixd, pixs);
821 */
822 PIX *
pixInvert(PIX * pixd,PIX * pixs)823 pixInvert(PIX *pixd,
824 PIX *pixs)
825 {
826 PROCNAME("pixInvert");
827
828 if (!pixs)
829 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
830
831 /* Prepare pixd for in-place operation */
832 if ((pixd = pixCopy(pixd, pixs)) == NULL)
833 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
834
835 pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
836 PIX_NOT(PIX_DST), NULL, 0, 0); /* invert pixd */
837
838 return pixd;
839 }
840
841
842 /*!
843 * pixOr()
844 *
845 * Input: pixd (<optional>; this can be null, equal to pixs1,
846 * different from pixs1)
847 * pixs1 (can be == pixd)
848 * pixs2 (must be != pixd)
849 * Return: pixd always
850 *
851 * Notes:
852 * (1) This gives the union of two images with equal depth,
853 * aligning them to the the UL corner. pixs1 and pixs2
854 * need not have the same width and height.
855 * (2) There are 3 cases:
856 * (a) pixd == null, (src1 | src2) --> new pixd
857 * (b) pixd == pixs1, (src1 | src2) --> src1 (in-place)
858 * (c) pixd != pixs1, (src1 | src2) --> input pixd
859 * (3) For clarity, if the case is known, use these patterns:
860 * (a) pixd = pixOr(NULL, pixs1, pixs2);
861 * (b) pixOr(pixs1, pixs1, pixs2);
862 * (c) pixOr(pixd, pixs1, pixs2);
863 * (4) The size of the result is determined by pixs1.
864 * (5) The depths of pixs1 and pixs2 must be equal.
865 * (6) Note carefully that the order of pixs1 and pixs2 only matters
866 * for the in-place case. For in-place, you must have
867 * pixd == pixs1. Setting pixd == pixs2 gives an incorrect
868 * result: the copy puts pixs1 image data in pixs2, and
869 * the rasterop is then between pixs2 and pixs2 (a no-op).
870 */
871 PIX *
pixOr(PIX * pixd,PIX * pixs1,PIX * pixs2)872 pixOr(PIX *pixd,
873 PIX *pixs1,
874 PIX *pixs2)
875 {
876 PROCNAME("pixOr");
877
878 if (!pixs1)
879 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
880 if (!pixs2)
881 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
882 if (pixd == pixs2)
883 return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
884 if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
885 return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
886
887 #if EQUAL_SIZE_WARNING
888 if (!pixSizesEqual(pixs1, pixs2))
889 L_WARNING("pixs1 and pixs2 not equal sizes", procName);
890 #endif /* EQUAL_SIZE_WARNING */
891
892 /* Prepare pixd to be a copy of pixs1 */
893 if ((pixd = pixCopy(pixd, pixs1)) == NULL)
894 return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
895
896 /* src1 | src2 --> dest */
897 pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
898 PIX_SRC | PIX_DST, pixs2, 0, 0);
899
900 return pixd;
901 }
902
903
904 /*!
905 * pixAnd()
906 *
907 * Input: pixd (<optional>; this can be null, equal to pixs1,
908 * different from pixs1)
909 * pixs1 (can be == pixd)
910 * pixs2 (must be != pixd)
911 * Return: pixd always
912 *
913 * Notes:
914 * (1) This gives the intersection of two images with equal depth,
915 * aligning them to the the UL corner. pixs1 and pixs2
916 * need not have the same width and height.
917 * (2) There are 3 cases:
918 * (a) pixd == null, (src1 & src2) --> new pixd
919 * (b) pixd == pixs1, (src1 & src2) --> src1 (in-place)
920 * (c) pixd != pixs1, (src1 & src2) --> input pixd
921 * (3) For clarity, if the case is known, use these patterns:
922 * (a) pixd = pixAnd(NULL, pixs1, pixs2);
923 * (b) pixAnd(pixs1, pixs1, pixs2);
924 * (c) pixAnd(pixd, pixs1, pixs2);
925 * (4) The size of the result is determined by pixs1.
926 * (5) The depths of pixs1 and pixs2 must be equal.
927 * (6) Note carefully that the order of pixs1 and pixs2 only matters
928 * for the in-place case. For in-place, you must have
929 * pixd == pixs1. Setting pixd == pixs2 gives an incorrect
930 * result: the copy puts pixs1 image data in pixs2, and
931 * the rasterop is then between pixs2 and pixs2 (a no-op).
932 */
933 PIX *
pixAnd(PIX * pixd,PIX * pixs1,PIX * pixs2)934 pixAnd(PIX *pixd,
935 PIX *pixs1,
936 PIX *pixs2)
937 {
938 PROCNAME("pixAnd");
939
940 if (!pixs1)
941 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
942 if (!pixs2)
943 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
944 if (pixd == pixs2)
945 return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
946 if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
947 return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
948
949 #if EQUAL_SIZE_WARNING
950 if (!pixSizesEqual(pixs1, pixs2))
951 L_WARNING("pixs1 and pixs2 not equal sizes", procName);
952 #endif /* EQUAL_SIZE_WARNING */
953
954 /* Prepare pixd to be a copy of pixs1 */
955 if ((pixd = pixCopy(pixd, pixs1)) == NULL)
956 return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
957
958 /* src1 & src2 --> dest */
959 pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
960 PIX_SRC & PIX_DST, pixs2, 0, 0);
961
962 return pixd;
963 }
964
965
966 /*!
967 * pixXor()
968 *
969 * Input: pixd (<optional>; this can be null, equal to pixs1,
970 * different from pixs1)
971 * pixs1 (can be == pixd)
972 * pixs2 (must be != pixd)
973 * Return: pixd always
974 *
975 * Notes:
976 * (1) This gives the XOR of two images with equal depth,
977 * aligning them to the the UL corner. pixs1 and pixs2
978 * need not have the same width and height.
979 * (2) There are 3 cases:
980 * (a) pixd == null, (src1 ^ src2) --> new pixd
981 * (b) pixd == pixs1, (src1 ^ src2) --> src1 (in-place)
982 * (c) pixd != pixs1, (src1 ^ src2) --> input pixd
983 * (3) For clarity, if the case is known, use these patterns:
984 * (a) pixd = pixXor(NULL, pixs1, pixs2);
985 * (b) pixXor(pixs1, pixs1, pixs2);
986 * (c) pixXor(pixd, pixs1, pixs2);
987 * (4) The size of the result is determined by pixs1.
988 * (5) The depths of pixs1 and pixs2 must be equal.
989 * (6) Note carefully that the order of pixs1 and pixs2 only matters
990 * for the in-place case. For in-place, you must have
991 * pixd == pixs1. Setting pixd == pixs2 gives an incorrect
992 * result: the copy puts pixs1 image data in pixs2, and
993 * the rasterop is then between pixs2 and pixs2 (a no-op).
994 */
995 PIX *
pixXor(PIX * pixd,PIX * pixs1,PIX * pixs2)996 pixXor(PIX *pixd,
997 PIX *pixs1,
998 PIX *pixs2)
999 {
1000 PROCNAME("pixXor");
1001
1002 if (!pixs1)
1003 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1004 if (!pixs2)
1005 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1006 if (pixd == pixs2)
1007 return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
1008 if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1009 return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
1010
1011 #if EQUAL_SIZE_WARNING
1012 if (!pixSizesEqual(pixs1, pixs2))
1013 L_WARNING("pixs1 and pixs2 not equal sizes", procName);
1014 #endif /* EQUAL_SIZE_WARNING */
1015
1016 /* Prepare pixd to be a copy of pixs1 */
1017 if ((pixd = pixCopy(pixd, pixs1)) == NULL)
1018 return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
1019
1020 /* src1 ^ src2 --> dest */
1021 pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1022 PIX_SRC ^ PIX_DST, pixs2, 0, 0);
1023
1024 return pixd;
1025 }
1026
1027
1028 /*!
1029 * pixSubtract()
1030 *
1031 * Input: pixd (<optional>; this can be null, equal to pixs1,
1032 * equal to pixs2, or different from both pixs1 and pixs2)
1033 * pixs1 (can be == pixd)
1034 * pixs2 (can be == pixd)
1035 * Return: pixd always
1036 *
1037 * Notes:
1038 * (1) This gives the set subtraction of two images with equal depth,
1039 * aligning them to the the UL corner. pixs1 and pixs2
1040 * need not have the same width and height.
1041 * (2) Source pixs2 is always subtracted from source pixs1.
1042 * The result is
1043 * pixs1 \ pixs2 = pixs1 & (~pixs2)
1044 * (3) There are 4 cases:
1045 * (a) pixd == null, (src1 - src2) --> new pixd
1046 * (b) pixd == pixs1, (src1 - src2) --> src1 (in-place)
1047 * (c) pixd == pixs2, (src1 - src2) --> src2 (in-place)
1048 * (d) pixd != pixs1 && pixd != pixs2),
1049 * (src1 - src2) --> input pixd
1050 * (4) For clarity, if the case is known, use these patterns:
1051 * (a) pixd = pixSubtract(NULL, pixs1, pixs2);
1052 * (b) pixSubtract(pixs1, pixs1, pixs2);
1053 * (c) pixSubtract(pixs2, pixs1, pixs2);
1054 * (d) pixSubtract(pixd, pixs1, pixs2);
1055 * (5) The size of the result is determined by pixs1.
1056 * (6) The depths of pixs1 and pixs2 must be equal.
1057 */
1058 PIX *
pixSubtract(PIX * pixd,PIX * pixs1,PIX * pixs2)1059 pixSubtract(PIX *pixd,
1060 PIX *pixs1,
1061 PIX *pixs2)
1062 {
1063 l_int32 w, h;
1064
1065 PROCNAME("pixSubtract");
1066
1067 if (!pixs1)
1068 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1069 if (!pixs2)
1070 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1071 if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1072 return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
1073
1074 #if EQUAL_SIZE_WARNING
1075 if (!pixSizesEqual(pixs1, pixs2))
1076 L_WARNING("pixs1 and pixs2 not equal sizes", procName);
1077 #endif /* EQUAL_SIZE_WARNING */
1078
1079 pixGetDimensions(pixs1, &w, &h, NULL);
1080 if (!pixd) {
1081 pixd = pixCopy(NULL, pixs1);
1082 pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1083 pixs2, 0, 0); /* src1 & (~src2) */
1084 }
1085 else if (pixd == pixs1) {
1086 pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1087 pixs2, 0, 0); /* src1 & (~src2) */
1088 }
1089 else if (pixd == pixs2) {
1090 pixRasterop(pixd, 0, 0, w, h, PIX_NOT(PIX_DST) & PIX_SRC,
1091 pixs1, 0, 0); /* src1 & (~src2) */
1092 }
1093 else { /* pixd != pixs1 && pixd != pixs2 */
1094 pixCopy(pixd, pixs1); /* sizes pixd to pixs1 if unequal */
1095 pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1096 pixs2, 0, 0); /* src1 & (~src2) */
1097 }
1098
1099 return pixd;
1100 }
1101
1102
1103 /*-------------------------------------------------------------*
1104 * Pixel counting *
1105 *-------------------------------------------------------------*/
1106 /*!
1107 * pixZero()
1108 *
1109 * Input: pix
1110 * &empty (<return> 1 if all bits in image are 0; 0 otherwise)
1111 * Return: 0 if OK; 1 on error
1112 *
1113 * Notes:
1114 * (1) For a binary image, if there are no fg (black) pixels, empty = 1.
1115 * (2) For a grayscale image, if all pixels are black (0), empty = 1.
1116 * (3) For an RGB image, if all 4 components in every pixel is 0,
1117 * empty = 1.
1118 */
1119 l_int32
pixZero(PIX * pix,l_int32 * pempty)1120 pixZero(PIX *pix,
1121 l_int32 *pempty)
1122 {
1123 l_int32 w, h, wpl, i, j, fullwords, endbits;
1124 l_uint32 endmask;
1125 l_uint32 *data, *line;
1126
1127 PROCNAME("pixZero");
1128
1129 if (!pempty)
1130 return ERROR_INT("pempty not defined", procName, 1);
1131 *pempty = 1;
1132 if (!pix)
1133 return ERROR_INT("pix not defined", procName, 1);
1134
1135 w = pixGetWidth(pix) * pixGetDepth(pix);
1136 h = pixGetHeight(pix);
1137 wpl = pixGetWpl(pix);
1138 data = pixGetData(pix);
1139 fullwords = w / 32;
1140 endbits = w & 31;
1141 endmask = 0xffffffff << (32 - endbits);
1142
1143 for (i = 0; i < h; i++) {
1144 line = data + wpl * i;
1145 for (j = 0; j < fullwords; j++)
1146 if (*line++) {
1147 *pempty = 0;
1148 return 0;
1149 }
1150 if (endbits) {
1151 if (*line & endmask) {
1152 *pempty = 0;
1153 return 0;
1154 }
1155 }
1156 }
1157
1158 return 0;
1159 }
1160
1161
1162 /*!
1163 * pixCountPixels()
1164 *
1165 * Input: binary pix
1166 * &count (<return> count of ON pixels)
1167 * tab8 (<optional> 8-bit pixel lookup table)
1168 * Return: 0 if OK; 1 on error
1169 */
1170 l_int32
pixCountPixels(PIX * pix,l_int32 * pcount,l_int32 * tab8)1171 pixCountPixels(PIX *pix,
1172 l_int32 *pcount,
1173 l_int32 *tab8)
1174 {
1175 l_uint32 endmask;
1176 l_int32 w, h, wpl, i, j;
1177 l_int32 fullwords, endbits, sum;
1178 l_int32 *tab;
1179 l_uint32 *data;
1180
1181 PROCNAME("pixCountPixels");
1182
1183 if (!pcount)
1184 return ERROR_INT("pcount not defined", procName, 1);
1185 *pcount = 0;
1186 if (!pix || pixGetDepth(pix) != 1)
1187 return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
1188
1189 if (!tab8)
1190 tab = makePixelSumTab8();
1191 else
1192 tab = tab8;
1193
1194 pixGetDimensions(pix, &w, &h, NULL);
1195 wpl = pixGetWpl(pix);
1196 data = pixGetData(pix);
1197 fullwords = w >> 5;
1198 endbits = w & 31;
1199 endmask = 0xffffffff << (32 - endbits);
1200
1201 sum = 0;
1202 for (i = 0; i < h; i++, data += wpl) {
1203 for (j = 0; j < fullwords; j++) {
1204 l_uint32 word = data[j];
1205 if (word) {
1206 sum += tab[word & 0xff] +
1207 tab[(word >> 8) & 0xff] +
1208 tab[(word >> 16) & 0xff] +
1209 tab[(word >> 24) & 0xff];
1210 }
1211 }
1212 if (endbits) {
1213 l_uint32 word = data[j] & endmask;
1214 if (word) {
1215 sum += tab[word & 0xff] +
1216 tab[(word >> 8) & 0xff] +
1217 tab[(word >> 16) & 0xff] +
1218 tab[(word >> 24) & 0xff];
1219 }
1220 }
1221 }
1222 *pcount = sum;
1223
1224 if (!tab8)
1225 FREE(tab);
1226 return 0;
1227 }
1228
1229
1230 /*!
1231 * pixaCountPixels()
1232 *
1233 * Input: pixa (array of binary pix)
1234 * Return: na of ON pixels in each pix, or null on error
1235 */
1236 NUMA *
pixaCountPixels(PIXA * pixa)1237 pixaCountPixels(PIXA *pixa)
1238 {
1239 l_int32 d, i, n, count;
1240 l_int32 *tab;
1241 NUMA *na;
1242 PIX *pix;
1243
1244 PROCNAME("pixaCountPixels");
1245
1246 if (!pixa)
1247 return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
1248
1249 if ((n = pixaGetCount(pixa)) == 0)
1250 return numaCreate(1);
1251
1252 pix = pixaGetPix(pixa, 0, L_CLONE);
1253 d = pixGetDepth(pix);
1254 pixDestroy(&pix);
1255 if (d != 1)
1256 return (NUMA *)ERROR_PTR("pixa not 1 bpp", procName, NULL);
1257
1258 tab = makePixelSumTab8();
1259 if ((na = numaCreate(n)) == NULL)
1260 return (NUMA *)ERROR_PTR("na not made", procName, NULL);
1261 for (i = 0; i < n; i++) {
1262 pix = pixaGetPix(pixa, i, L_CLONE);
1263 pixCountPixels(pix, &count, tab);
1264 numaAddNumber(na, count);
1265 pixDestroy(&pix);
1266 }
1267
1268 FREE(tab);
1269 return na;
1270 }
1271
1272
1273 /*!
1274 * pixCountPixelsInRow()
1275 *
1276 * Input: binary pix
1277 * row number
1278 * &count (<return> sum of ON pixels in raster line)
1279 * tab8 (<optional> 8-bit pixel lookup table)
1280 * Return: 0 if OK; 1 on error
1281 */
1282 l_int32
pixCountPixelsInRow(PIX * pix,l_int32 row,l_int32 * pcount,l_int32 * tab8)1283 pixCountPixelsInRow(PIX *pix,
1284 l_int32 row,
1285 l_int32 *pcount,
1286 l_int32 *tab8)
1287 {
1288 l_uint32 word, endmask;
1289 l_int32 j, w, h, wpl;
1290 l_int32 fullwords, endbits, sum;
1291 l_int32 *tab;
1292 l_uint32 *line;
1293
1294 PROCNAME("pixCountPixelsInRow");
1295
1296 if (!pcount)
1297 return ERROR_INT("pcount not defined", procName, 1);
1298 *pcount = 0;
1299 if (!pix || pixGetDepth(pix) != 1)
1300 return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
1301
1302 pixGetDimensions(pix, &w, &h, NULL);
1303 if (row < 0 || row >= h)
1304 return ERROR_INT("row out of bounds", procName, 1);
1305 wpl = pixGetWpl(pix);
1306 line = pixGetData(pix) + row * wpl;
1307 fullwords = w >> 5;
1308 endbits = w & 31;
1309 endmask = 0xffffffff << (32 - endbits);
1310
1311 if (!tab8)
1312 tab = makePixelSumTab8();
1313 else
1314 tab = tab8;
1315
1316 sum = 0;
1317 for (j = 0; j < fullwords; j++) {
1318 word = line[j];
1319 if (word) {
1320 sum += tab[word & 0xff] +
1321 tab[(word >> 8) & 0xff] +
1322 tab[(word >> 16) & 0xff] +
1323 tab[(word >> 24) & 0xff];
1324 }
1325 }
1326 if (endbits) {
1327 word = line[j] & endmask;
1328 if (word) {
1329 sum += tab[word & 0xff] +
1330 tab[(word >> 8) & 0xff] +
1331 tab[(word >> 16) & 0xff] +
1332 tab[(word >> 24) & 0xff];
1333 }
1334 }
1335 *pcount = sum;
1336
1337 if (!tab8)
1338 FREE(tab);
1339 return 0;
1340 }
1341
1342
1343 /*!
1344 * pixCountPixelsByRow()
1345 *
1346 * Input: binary pix
1347 * tab8 (<optional> 8-bit pixel lookup table)
1348 * Return: na of counts, or null on error
1349 */
1350 NUMA *
pixCountPixelsByRow(PIX * pix,l_int32 * tab8)1351 pixCountPixelsByRow(PIX *pix,
1352 l_int32 *tab8)
1353 {
1354 l_int32 h, i, count;
1355 l_int32 *tab;
1356 NUMA *na;
1357
1358 PROCNAME("pixCountPixelsByRow");
1359
1360 if (!pix || pixGetDepth(pix) != 1)
1361 return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
1362
1363 if (!tab8)
1364 tab = makePixelSumTab8();
1365 else
1366 tab = tab8;
1367
1368 if ((na = numaCreate(h)) == NULL)
1369 return (NUMA *)ERROR_PTR("na not made", procName, NULL);
1370
1371 h = pixGetHeight(pix);
1372 for (i = 0; i < h; i++) {
1373 pixCountPixelsInRow(pix, i, &count, tab);
1374 numaAddNumber(na, count);
1375 }
1376
1377 if (!tab8)
1378 FREE(tab);
1379
1380 return na;
1381 }
1382
1383
1384 /*!
1385 * pixThresholdPixels()
1386 *
1387 * Input: binary pix
1388 * threshold
1389 * &above (<return> 1 if above threshold;
1390 * 0 if equal to or less than threshold)
1391 * tab8 (<optional> 8-bit pixel lookup table)
1392 * Return: 0 if OK; 1 on error
1393 *
1394 * Notes:
1395 * (1) This sums the ON pixels and returns immediately if the count
1396 * goes above threshold. It is therefore more efficient
1397 * for matching images (by running this function on the xor of
1398 * the 2 images) than using pixCountPixels(), which counts all
1399 * pixels before returning.
1400 */
1401 l_int32
pixThresholdPixels(PIX * pix,l_int32 thresh,l_int32 * pabove,l_int32 * tab8)1402 pixThresholdPixels(PIX *pix,
1403 l_int32 thresh,
1404 l_int32 *pabove,
1405 l_int32 *tab8)
1406 {
1407 l_uint32 word, endmask;
1408 l_int32 *tab;
1409 l_int32 w, h, wpl, i, j;
1410 l_int32 fullwords, endbits, sum;
1411 l_uint32 *line, *data;
1412
1413 PROCNAME("pixThresholdPixels");
1414
1415 if (!pabove)
1416 return ERROR_INT("pabove not defined", procName, 1);
1417 *pabove = 0;
1418 if (!pix || pixGetDepth(pix) != 1)
1419 return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
1420
1421 if (!tab8)
1422 tab = makePixelSumTab8();
1423 else
1424 tab = tab8;
1425
1426 pixGetDimensions(pix, &w, &h, NULL);
1427 wpl = pixGetWpl(pix);
1428 data = pixGetData(pix);
1429 fullwords = w >> 5;
1430 endbits = w & 31;
1431 endmask = 0xffffffff << (32 - endbits);
1432
1433 sum = 0;
1434 for (i = 0; i < h; i++) {
1435 line = data + wpl * i;
1436 for (j = 0; j < fullwords; j++) {
1437 word = line[j];
1438 if (word) {
1439 sum += tab[word & 0xff] +
1440 tab[(word >> 8) & 0xff] +
1441 tab[(word >> 16) & 0xff] +
1442 tab[(word >> 24) & 0xff];
1443 }
1444 }
1445 if (endbits) {
1446 word = line[j] & endmask;
1447 if (word) {
1448 sum += tab[word & 0xff] +
1449 tab[(word >> 8) & 0xff] +
1450 tab[(word >> 16) & 0xff] +
1451 tab[(word >> 24) & 0xff];
1452 }
1453 }
1454 if (sum > thresh) {
1455 *pabove = 1;
1456 if (!tab8)
1457 FREE(tab);
1458 return 0;
1459 }
1460 }
1461
1462 if (!tab8)
1463 FREE(tab);
1464 return 0;
1465 }
1466
1467
1468 /*!
1469 * makePixelSumTab8()
1470 *
1471 * Input: void
1472 * Return: table of 256 l_int32, or null on error
1473 *
1474 * Notes:
1475 * (1) This table of integers gives the number of 1 bits
1476 * in the 8 bit index.
1477 */
1478 l_int32 *
makePixelSumTab8(void)1479 makePixelSumTab8(void)
1480 {
1481 l_uint8 byte;
1482 l_int32 i;
1483 l_int32 *tab;
1484
1485 PROCNAME("makePixelSumTab8");
1486
1487 if ((tab = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
1488 return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
1489
1490 for (i = 0; i < 256; i++) {
1491 byte = (l_uint8)i;
1492 tab[i] = (byte & 0x1) +
1493 ((byte >> 1) & 0x1) +
1494 ((byte >> 2) & 0x1) +
1495 ((byte >> 3) & 0x1) +
1496 ((byte >> 4) & 0x1) +
1497 ((byte >> 5) & 0x1) +
1498 ((byte >> 6) & 0x1) +
1499 ((byte >> 7) & 0x1);
1500 }
1501
1502 return tab;
1503 }
1504
1505
1506 /*!
1507 * makePixelCentroidTab8()
1508 *
1509 * Input: void
1510 * Return: table of 256 l_int32, or null on error
1511 *
1512 * Notes:
1513 * (1) This table of integers gives the centroid weight of the 1 bits
1514 * in the 8 bit index. In other words, if sumtab is obtained by
1515 * makePixelSumTab8, and centroidtab is obtained by
1516 * makePixelCentroidTab8, then, for 1 <= i <= 255,
1517 * centroidtab[i] / (float)sumtab[i]
1518 * is the centroid of the 1 bits in the 8-bit index i, where the
1519 * MSB is considered to have position 0 and the LSB is considered
1520 * to have position 7.
1521 */
1522 l_int32 *
makePixelCentroidTab8(void)1523 makePixelCentroidTab8(void)
1524 {
1525 l_int32 i;
1526 l_int32 *tab;
1527
1528 PROCNAME("makePixelCentroidTab8");
1529
1530 if ((tab = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
1531 return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
1532
1533 tab[0] = 0;
1534 tab[1] = 7;
1535 for (i = 2; i < 4; i++) {
1536 tab[i] = tab[i - 2] + 6;
1537 }
1538 for (i = 4; i < 8; i++) {
1539 tab[i] = tab[i - 4] + 5;
1540 }
1541 for (i = 8; i < 16; i++) {
1542 tab[i] = tab[i - 8] + 4;
1543 }
1544 for (i = 16; i < 32; i++) {
1545 tab[i] = tab[i - 16] + 3;
1546 }
1547 for (i = 32; i < 64; i++) {
1548 tab[i] = tab[i - 32] + 2;
1549 }
1550 for (i = 64; i < 128; i++) {
1551 tab[i] = tab[i - 64] + 1;
1552 }
1553 for (i = 128; i < 256; i++) {
1554 tab[i] = tab[i - 128];
1555 }
1556
1557 return tab;
1558 }
1559
1560
1561 /*-------------------------------------------------------------*
1562 * Sum of pixel values *
1563 *-------------------------------------------------------------*/
1564 /*!
1565 * pixSumPixelValues()
1566 *
1567 * Input: pix (1, 2, 4, 8, 16, 32 bpp; not cmapped)
1568 * box (<optional> if null, use entire image)
1569 * &sum (<return> sum of pixel values in region)
1570 * Return: 0 if OK; 1 on error
1571 */
1572 l_int32
pixSumPixelValues(PIX * pix,BOX * box,l_float64 * psum)1573 pixSumPixelValues(PIX *pix,
1574 BOX *box,
1575 l_float64 *psum)
1576 {
1577 l_int32 w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh;
1578 l_uint32 *data, *line;
1579 l_float64 sum;
1580 BOX *boxc;
1581
1582 PROCNAME("pixSumPixelValues");
1583
1584 if (!psum)
1585 return ERROR_INT("psum not defined", procName, 1);
1586 *psum = 0;
1587 if (!pix)
1588 return ERROR_INT("pix not defined", procName, 1);
1589 if (pixGetColormap(pix) != NULL)
1590 return ERROR_INT("pix is colormapped", procName, 1);
1591 pixGetDimensions(pix, &w, &h, &d);
1592 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
1593 return ERROR_INT("pix not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
1594
1595 wpl = pixGetWpl(pix);
1596 data = pixGetData(pix);
1597 boxc = NULL;
1598 if (box) {
1599 boxc = boxClipToRectangle(box, w, h);
1600 if (!boxc)
1601 return ERROR_INT("box outside image", procName, 1);
1602 }
1603 xstart = ystart = 0;
1604 xend = w;
1605 yend = h;
1606 if (boxc) {
1607 boxGetGeometry(boxc, &xstart, &ystart, &bw, &bh);
1608 xend = xstart + bw; /* 1 past the end */
1609 yend = ystart + bh; /* 1 past the end */
1610 boxDestroy(&boxc);
1611 }
1612
1613 sum = 0;
1614 for (i = ystart; i < yend; i++) {
1615 line = data + i * wpl;
1616 for (j = xstart; j < xend; j++) {
1617 if (d == 1)
1618 sum += GET_DATA_BIT(line, j);
1619 else if (d == 2)
1620 sum += GET_DATA_DIBIT(line, j);
1621 else if (d == 4)
1622 sum += GET_DATA_QBIT(line, j);
1623 else if (d == 8)
1624 sum += GET_DATA_BYTE(line, j);
1625 else if (d == 16)
1626 sum += GET_DATA_TWO_BYTES(line, j);
1627 else if (d == 32)
1628 sum += line[j];
1629 }
1630 }
1631 *psum = sum;
1632
1633 return 0;
1634 }
1635
1636
1637
1638 /*-------------------------------------------------------------*
1639 * Mirrored tiling of a smaller image *
1640 *-------------------------------------------------------------*/
1641 /*!
1642 * pixMirroredTiling()
1643 *
1644 * Input: pixs (8 or 32 bpp, small tile; to be replicated)
1645 * w, h (dimensions of output pix)
1646 * Return: pixd (usually larger pix, mirror-tiled with pixs),
1647 * or null on error
1648 *
1649 * Notes:
1650 * (1) This uses mirrored tiling, where each row alternates
1651 * with LR flips and every column alternates with TB
1652 * flips, such that the result is a tiling with identical
1653 * 2 x 2 tiles, each of which is composed of these transforms:
1654 * -----------------
1655 * | 1 | LR |
1656 * -----------------
1657 * | TB | LR/TB |
1658 * -----------------
1659 */
1660 PIX *
pixMirroredTiling(PIX * pixs,l_int32 w,l_int32 h)1661 pixMirroredTiling(PIX *pixs,
1662 l_int32 w,
1663 l_int32 h)
1664 {
1665 l_int32 wt, ht, d, i, j, nx, ny;
1666 PIX *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix;
1667
1668 PROCNAME("pixMirroredTiling");
1669
1670 if (!pixs)
1671 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1672 pixGetDimensions(pixs, &wt, &ht, &d);
1673 if (wt <= 0 || ht <= 0)
1674 return (PIX *)ERROR_PTR("pixs size illegal", procName, NULL);
1675 if (d != 8 && d != 32)
1676 return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL);
1677 if ((pixd = pixCreate(w, h, d)) == NULL)
1678 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1679
1680 nx = (w + wt - 1) / wt;
1681 ny = (h + ht - 1) / ht;
1682 pixsfx = pixFlipLR(NULL, pixs);
1683 pixsfy = pixFlipTB(NULL, pixs);
1684 pixsfxy = pixFlipTB(NULL, pixsfx);
1685 for (i = 0; i < ny; i++) {
1686 for (j = 0; j < nx; j++) {
1687 pix = pixs;
1688 if ((i & 1) && !(j & 1))
1689 pix = pixsfy;
1690 else if (!(i & 1) && (j & 1))
1691 pix = pixsfx;
1692 else if ((i & 1) && (j & 1))
1693 pix = pixsfxy;
1694 pixRasterop(pixd, j * wt, i * ht, wt, ht, PIX_SRC, pix, 0, 0);
1695 }
1696 }
1697
1698 pixDestroy(&pixsfx);
1699 pixDestroy(&pixsfy);
1700 pixDestroy(&pixsfxy);
1701 return pixd;
1702 }
1703
1704
1705 /*!
1706 * findTilePatchCenter()
1707 *
1708 * Input: pixs (8 or 16 bpp; distance function of a binary mask)
1709 * box (region of pixs to search around)
1710 * searchdir (L_HORIZ or L_VERT; direction to search)
1711 * targdist (desired distance of selected patch center from fg)
1712 * &dist (<return> actual distance of selected location)
1713 * &xc, &yc (<return> location of selected patch center)
1714 * Return: 0 if OK, 1 on error
1715 *
1716 * Notes:
1717 * (1) This looks for a patch of non-masked image, that is outside
1718 * but near the input box. The input pixs is a distance
1719 * function giving the distance from the fg in a binary mask.
1720 * (2) The target distance implicitly specifies a desired size
1721 * for the patch. The location of the center of the patch,
1722 * and the actual distance from fg are returned.
1723 * (3) If the target distance is larger than 255, a 16-bit distance
1724 * transform is input.
1725 * (4) It is assured that a square centered at (xc, yc) and of
1726 * size 'dist' will not intersect with the fg of the binary
1727 * mask that was used to generate pixs.
1728 */
1729 static l_int32
findTilePatchCenter(PIX * pixs,BOX * box,l_int32 searchdir,l_uint32 targdist,l_uint32 * pdist,l_int32 * pxc,l_int32 * pyc)1730 findTilePatchCenter(PIX *pixs,
1731 BOX *box,
1732 l_int32 searchdir,
1733 l_uint32 targdist,
1734 l_uint32 *pdist,
1735 l_int32 *pxc,
1736 l_int32 *pyc)
1737 {
1738 l_int32 w, h, bx, by, bw, bh, left, right, top, bot, i, j;
1739 l_uint32 val, maxval;
1740
1741 PROCNAME("findTilePatchCenter");
1742
1743 if (!pdist || !pxc || !pyc)
1744 return ERROR_INT("&pdist, &pxc, &pyc not all defined", procName, 1);
1745 *pdist = *pxc = *pyc = 0;
1746 if (!pixs)
1747 return ERROR_INT("pixs not defined", procName, 1);
1748 if (!box)
1749 return ERROR_INT("box not defined", procName, 1);
1750
1751 pixGetDimensions(pixs, &w, &h, NULL);
1752 boxGetGeometry(box, &bx, &by, &bw, &bh);
1753
1754 if (searchdir == L_HORIZ) {
1755 left = bx; /* distance to left of box */
1756 right = w - bx - bw + 1; /* distance to right of box */
1757 maxval = 0;
1758 if (left > right) { /* search to left */
1759 for (j = bx - 1; j >= 0; j--) {
1760 for (i = by; i < by + bh; i++) {
1761 pixGetPixel(pixs, j, i, &val);
1762 if (val > maxval) {
1763 maxval = val;
1764 *pxc = j;
1765 *pyc = i;
1766 *pdist = val;
1767 if (val >= targdist)
1768 return 0;
1769 }
1770 }
1771 }
1772 }
1773 else { /* search to right */
1774 for (j = bx + bw; j < w; j++) {
1775 for (i = by; i < by + bh; i++) {
1776 pixGetPixel(pixs, j, i, &val);
1777 if (val > maxval) {
1778 maxval = val;
1779 *pxc = j;
1780 *pyc = i;
1781 *pdist = val;
1782 if (val >= targdist)
1783 return 0;
1784 }
1785 }
1786 }
1787 }
1788 }
1789 else { /* searchdir == L_VERT */
1790 top = by; /* distance above box */
1791 bot = h - by - bh + 1; /* distance below box */
1792 maxval = 0;
1793 if (top > bot) { /* search above */
1794 for (i = by - 1; i >= 0; i--) {
1795 for (j = bx; j < bx + bw; j++) {
1796 pixGetPixel(pixs, j, i, &val);
1797 if (val > maxval) {
1798 maxval = val;
1799 *pxc = j;
1800 *pyc = i;
1801 *pdist = val;
1802 if (val >= targdist)
1803 return 0;
1804 }
1805 }
1806 }
1807 }
1808 else { /* search below */
1809 for (i = by + bh; i < h; i++) {
1810 for (j = bx; j < bx + bw; j++) {
1811 pixGetPixel(pixs, j, i, &val);
1812 if (val > maxval) {
1813 maxval = val;
1814 *pxc = j;
1815 *pyc = i;
1816 *pdist = val;
1817 if (val >= targdist)
1818 return 0;
1819 }
1820 }
1821 }
1822 }
1823 }
1824
1825
1826 pixGetPixel(pixs, *pxc, *pyc, pdist);
1827 return 0;
1828 }
1829
1830