Leptonica  1.83.1
Image processing and image analysis suite
pix3.c
Go to the documentation of this file.
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 
112 #ifdef HAVE_CONFIG_H
113 #include <config_auto.h>
114 #endif /* HAVE_CONFIG_H */
115 
116 #include <string.h>
117 #include <math.h>
118 #include "allheaders.h"
119 
120 static BOXA *findTileRegionsForSearch(BOX *box, l_int32 w, l_int32 h,
121  l_int32 searchdir, l_int32 mindist,
122  l_int32 tsize, l_int32 ntiles);
123 
124 #ifndef NO_CONSOLE_IO
125 #define EQUAL_SIZE_WARNING 0
126 #endif /* ~NO_CONSOLE_IO */
127 
128 /*-------------------------------------------------------------*
129  * Masked operations *
130  *-------------------------------------------------------------*/
162 l_ok
164  PIX *pixm,
165  l_uint32 val)
166 {
167 l_int32 wd, hd, wm, hm, w, h, d, wpld, wplm;
168 l_int32 i, j, rval, gval, bval;
169 l_uint32 *datad, *datam, *lined, *linem;
170 
171  if (!pixd)
172  return ERROR_INT("pixd not defined", __func__, 1);
173  if (!pixm) {
174  L_WARNING("no mask; nothing to do\n", __func__);
175  return 0;
176  }
177  if (pixGetColormap(pixd)) {
178  extractRGBValues(val, &rval, &gval, &bval);
179  return pixSetMaskedCmap(pixd, pixm, 0, 0, rval, gval, bval);
180  }
181 
182  if (pixGetDepth(pixm) != 1)
183  return ERROR_INT("pixm not 1 bpp", __func__, 1);
184  d = pixGetDepth(pixd);
185  if (d == 1)
186  val &= 1;
187  else if (d == 2)
188  val &= 3;
189  else if (d == 4)
190  val &= 0x0f;
191  else if (d == 8)
192  val &= 0xff;
193  else if (d == 16)
194  val &= 0xffff;
195  else if (d != 32)
196  return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", __func__, 1);
197  pixGetDimensions(pixm, &wm, &hm, NULL);
198 
199  /* If d == 1, use rasterop; it's about 25x faster */
200  if (d == 1) {
201  if (val == 0) {
202  PIX *pixmi = pixInvert(NULL, pixm);
203  pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmi, 0, 0);
204  pixDestroy(&pixmi);
205  } else { /* val == 1 */
206  pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixm, 0, 0);
207  }
208  return 0;
209  }
210 
211  /* For d < 32, use rasterop for val == 0 (black); ~3x faster. */
212  if (d < 32 && val == 0) {
213  PIX *pixmd = pixUnpackBinary(pixm, d, 1);
214  pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmd, 0, 0);
215  pixDestroy(&pixmd);
216  return 0;
217  }
218 
219  /* For d < 32, use rasterop for val == maxval (white); ~3x faster. */
220  if (d < 32 && val == ((1 << d) - 1)) {
221  PIX *pixmd = pixUnpackBinary(pixm, d, 0);
222  pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixmd, 0, 0);
223  pixDestroy(&pixmd);
224  return 0;
225  }
226 
227  pixGetDimensions(pixd, &wd, &hd, &d);
228  w = L_MIN(wd, wm);
229  h = L_MIN(hd, hm);
230  if (L_ABS(wd - wm) > 7 || L_ABS(hd - hm) > 7) /* allow a small tolerance */
231  L_WARNING("pixd and pixm sizes differ\n", __func__);
232 
233  datad = pixGetData(pixd);
234  datam = pixGetData(pixm);
235  wpld = pixGetWpl(pixd);
236  wplm = pixGetWpl(pixm);
237  for (i = 0; i < h; i++) {
238  lined = datad + i * wpld;
239  linem = datam + i * wplm;
240  for (j = 0; j < w; j++) {
241  if (GET_DATA_BIT(linem, j)) {
242  switch(d)
243  {
244  case 2:
245  SET_DATA_DIBIT(lined, j, val);
246  break;
247  case 4:
248  SET_DATA_QBIT(lined, j, val);
249  break;
250  case 8:
251  SET_DATA_BYTE(lined, j, val);
252  break;
253  case 16:
254  SET_DATA_TWO_BYTES(lined, j, val);
255  break;
256  case 32:
257  *(lined + j) = val;
258  break;
259  default:
260  return ERROR_INT("shouldn't get here", __func__, 1);
261  }
262  }
263  }
264  }
265 
266  return 0;
267 }
268 
269 
299 l_ok
301  PIX *pixm,
302  l_uint32 val,
303  l_int32 x,
304  l_int32 y)
305 {
306 l_int32 wm, hm, d;
307 PIX *pixmu, *pixc;
308 
309  if (!pixd)
310  return ERROR_INT("pixd not defined", __func__, 1);
311  if (!pixm) /* nothing to do */
312  return 0;
313 
314  d = pixGetDepth(pixd);
315  if (d != 8 && d != 16 && d != 32)
316  return ERROR_INT("pixd not 8, 16 or 32 bpp", __func__, 1);
317  if (pixGetDepth(pixm) != 1)
318  return ERROR_INT("pixm not 1 bpp", __func__, 1);
319 
320  /* Unpack binary to depth d, with inversion: 1 --> 0, 0 --> 0xff... */
321  if ((pixmu = pixUnpackBinary(pixm, d, 1)) == NULL)
322  return ERROR_INT("pixmu not made", __func__, 1);
323 
324  /* Clear stenciled pixels in pixd */
325  pixGetDimensions(pixm, &wm, &hm, NULL);
326  pixRasterop(pixd, x, y, wm, hm, PIX_SRC & PIX_DST, pixmu, 0, 0);
327 
328  /* Generate image with requisite color */
329  if ((pixc = pixCreateTemplate(pixmu)) == NULL) {
330  pixDestroy(&pixmu);
331  return ERROR_INT("pixc not made", __func__, 1);
332  }
333  pixSetAllArbitrary(pixc, val);
334 
335  /* Invert stencil mask, and paint color color into stencil */
336  pixInvert(pixmu, pixmu);
337  pixAnd(pixmu, pixmu, pixc);
338 
339  /* Finally, repaint stenciled pixels, with val, in pixd */
340  pixRasterop(pixd, x, y, wm, hm, PIX_SRC | PIX_DST, pixmu, 0, 0);
341 
342  pixDestroy(&pixmu);
343  pixDestroy(&pixc);
344  return 0;
345 }
346 
347 
377 l_ok
379  PIX *pixs,
380  PIX *pixm)
381 {
382 l_int32 w, h, d, ws, hs, ds, wm, hm, dm, wmin, hmin;
383 l_int32 wpl, wpls, wplm, i, j, val;
384 l_uint32 *data, *datas, *datam, *line, *lines, *linem;
385 PIX *pixt;
386 
387  if (!pixm) /* nothing to do */
388  return 0;
389  if (!pixd)
390  return ERROR_INT("pixd not defined", __func__, 1);
391  if (!pixs)
392  return ERROR_INT("pixs not defined", __func__, 1);
393  pixGetDimensions(pixd, &w, &h, &d);
394  pixGetDimensions(pixs, &ws, &hs, &ds);
395  pixGetDimensions(pixm, &wm, &hm, &dm);
396  if (d != ds)
397  return ERROR_INT("pixs and pixd depths differ", __func__, 1);
398  if (dm != 1)
399  return ERROR_INT("pixm not 1 bpp", __func__, 1);
400  if (d != 1 && d != 8 && d != 32)
401  return ERROR_INT("pixd not 1, 8 or 32 bpp", __func__, 1);
402  if (pixGetColormap(pixd) || pixGetColormap(pixs))
403  return ERROR_INT("pixs and/or pixd is cmapped", __func__, 1);
404 
405  /* For d = 1, use rasterop. pixt is the part from pixs, under
406  * the fg of pixm, that is to be combined with pixd. We also
407  * use pixt to remove all fg of pixd that is under the fg of pixm.
408  * Then pixt and pixd are combined by ORing. */
409  wmin = L_MIN(w, L_MIN(ws, wm));
410  hmin = L_MIN(h, L_MIN(hs, hm));
411  if (d == 1) {
412  pixt = pixAnd(NULL, pixs, pixm);
413  pixRasterop(pixd, 0, 0, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
414  pixm, 0, 0);
415  pixRasterop(pixd, 0, 0, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
416  pixDestroy(&pixt);
417  return 0;
418  }
419 
420  data = pixGetData(pixd);
421  datas = pixGetData(pixs);
422  datam = pixGetData(pixm);
423  wpl = pixGetWpl(pixd);
424  wpls = pixGetWpl(pixs);
425  wplm = pixGetWpl(pixm);
426  if (d == 8) {
427  for (i = 0; i < hmin; i++) {
428  line = data + i * wpl;
429  lines = datas + i * wpls;
430  linem = datam + i * wplm;
431  for (j = 0; j < wmin; j++) {
432  if (GET_DATA_BIT(linem, j)) {
433  val = GET_DATA_BYTE(lines, j);
434  SET_DATA_BYTE(line, j, val);
435  }
436  }
437  }
438  } else { /* d == 32 */
439  for (i = 0; i < hmin; i++) {
440  line = data + i * wpl;
441  lines = datas + i * wpls;
442  linem = datam + i * wplm;
443  for (j = 0; j < wmin; j++) {
444  if (GET_DATA_BIT(linem, j))
445  line[j] = lines[j];
446  }
447  }
448  }
449 
450  return 0;
451 }
452 
453 
494 l_ok
496  PIX *pixs,
497  PIX *pixm,
498  l_int32 x,
499  l_int32 y)
500 {
501 l_int32 d, w, h, ws, hs, ds, wm, hm, dm, wmin, hmin;
502 l_int32 wpl, wpls, wplm, i, j, val;
503 l_uint32 *data, *datas, *datam, *line, *lines, *linem;
504 PIX *pixt;
505 
506  if (!pixm) /* nothing to do */
507  return 0;
508  if (!pixd)
509  return ERROR_INT("pixd not defined", __func__, 1);
510  if (!pixs)
511  return ERROR_INT("pixs not defined", __func__, 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", __func__, 1);
517  if (dm != 1)
518  return ERROR_INT("pixm not 1 bpp", __func__, 1);
519  if (d != 1 && d != 8 && d != 32)
520  return ERROR_INT("pixd not 1, 8 or 32 bpp", __func__, 1);
521  if (pixGetColormap(pixd) || pixGetColormap(pixs))
522  return ERROR_INT("pixs and/or pixd is cmapped", __func__, 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", __func__, 1);
565  }
566  }
567  }
568  }
569 
570  return 0;
571 }
572 
573 
617 l_ok
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  if (!pixm) /* nothing to do */
628  return 0;
629  if (!pixd)
630  return ERROR_INT("pixd not defined", __func__, 1);
631  if (pixGetColormap(pixd)) {
632  extractRGBValues(val, &rval, &gval, &bval);
633  return pixSetMaskedCmap(pixd, pixm, x, y, rval, gval, bval);
634  }
635 
636  if (pixGetDepth(pixm) != 1)
637  return ERROR_INT("pixm not 1 bpp", __func__, 1);
638  d = pixGetDepth(pixd);
639  if (d == 1)
640  val &= 1;
641  else if (d == 2)
642  val &= 3;
643  else if (d == 4)
644  val &= 0x0f;
645  else if (d == 8)
646  val &= 0xff;
647  else if (d == 16)
648  val &= 0xffff;
649  else if (d != 32)
650  return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", __func__, 1);
651  pixGetDimensions(pixm, &wm, &hm, NULL);
652 
653  /* If d == 1, use rasterop; it's about 25x faster. */
654  if (d == 1) {
655  if (val == 0) {
656  PIX *pixmi = pixInvert(NULL, pixm);
657  pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmi, 0, 0);
658  pixDestroy(&pixmi);
659  } else { /* val == 1 */
660  pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixm, 0, 0);
661  }
662  return 0;
663  }
664 
665  /* For d < 32, use rasterop if val == 0 (black); ~3x faster. */
666  if (d < 32 && val == 0) {
667  PIX *pixmd = pixUnpackBinary(pixm, d, 1);
668  pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmd, 0, 0);
669  pixDestroy(&pixmd);
670  return 0;
671  }
672 
673  /* For d < 32, use rasterop if val == maxval (white); ~3x faster. */
674  if (d < 32 && val == ((1 << d) - 1)) {
675  PIX *pixmd = pixUnpackBinary(pixm, d, 0);
676  pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixmd, 0, 0);
677  pixDestroy(&pixmd);
678  return 0;
679  }
680 
681  /* All other cases */
682  pixGetDimensions(pixd, &w, &h, NULL);
683  wpl = pixGetWpl(pixd);
684  data = pixGetData(pixd);
685  wplm = pixGetWpl(pixm);
686  datam = pixGetData(pixm);
687  for (i = 0; i < hm; i++) {
688  if (y + i < 0 || y + i >= h) continue;
689  line = data + (y + i) * wpl;
690  linem = datam + i * wplm;
691  for (j = 0; j < wm; j++) {
692  if (x + j < 0 || x + j >= w) continue;
693  if (GET_DATA_BIT(linem, j)) {
694  switch (d)
695  {
696  case 2:
697  SET_DATA_DIBIT(line, x + j, val);
698  break;
699  case 4:
700  SET_DATA_QBIT(line, x + j, val);
701  break;
702  case 8:
703  SET_DATA_BYTE(line, x + j, val);
704  break;
705  case 16:
706  SET_DATA_TWO_BYTES(line, x + j, val);
707  break;
708  case 32:
709  *(line + x + j) = val;
710  break;
711  default:
712  return ERROR_INT("shouldn't get here", __func__, 1);
713  }
714  }
715  }
716  }
717 
718  return 0;
719 }
720 
721 
748 PIX *
750  BOXA *boxa,
751  l_int32 background)
752 {
753 l_int32 i, n, x, y, w, h;
754 PIX *pixd;
755 
756  if (!pixs)
757  return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
758  if (!boxa)
759  return (PIX *)ERROR_PTR("boxa not defined", __func__, NULL);
760  if (background != L_SET_WHITE && background != L_SET_BLACK)
761  return (PIX *)ERROR_PTR("invalid background", __func__, NULL);
762 
763  pixd = pixCreateTemplate(pixs);
764  pixSetBlackOrWhite(pixd, background);
765  n = boxaGetCount(boxa);
766  for (i = 0; i < n; i++) {
767  boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
768  pixRasterop(pixd, x, y, w, h, PIX_SRC, pixs, x, y);
769  }
770  return pixd;
771 }
772 
773 
835 l_ok
837  PIX *pixm,
838  l_int32 x,
839  l_int32 y,
840  l_int32 searchdir,
841  l_int32 mindist,
842  l_int32 tilesize,
843  l_int32 ntiles,
844  l_int32 distblend)
845 {
846 l_int32 w, h, d, wm, hm, dm, i, n, bx, by, bw, bh, edgeblend, retval, minside;
847 l_uint32 pixval;
848 BOX *box, *boxv, *boxh;
849 BOXA *boxa;
850 PIX *pixf, *pixv, *pixh, *pix1, *pix2, *pix3, *pix4, *pix5;
851 PIXA *pixa;
852 
853  if (!pixm) /* nothing to do */
854  return 0;
855  if (!pixd)
856  return ERROR_INT("pixd not defined", __func__, 1);
857  if (pixGetColormap(pixd) != NULL)
858  return ERROR_INT("pixd has colormap", __func__, 1);
859  pixGetDimensions(pixd, &w, &h, &d);
860  if (d != 8 && d != 32)
861  return ERROR_INT("pixd not 8 or 32 bpp", __func__, 1);
862  pixGetDimensions(pixm, &wm, &hm, &dm);
863  if (dm != 1)
864  return ERROR_INT("pixm not 1 bpp", __func__, 1);
865  if (x < 0 || y < 0)
866  return ERROR_INT("x and y must be non-negative", __func__, 1);
867  if (searchdir != L_HORIZ && searchdir != L_VERT &&
868  searchdir != L_BOTH_DIRECTIONS)
869  return ERROR_INT("invalid searchdir", __func__, 1);
870  if (tilesize < 2)
871  return ERROR_INT("tilesize must be >= 2", __func__, 1);
872  if (distblend < 0)
873  return ERROR_INT("distblend must be >= 0", __func__, 1);
874 
875  /* Embed mask in full sized mask */
876  if (wm < w || hm < h) {
877  pixf = pixCreate(w, h, 1);
878  pixRasterop(pixf, x, y, wm, hm, PIX_SRC, pixm, 0, 0);
879  } else {
880  pixf = pixCopy(NULL, pixm);
881  }
882 
883  /* Get connected components of mask */
884  boxa = pixConnComp(pixf, &pixa, 8);
885  if ((n = pixaGetCount(pixa)) == 0) {
886  L_WARNING("no fg in mask\n", __func__);
887  pixDestroy(&pixf);
888  pixaDestroy(&pixa);
889  boxaDestroy(&boxa);
890  return 1;
891  }
892  boxaDestroy(&boxa);
893 
894  /* For each c.c., generate one or two representative tiles for
895  * texturizing and apply through the mask. The input 'tilesize'
896  * is the requested value. Note that if there is exactly one
897  * component, and blending at the edge is requested, an alpha mask
898  * is generated, which is larger than the bounding box of the c.c. */
899  edgeblend = (n == 1 && distblend > 0) ? 1 : 0;
900  if (distblend > 0 && n > 1)
901  L_WARNING("%d components; can not blend at edges\n", __func__, n);
902  retval = 0;
903  for (i = 0; i < n; i++) {
904  if (edgeblend) {
905  pix1 = pixMakeAlphaFromMask(pixf, distblend, &box);
906  } else {
907  pix1 = pixaGetPix(pixa, i, L_CLONE);
908  box = pixaGetBox(pixa, i, L_CLONE);
909  }
910  boxGetGeometry(box, &bx, &by, &bw, &bh);
911  minside = L_MIN(bw, bh);
912 
913  boxh = boxv = NULL;
914  if (searchdir == L_HORIZ || searchdir == L_BOTH_DIRECTIONS) {
915  pixFindRepCloseTile(pixd, box, L_HORIZ, mindist,
916  L_MIN(minside, tilesize), ntiles, &boxh, 0);
917  }
918  if (searchdir == L_VERT || searchdir == L_BOTH_DIRECTIONS) {
919  pixFindRepCloseTile(pixd, box, L_VERT, mindist,
920  L_MIN(minside, tilesize), ntiles, &boxv, 0);
921  }
922  if (!boxh && !boxv) {
923  L_WARNING("tile region not selected; paint color near boundary\n",
924  __func__);
925  pixDestroy(&pix1);
926  pix1 = pixaGetPix(pixa, i, L_CLONE);
927  pixaGetBoxGeometry(pixa, i, &bx, &by, NULL, NULL);
928  retval = pixGetColorNearMaskBoundary(pixd, pixm, box, distblend,
929  &pixval, 0);
930  pixSetMaskedGeneral(pixd, pix1, pixval, bx, by);
931  pixDestroy(&pix1);
932  boxDestroy(&box);
933  continue;
934  }
935 
936  /* Extract the selected squares from pixd */
937  pixh = (boxh) ? pixClipRectangle(pixd, boxh, NULL) : NULL;
938  pixv = (boxv) ? pixClipRectangle(pixd, boxv, NULL) : NULL;
939  if (pixh && pixv)
940  pix2 = pixBlend(pixh, pixv, 0, 0, 0.5);
941  else if (pixh)
942  pix2 = pixClone(pixh);
943  else /* pixv */
944  pix2 = pixClone(pixv);
945  pixDestroy(&pixh);
946  pixDestroy(&pixv);
947  boxDestroy(&boxh);
948  boxDestroy(&boxv);
949 
950  /* Generate an image the size of the b.b. of the c.c.,
951  * possibly extended by the blending distance, which
952  * is then either painted through the c.c. mask or
953  * blended using the alpha mask for that c.c. */
954  pix3 = pixMirroredTiling(pix2, bw, bh);
955  if (edgeblend) {
956  pix4 = pixClipRectangle(pixd, box, NULL);
957  pix5 = pixBlendWithGrayMask(pix4, pix3, pix1, 0, 0);
958  pixRasterop(pixd, bx, by, bw, bh, PIX_SRC, pix5, 0, 0);
959  pixDestroy(&pix4);
960  pixDestroy(&pix5);
961  } else {
962  pixCombineMaskedGeneral(pixd, pix3, pix1, bx, by);
963  }
964  pixDestroy(&pix1);
965  pixDestroy(&pix2);
966  pixDestroy(&pix3);
967  boxDestroy(&box);
968  }
969 
970  pixaDestroy(&pixa);
971  pixDestroy(&pixf);
972  return retval;
973 }
974 
975 
990 PIX *
992  l_int32 val)
993 {
994 l_int32 w, h, d, i, j, sval, wpls, wpld;
995 l_uint32 *datas, *datad, *lines, *lined;
996 PIX *pixd;
997 
998  if (!pixs)
999  return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1000  pixGetDimensions(pixs, &w, &h, &d);
1001  if (d != 2 && d != 4 && d != 8)
1002  return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", __func__, NULL);
1003 
1004  pixd = pixCreate(w, h, 1);
1005  pixCopyResolution(pixd, pixs);
1006  pixCopyInputFormat(pixd, pixs);
1007  datas = pixGetData(pixs);
1008  datad = pixGetData(pixd);
1009  wpls = pixGetWpl(pixs);
1010  wpld = pixGetWpl(pixd);
1011  for (i = 0; i < h; i++) {
1012  lines = datas + i * wpls;
1013  lined = datad + i * wpld;
1014  for (j = 0; j < w; j++) {
1015  if (d == 2)
1016  sval = GET_DATA_DIBIT(lines, j);
1017  else if (d == 4)
1018  sval = GET_DATA_QBIT(lines, j);
1019  else /* d == 8 */
1020  sval = GET_DATA_BYTE(lines, j);
1021  if (sval == val)
1022  SET_DATA_BIT(lined, j);
1023  }
1024  }
1025 
1026  return pixd;
1027 }
1028 
1029 
1045 PIX *
1047  l_int32 *tab)
1048 {
1049 l_int32 w, h, d, i, j, val, wpls, wpld;
1050 l_uint32 *datas, *datad, *lines, *lined;
1051 PIX *pixd;
1052 
1053  if (!pixs)
1054  return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1055  if (!tab)
1056  return (PIX *)ERROR_PTR("tab not defined", __func__, NULL);
1057  pixGetDimensions(pixs, &w, &h, &d);
1058  if (d != 2 && d != 4 && d != 8)
1059  return (PIX *)ERROR_PTR("pix not 2, 4 or 8 bpp", __func__, NULL);
1060 
1061  pixd = pixCreate(w, h, 1);
1062  pixCopyResolution(pixd, pixs);
1063  pixCopyInputFormat(pixd, pixs);
1064  datas = pixGetData(pixs);
1065  datad = pixGetData(pixd);
1066  wpls = pixGetWpl(pixs);
1067  wpld = pixGetWpl(pixd);
1068  for (i = 0; i < h; i++) {
1069  lines = datas + i * wpls;
1070  lined = datad + i * wpld;
1071  for (j = 0; j < w; j++) {
1072  if (d == 2)
1073  val = GET_DATA_DIBIT(lines, j);
1074  else if (d == 4)
1075  val = GET_DATA_QBIT(lines, j);
1076  else /* d == 8 */
1077  val = GET_DATA_BYTE(lines, j);
1078  if (tab[val] == 1)
1079  SET_DATA_BIT(lined, j);
1080  }
1081  }
1082 
1083  return pixd;
1084 }
1085 
1086 
1112 PIX *
1114  l_float32 rc,
1115  l_float32 gc,
1116  l_float32 bc,
1117  l_float32 thresh)
1118 {
1119 PIX *pix1, *pix2;
1120 
1121  if (!pixs || pixGetDepth(pixs) != 32)
1122  return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL);
1123  if (thresh >= 255.0) thresh = 254.0; /* avoid 8 bit overflow */
1124 
1125  if ((pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc)) == NULL)
1126  return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL);
1127  pix2 = pixThresholdToBinary(pix1, thresh + 1);
1128  pixInvert(pix2, pix2);
1129  pixDestroy(&pix1);
1130  return pix2;
1131 }
1132 
1133 
1187 PIX *
1189  l_uint32 val,
1190  l_int32 debug)
1191 {
1192 PIX *pixg, *pixm, *pixt, *pixd;
1193 
1194  if (!pixs || pixGetDepth(pixs) != 32)
1195  return (PIX *)ERROR_PTR("pixs not defined or not 32 bpp",
1196  __func__, NULL);
1197 
1198  if (pixGetSpp(pixs) != 4) {
1199  L_WARNING("no alpha channel; returning a copy\n", __func__);
1200  return pixCopy(NULL, pixs);
1201  }
1202 
1203  /* Make a mask from the alpha component with ON pixels
1204  * wherever the alpha component is fully transparent (0).
1205  * The hard way:
1206  * l_int32 *lut = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1207  * lut[0] = 1;
1208  * pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
1209  * pixm = pixMakeMaskFromLUT(pixg, lut);
1210  * LEPT_FREE(lut);
1211  * But there's an easier way to set pixels in a mask where
1212  * the alpha component is 0 ... */
1213  pixg = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
1214  pixm = pixThresholdToBinary(pixg, 1);
1215 
1216  if (debug) {
1217  pixt = pixDisplayLayersRGBA(pixs, 0xffffff00, 600);
1218  pixDisplay(pixt, 0, 0);
1219  pixDestroy(&pixt);
1220  }
1221 
1222  pixd = pixCopy(NULL, pixs);
1223  pixSetMasked(pixd, pixm, (val & 0xffffff00));
1224  pixDestroy(&pixg);
1225  pixDestroy(&pixm);
1226  return pixd;
1227 }
1228 
1229 
1261 PIX *
1263  l_int32 dist,
1264  BOX **pbox)
1265 {
1266 l_int32 w, h;
1267 BOX *box1, *box2;
1268 PIX *pix1, *pixd;
1269 
1270  if (pbox) *pbox = NULL;
1271  if (!pixs || pixGetDepth(pixs) != 1)
1272  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
1273  if (dist < 0)
1274  return (PIX *)ERROR_PTR("dist must be >= 0", __func__, NULL);
1275 
1276  /* If requested, extract just the region to be affected by the mask */
1277  if (pbox) {
1278  pixClipToForeground(pixs, NULL, &box1);
1279  if (!box1) {
1280  L_WARNING("no ON pixels in mask\n", __func__);
1281  return pixCreateTemplate(pixs); /* all background (0) */
1282  }
1283 
1284  boxAdjustSides(box1, box1, -dist, dist, -dist, dist);
1285  pixGetDimensions(pixs, &w, &h, NULL);
1286  box2 = boxClipToRectangle(box1, w, h);
1287  *pbox = box2;
1288  pix1 = pixClipRectangle(pixs, box2, NULL);
1289  boxDestroy(&box1);
1290  } else {
1291  pix1 = pixCopy(NULL, pixs);
1292  }
1293 
1294  if (dist == 0) {
1295  pixd = pixConvert1To8(NULL, pix1, 0, 255);
1296  pixDestroy(&pix1);
1297  return pixd;
1298  }
1299 
1300  /* Blur the boundary of the input mask */
1301  pixInvert(pix1, pix1);
1302  pixd = pixDistanceFunction(pix1, 8, 8, L_BOUNDARY_FG);
1303  pixMultConstantGray(pixd, 256.0 / dist);
1304  pixInvert(pixd, pixd);
1305  pixDestroy(&pix1);
1306  return pixd;
1307 }
1308 
1309 
1328 l_ok
1330  PIX *pixm,
1331  BOX *box,
1332  l_int32 dist,
1333  l_uint32 *pval,
1334  l_int32 debug)
1335 {
1336 char op[64];
1337 l_int32 empty, bx, by;
1338 l_float32 rval, gval, bval;
1339 BOX *box1, *box2;
1340 PIX *pix1, *pix2, *pix3;
1341 
1342  if (!pval)
1343  return ERROR_INT("&pval not defined", __func__, 1);
1344  *pval = 0xffffff00; /* white */
1345  if (!pixs || pixGetDepth(pixs) != 32)
1346  return ERROR_INT("pixs undefined or not 32 bpp", __func__, 1);
1347  if (!pixm || pixGetDepth(pixm) != 1)
1348  return ERROR_INT("pixm undefined or not 1 bpp", __func__, 1);
1349  if (!box)
1350  return ERROR_INT("box not defined", __func__, 1);
1351  if (dist < 0)
1352  return ERROR_INT("dist must be >= 0", __func__, 1);
1353 
1354  /* Clip mask piece, expanded beyond %box by (%dist + 5) on each side.
1355  * box1 is the region requested; box2 is the actual region retrieved,
1356  * which is clipped to %pixm */
1357  box1 = boxAdjustSides(NULL, box, -dist - 5, dist + 5, -dist - 5, dist + 5);
1358  pix1 = pixClipRectangle(pixm, box1, &box2);
1359 
1360  /* Expand FG by %dist into the BG */
1361  if (dist == 0) {
1362  pix2 = pixCopy(NULL, pix1);
1363  } else {
1364  snprintf(op, sizeof(op), "d%d.%d", 2 * dist, 2 * dist);
1365  pix2 = pixMorphSequence(pix1, op, 0);
1366  }
1367 
1368  /* Expand again by 5 pixels on all sides (dilate 11x11) and XOR,
1369  * getting the annulus of FG pixels between %dist and %dist + 5 */
1370  pix3 = pixCopy(NULL, pix2);
1371  pixDilateBrick(pix3, pix3, 11, 11);
1372  pixXor(pix3, pix3, pix2);
1373  pixZero(pix3, &empty);
1374  if (!empty) {
1375  /* Scan the same region in %pixs, to get average under FG in pix3 */
1376  boxGetGeometry(box2, &bx, &by, NULL, NULL);
1377  pixGetAverageMaskedRGB(pixs, pix3, bx, by, 1, L_MEAN_ABSVAL,
1378  &rval, &gval, &bval);
1379  composeRGBPixel((l_int32)(rval + 0.5), (l_int32)(gval + 0.5),
1380  (l_int32)(bval + 0.5), pval);
1381  } else {
1382  L_WARNING("no pixels found\n", __func__);
1383  }
1384 
1385  if (debug) {
1386  lept_rmdir("masknear"); /* erase previous images */
1387  lept_mkdir("masknear");
1388  pixWriteDebug("/tmp/masknear/input.png", pix1, IFF_PNG);
1389  pixWriteDebug("/tmp/masknear/adjusted.png", pix2, IFF_PNG);
1390  pixWriteDebug("/tmp/masknear/outerfive.png", pix3, IFF_PNG);
1391  lept_stderr("Input box; with adjusted sides; clipped\n");
1392  boxPrintStreamInfo(stderr, box);
1393  boxPrintStreamInfo(stderr, box1);
1394  boxPrintStreamInfo(stderr, box2);
1395  }
1396 
1397  pixDestroy(&pix1);
1398  pixDestroy(&pix2);
1399  pixDestroy(&pix3);
1400  boxDestroy(&box1);
1401  boxDestroy(&box2);
1402  return 0;
1403 }
1404 
1405 
1426 PIX *
1428  PIX *pixm,
1429  SEL *sel,
1430  l_uint32 val)
1431 {
1432 l_int32 w, h;
1433 PIX *pix1, *pix2;
1434 
1435  if (!pixm || pixGetDepth(pixm) != 1)
1436  return (PIX *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, NULL);
1437 
1438  if (pixs) {
1439  pix1 = pixConvertTo32(pixs);
1440  } else {
1441  pixGetDimensions(pixm, &w, &h, NULL);
1442  pix1 = pixCreate(w, h, 32);
1443  pixSetAll(pix1);
1444  }
1445 
1446  if (sel)
1447  pix2 = pixDilate(NULL, pixm, sel);
1448  else
1449  pix2 = pixClone(pixm);
1450  pixSetMasked(pix1, pix2, val);
1451  pixDestroy(&pix2);
1452  return pix1;
1453 }
1454 
1455 
1456 /*-------------------------------------------------------------*
1457  * One and two-image boolean ops on arbitrary depth images *
1458  *-------------------------------------------------------------*/
1480 PIX *
1482  PIX *pixs)
1483 {
1484  if (!pixs)
1485  return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1486 
1487  /* Prepare pixd for in-place operation */
1488  if ((pixd = pixCopy(pixd, pixs)) == NULL)
1489  return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1490 
1491  pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1492  PIX_NOT(PIX_DST), NULL, 0, 0); /* invert pixd */
1493 
1494  return pixd;
1495 }
1496 
1497 
1529 PIX *
1530 pixOr(PIX *pixd,
1531  PIX *pixs1,
1532  PIX *pixs2)
1533 {
1534  if (!pixs1)
1535  return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
1536  if (!pixs2)
1537  return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
1538  if (pixd == pixs2)
1539  return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", __func__, pixd);
1540  if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1541  return (PIX *)ERROR_PTR("depths of pixs* unequal", __func__, pixd);
1542 
1543 #if EQUAL_SIZE_WARNING
1544  if (!pixSizesEqual(pixs1, pixs2))
1545  L_WARNING("pixs1 and pixs2 not equal sizes\n", __func__);
1546 #endif /* EQUAL_SIZE_WARNING */
1547 
1548  /* Prepare pixd to be a copy of pixs1 */
1549  if ((pixd = pixCopy(pixd, pixs1)) == NULL)
1550  return (PIX *)ERROR_PTR("pixd not made", __func__, pixd);
1551 
1552  /* src1 | src2 --> dest */
1553  pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1554  PIX_SRC | PIX_DST, pixs2, 0, 0);
1555 
1556  return pixd;
1557 }
1558 
1559 
1591 PIX *
1592 pixAnd(PIX *pixd,
1593  PIX *pixs1,
1594  PIX *pixs2)
1595 {
1596  if (!pixs1)
1597  return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
1598  if (!pixs2)
1599  return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
1600  if (pixd == pixs2)
1601  return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", __func__, pixd);
1602  if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1603  return (PIX *)ERROR_PTR("depths of pixs* unequal", __func__, pixd);
1604 
1605 #if EQUAL_SIZE_WARNING
1606  if (!pixSizesEqual(pixs1, pixs2))
1607  L_WARNING("pixs1 and pixs2 not equal sizes\n", __func__);
1608 #endif /* EQUAL_SIZE_WARNING */
1609 
1610  /* Prepare pixd to be a copy of pixs1 */
1611  if ((pixd = pixCopy(pixd, pixs1)) == NULL)
1612  return (PIX *)ERROR_PTR("pixd not made", __func__, pixd);
1613 
1614  /* src1 & src2 --> dest */
1615  pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1616  PIX_SRC & PIX_DST, pixs2, 0, 0);
1617 
1618  return pixd;
1619 }
1620 
1621 
1653 PIX *
1654 pixXor(PIX *pixd,
1655  PIX *pixs1,
1656  PIX *pixs2)
1657 {
1658  if (!pixs1)
1659  return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
1660  if (!pixs2)
1661  return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
1662  if (pixd == pixs2)
1663  return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", __func__, pixd);
1664  if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1665  return (PIX *)ERROR_PTR("depths of pixs* unequal", __func__, pixd);
1666 
1667 #if EQUAL_SIZE_WARNING
1668  if (!pixSizesEqual(pixs1, pixs2))
1669  L_WARNING("pixs1 and pixs2 not equal sizes\n", __func__);
1670 #endif /* EQUAL_SIZE_WARNING */
1671 
1672  /* Prepare pixd to be a copy of pixs1 */
1673  if ((pixd = pixCopy(pixd, pixs1)) == NULL)
1674  return (PIX *)ERROR_PTR("pixd not made", __func__, pixd);
1675 
1676  /* src1 ^ src2 --> dest */
1677  pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1678  PIX_SRC ^ PIX_DST, pixs2, 0, 0);
1679 
1680  return pixd;
1681 }
1682 
1683 
1716 PIX *
1718  PIX *pixs1,
1719  PIX *pixs2)
1720 {
1721 l_int32 w, h;
1722 
1723  if (!pixs1)
1724  return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
1725  if (!pixs2)
1726  return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
1727  if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1728  return (PIX *)ERROR_PTR("depths of pixs* unequal", __func__, pixd);
1729 
1730 #if EQUAL_SIZE_WARNING
1731  if (!pixSizesEqual(pixs1, pixs2))
1732  L_WARNING("pixs1 and pixs2 not equal sizes\n", __func__);
1733 #endif /* EQUAL_SIZE_WARNING */
1734 
1735  pixGetDimensions(pixs1, &w, &h, NULL);
1736  if (!pixd) {
1737  pixd = pixCopy(NULL, pixs1);
1738  pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1739  pixs2, 0, 0); /* src1 & (~src2) */
1740  } else if (pixd == pixs1) {
1741  pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1742  pixs2, 0, 0); /* src1 & (~src2) */
1743  } else if (pixd == pixs2) {
1744  pixRasterop(pixd, 0, 0, w, h, PIX_NOT(PIX_DST) & PIX_SRC,
1745  pixs1, 0, 0); /* src1 & (~src2) */
1746  } else { /* pixd != pixs1 && pixd != pixs2 */
1747  pixCopy(pixd, pixs1); /* sizes pixd to pixs1 if unequal */
1748  pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1749  pixs2, 0, 0); /* src1 & (~src2) */
1750  }
1751 
1752  return pixd;
1753 }
1754 
1755 
1756 /*-------------------------------------------------------------*
1757  * Pixel counting *
1758  *-------------------------------------------------------------*/
1776 l_ok
1778  l_int32 *pempty)
1779 {
1780 l_int32 w, h, wpl, i, j, fullwords, endbits;
1781 l_uint32 endmask;
1782 l_uint32 *data, *line;
1783 
1784  if (!pempty)
1785  return ERROR_INT("&empty not defined", __func__, 1);
1786  *pempty = 1;
1787  if (!pix)
1788  return ERROR_INT("pix not defined", __func__, 1);
1789 
1790  w = pixGetWidth(pix) * pixGetDepth(pix); /* in bits */
1791  h = pixGetHeight(pix);
1792  wpl = pixGetWpl(pix);
1793  data = pixGetData(pix);
1794  fullwords = w / 32;
1795  endbits = w & 31;
1796  endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits));
1797 
1798  for (i = 0; i < h; i++) {
1799  line = data + wpl * i;
1800  for (j = 0; j < fullwords; j++)
1801  if (*line++) {
1802  *pempty = 0;
1803  return 0;
1804  }
1805  if (endbits) {
1806  if (*line & endmask) {
1807  *pempty = 0;
1808  return 0;
1809  }
1810  }
1811  }
1812 
1813  return 0;
1814 }
1815 
1816 
1824 l_ok
1826  l_float32 *pfract)
1827 {
1828 l_int32 w, h, count;
1829 
1830  if (!pfract)
1831  return ERROR_INT("&fract not defined", __func__, 1);
1832  *pfract = 0.0;
1833  if (!pix || pixGetDepth(pix) != 1)
1834  return ERROR_INT("pix not defined or not 1 bpp", __func__, 1);
1835 
1836  pixCountPixels(pix, &count, NULL);
1837  pixGetDimensions(pix, &w, &h, NULL);
1838  *pfract = (l_float32)count / (l_float32)(w * h);
1839  return 0;
1840 }
1841 
1842 
1849 NUMA *
1851 {
1852 l_int32 d, i, n, count;
1853 l_int32 *tab;
1854 NUMA *na;
1855 PIX *pix;
1856 
1857  if (!pixa)
1858  return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL);
1859 
1860  if ((n = pixaGetCount(pixa)) == 0)
1861  return numaCreate(1);
1862 
1863  pix = pixaGetPix(pixa, 0, L_CLONE);
1864  d = pixGetDepth(pix);
1865  pixDestroy(&pix);
1866  if (d != 1)
1867  return (NUMA *)ERROR_PTR("pixa not 1 bpp", __func__, NULL);
1868 
1869  if ((na = numaCreate(n)) == NULL)
1870  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
1871  tab = makePixelSumTab8();
1872  for (i = 0; i < n; i++) {
1873  pix = pixaGetPix(pixa, i, L_CLONE);
1874  pixCountPixels(pix, &count, tab);
1875  numaAddNumber(na, count);
1876  pixDestroy(&pix);
1877  }
1878 
1879  LEPT_FREE(tab);
1880  return na;
1881 }
1882 
1883 
1892 l_ok
1894  l_int32 *pcount,
1895  l_int32 *tab8)
1896 {
1897 l_uint32 endmask;
1898 l_int32 w, h, wpl, i, j;
1899 l_int32 fullwords, endbits, sum;
1900 l_int32 *tab;
1901 l_uint32 *data;
1902 
1903  if (!pcount)
1904  return ERROR_INT("&count not defined", __func__, 1);
1905  *pcount = 0;
1906  if (!pixs || pixGetDepth(pixs) != 1)
1907  return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1908 
1909  tab = (tab8) ? tab8 : makePixelSumTab8();
1910  pixGetDimensions(pixs, &w, &h, NULL);
1911  wpl = pixGetWpl(pixs);
1912  data = pixGetData(pixs);
1913  fullwords = w >> 5;
1914  endbits = w & 31;
1915  endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits));
1916 
1917  sum = 0;
1918  for (i = 0; i < h; i++, data += wpl) {
1919  for (j = 0; j < fullwords; j++) {
1920  l_uint32 word = data[j];
1921  if (word) {
1922  sum += tab[word & 0xff] +
1923  tab[(word >> 8) & 0xff] +
1924  tab[(word >> 16) & 0xff] +
1925  tab[(word >> 24) & 0xff];
1926  }
1927  }
1928  if (endbits) {
1929  l_uint32 word = data[j] & endmask;
1930  if (word) {
1931  sum += tab[word & 0xff] +
1932  tab[(word >> 8) & 0xff] +
1933  tab[(word >> 16) & 0xff] +
1934  tab[(word >> 24) & 0xff];
1935  }
1936  }
1937  }
1938  *pcount = sum;
1939 
1940  if (!tab8) LEPT_FREE(tab);
1941  return 0;
1942 }
1943 
1944 
1954 l_ok
1956  BOX *box,
1957  l_int32 *pcount,
1958  l_int32 *tab8)
1959 {
1960 l_int32 w, h, bx, by, bw, bh;
1961 BOX *box1;
1962 PIX *pix1;
1963 
1964  if (!pcount)
1965  return ERROR_INT("&count not defined", __func__, 1);
1966  *pcount = 0;
1967  if (!pixs || pixGetDepth(pixs) != 1)
1968  return ERROR_INT("pixs not defined or not 1 bpp", __func__, 1);
1969 
1970  if (box) {
1971  pixGetDimensions(pixs, &w, &h, NULL);
1972  if ((box1 = boxClipToRectangle(box, w, h)) == NULL)
1973  return ERROR_INT("box1 not made", __func__, 1);
1974  boxGetGeometry(box1, &bx, &by, &bw, &bh);
1975  pix1 = pixCreate(bw, bh, 1);
1976  pixRasterop(pix1, 0, 0, bw, bh, PIX_SRC, pixs, bx, by);
1977  pixCountPixels(pix1, pcount, tab8);
1978  pixDestroy(&pix1);
1979  boxDestroy(&box1);
1980  } else {
1981  pixCountPixels(pixs, pcount, tab8);
1982  }
1983 
1984  return 0;
1985 }
1986 
1987 
2001 NUMA *
2003  BOX *box)
2004 {
2005 l_int32 i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh;
2006 l_uint32 *line, *data;
2007 NUMA *na;
2008 
2009  if (!pix || pixGetDepth(pix) != 1)
2010  return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL);
2011  if (!box)
2012  return pixCountPixelsByRow(pix, NULL);
2013 
2014  pixGetDimensions(pix, &w, &h, NULL);
2015  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2016  &bw, &bh) == 1)
2017  return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL);
2018 
2019  if ((na = numaCreate(bh)) == NULL)
2020  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2021  numaSetParameters(na, ystart, 1);
2022  data = pixGetData(pix);
2023  wpl = pixGetWpl(pix);
2024  for (i = ystart; i < yend; i++) {
2025  count = 0;
2026  line = data + i * wpl;
2027  for (j = xstart; j < xend; j++) {
2028  if (GET_DATA_BIT(line, j))
2029  count++;
2030  }
2031  numaAddNumber(na, count);
2032  }
2033 
2034  return na;
2035 }
2036 
2037 
2051 NUMA *
2053  BOX *box)
2054 {
2055 l_int32 i, j, w, h, wpl, count, xstart, xend, ystart, yend, bw, bh;
2056 l_uint32 *line, *data;
2057 NUMA *na;
2058 
2059  if (!pix || pixGetDepth(pix) != 1)
2060  return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL);
2061  if (!box)
2062  return pixCountPixelsByColumn(pix);
2063 
2064  pixGetDimensions(pix, &w, &h, NULL);
2065  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2066  &bw, &bh) == 1)
2067  return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL);
2068 
2069  if ((na = numaCreate(bw)) == NULL)
2070  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2071  numaSetParameters(na, xstart, 1);
2072  data = pixGetData(pix);
2073  wpl = pixGetWpl(pix);
2074  for (j = xstart; j < xend; j++) {
2075  count = 0;
2076  for (i = ystart; i < yend; i++) {
2077  line = data + i * wpl;
2078  if (GET_DATA_BIT(line, j))
2079  count++;
2080  }
2081  numaAddNumber(na, count);
2082  }
2083 
2084  return na;
2085 }
2086 
2087 
2095 NUMA *
2097  l_int32 *tab8)
2098 {
2099 l_int32 h, i, count;
2100 l_int32 *tab;
2101 NUMA *na;
2102 
2103  if (!pix || pixGetDepth(pix) != 1)
2104  return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL);
2105 
2106  h = pixGetHeight(pix);
2107  if ((na = numaCreate(h)) == NULL)
2108  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2109 
2110  tab = (tab8) ? tab8 : makePixelSumTab8();
2111  for (i = 0; i < h; i++) {
2112  pixCountPixelsInRow(pix, i, &count, tab);
2113  numaAddNumber(na, count);
2114  }
2115 
2116  if (!tab8) LEPT_FREE(tab);
2117  return na;
2118 }
2119 
2120 
2127 NUMA *
2129 {
2130 l_int32 i, j, w, h, wpl;
2131 l_uint32 *line, *data;
2132 l_float32 *array;
2133 NUMA *na;
2134 
2135  if (!pix || pixGetDepth(pix) != 1)
2136  return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL);
2137 
2138  pixGetDimensions(pix, &w, &h, NULL);
2139  if ((na = numaCreate(w)) == NULL)
2140  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2141  numaSetCount(na, w);
2142  array = numaGetFArray(na, L_NOCOPY);
2143  data = pixGetData(pix);
2144  wpl = pixGetWpl(pix);
2145  for (i = 0; i < h; i++) {
2146  line = data + wpl * i;
2147  for (j = 0; j < w; j++) {
2148  if (GET_DATA_BIT(line, j))
2149  array[j] += 1.0;
2150  }
2151  }
2152 
2153  return na;
2154 }
2155 
2156 
2166 l_ok
2168  l_int32 row,
2169  l_int32 *pcount,
2170  l_int32 *tab8)
2171 {
2172 l_uint32 word, endmask;
2173 l_int32 j, w, h, wpl;
2174 l_int32 fullwords, endbits, sum;
2175 l_int32 *tab;
2176 l_uint32 *line;
2177 
2178  if (!pcount)
2179  return ERROR_INT("&count not defined", __func__, 1);
2180  *pcount = 0;
2181  if (!pix || pixGetDepth(pix) != 1)
2182  return ERROR_INT("pix not defined or not 1 bpp", __func__, 1);
2183 
2184  pixGetDimensions(pix, &w, &h, NULL);
2185  if (row < 0 || row >= h)
2186  return ERROR_INT("row out of bounds", __func__, 1);
2187  wpl = pixGetWpl(pix);
2188  line = pixGetData(pix) + row * wpl;
2189  fullwords = w >> 5;
2190  endbits = w & 31;
2191  endmask = (endbits == 0) ? 0 : (0xffffffffU << (32 - endbits));
2192 
2193  tab = (tab8) ? tab8 : makePixelSumTab8();
2194  sum = 0;
2195  for (j = 0; j < fullwords; j++) {
2196  word = line[j];
2197  if (word) {
2198  sum += tab[word & 0xff] +
2199  tab[(word >> 8) & 0xff] +
2200  tab[(word >> 16) & 0xff] +
2201  tab[(word >> 24) & 0xff];
2202  }
2203  }
2204  if (endbits) {
2205  word = line[j] & endmask;
2206  if (word) {
2207  sum += tab[word & 0xff] +
2208  tab[(word >> 8) & 0xff] +
2209  tab[(word >> 16) & 0xff] +
2210  tab[(word >> 24) & 0xff];
2211  }
2212  }
2213  *pcount = sum;
2214 
2215  if (!tab8) LEPT_FREE(tab);
2216  return 0;
2217 }
2218 
2219 
2227 NUMA *
2229  l_int32 order)
2230 {
2231 l_int32 i, j, w, h, wpl;
2232 l_uint32 *line, *data;
2233 l_float32 *array;
2234 NUMA *na;
2235 
2236  if (!pix || pixGetDepth(pix) != 1)
2237  return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", __func__, NULL);
2238  if (order != 1 && order != 2)
2239  return (NUMA *)ERROR_PTR("order of moment not 1 or 2", __func__, NULL);
2240 
2241  pixGetDimensions(pix, &w, &h, NULL);
2242  if ((na = numaCreate(w)) == NULL)
2243  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2244  numaSetCount(na, w);
2245  array = numaGetFArray(na, L_NOCOPY);
2246  data = pixGetData(pix);
2247  wpl = pixGetWpl(pix);
2248  for (i = 0; i < h; i++) {
2249  line = data + wpl * i;
2250  for (j = 0; j < w; j++) {
2251  if (GET_DATA_BIT(line, j)) {
2252  if (order == 1)
2253  array[j] += i;
2254  else /* order == 2 */
2255  array[j] += i * i;
2256  }
2257  }
2258  }
2259 
2260  return na;
2261 }
2262 
2263 
2283 l_ok
2285  l_int32 thresh,
2286  l_int32 *pabove,
2287  l_int32 *tab8)
2288 {
2289 l_uint32 word, endmask;
2290 l_int32 *tab;
2291 l_int32 w, h, wpl, i, j;
2292 l_int32 fullwords, endbits, sum;
2293 l_uint32 *line, *data;
2294 
2295  if (!pabove)
2296  return ERROR_INT("&above not defined", __func__, 1);
2297  *pabove = 0;
2298  if (!pix || pixGetDepth(pix) != 1)
2299  return ERROR_INT("pix not defined or not 1 bpp", __func__, 1);
2300 
2301  tab = (tab8) ? tab8 : makePixelSumTab8();
2302  pixGetDimensions(pix, &w, &h, NULL);
2303  wpl = pixGetWpl(pix);
2304  data = pixGetData(pix);
2305  fullwords = w >> 5;
2306  endbits = w & 31;
2307  endmask = 0xffffffff << (32 - endbits);
2308 
2309  sum = 0;
2310  for (i = 0; i < h; i++) {
2311  line = data + wpl * i;
2312  for (j = 0; j < fullwords; j++) {
2313  word = line[j];
2314  if (word) {
2315  sum += tab[word & 0xff] +
2316  tab[(word >> 8) & 0xff] +
2317  tab[(word >> 16) & 0xff] +
2318  tab[(word >> 24) & 0xff];
2319  }
2320  }
2321  if (endbits) {
2322  word = line[j] & endmask;
2323  if (word) {
2324  sum += tab[word & 0xff] +
2325  tab[(word >> 8) & 0xff] +
2326  tab[(word >> 16) & 0xff] +
2327  tab[(word >> 24) & 0xff];
2328  }
2329  }
2330  if (sum > thresh) {
2331  *pabove = 1;
2332  if (!tab8) LEPT_FREE(tab);
2333  return 0;
2334  }
2335  }
2336 
2337  if (!tab8) LEPT_FREE(tab);
2338  return 0;
2339 }
2340 
2341 
2353 l_int32 *
2355 {
2356 l_uint8 byte;
2357 l_int32 i;
2358 l_int32 *tab;
2359 
2360  tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2361  for (i = 0; i < 256; i++) {
2362  byte = (l_uint8)i;
2363  tab[i] = (byte & 0x1) +
2364  ((byte >> 1) & 0x1) +
2365  ((byte >> 2) & 0x1) +
2366  ((byte >> 3) & 0x1) +
2367  ((byte >> 4) & 0x1) +
2368  ((byte >> 5) & 0x1) +
2369  ((byte >> 6) & 0x1) +
2370  ((byte >> 7) & 0x1);
2371  }
2372  return tab;
2373 }
2374 
2375 
2393 l_int32 *
2395 {
2396 l_int32 i;
2397 l_int32 *tab;
2398 
2399  tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
2400  tab[0] = 0;
2401  tab[1] = 7;
2402  for (i = 2; i < 4; i++) {
2403  tab[i] = tab[i - 2] + 6;
2404  }
2405  for (i = 4; i < 8; i++) {
2406  tab[i] = tab[i - 4] + 5;
2407  }
2408  for (i = 8; i < 16; i++) {
2409  tab[i] = tab[i - 8] + 4;
2410  }
2411  for (i = 16; i < 32; i++) {
2412  tab[i] = tab[i - 16] + 3;
2413  }
2414  for (i = 32; i < 64; i++) {
2415  tab[i] = tab[i - 32] + 2;
2416  }
2417  for (i = 64; i < 128; i++) {
2418  tab[i] = tab[i - 64] + 1;
2419  }
2420  for (i = 128; i < 256; i++) {
2421  tab[i] = tab[i - 128];
2422  }
2423  return tab;
2424 }
2425 
2426 
2427 /*-------------------------------------------------------------*
2428  * Average of pixel values in gray images *
2429  *-------------------------------------------------------------*/
2446 NUMA *
2448  BOX *box,
2449  l_int32 type)
2450 {
2451 l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh;
2452 l_uint32 *line, *data;
2453 l_float64 norm, sum;
2454 NUMA *na;
2455 
2456  if (!pix)
2457  return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL);
2458  pixGetDimensions(pix, &w, &h, &d);
2459  if (d != 8 && d != 16)
2460  return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", __func__, NULL);
2461  if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX)
2462  return (NUMA *)ERROR_PTR("invalid type", __func__, NULL);
2463  if (pixGetColormap(pix) != NULL)
2464  return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL);
2465 
2466  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2467  &bw, &bh) == 1)
2468  return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL);
2469 
2470  norm = 1. / (l_float32)bw;
2471  if ((na = numaCreate(bh)) == NULL)
2472  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2473  numaSetParameters(na, ystart, 1);
2474  data = pixGetData(pix);
2475  wpl = pixGetWpl(pix);
2476  for (i = ystart; i < yend; i++) {
2477  sum = 0.0;
2478  line = data + i * wpl;
2479  if (d == 8) {
2480  for (j = xstart; j < xend; j++)
2481  sum += GET_DATA_BYTE(line, j);
2482  if (type == L_BLACK_IS_MAX)
2483  sum = bw * 255 - sum;
2484  } else { /* d == 16 */
2485  for (j = xstart; j < xend; j++)
2486  sum += GET_DATA_TWO_BYTES(line, j);
2487  if (type == L_BLACK_IS_MAX)
2488  sum = bw * 0xffff - sum;
2489  }
2490  numaAddNumber(na, (l_float32)(norm * sum));
2491  }
2492 
2493  return na;
2494 }
2495 
2496 
2513 NUMA *
2515  BOX *box,
2516  l_int32 type)
2517 {
2518 l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh;
2519 l_uint32 *line, *data;
2520 l_float32 norm, sum;
2521 NUMA *na;
2522 
2523  if (!pix)
2524  return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL);
2525  pixGetDimensions(pix, &w, &h, &d);
2526 
2527  if (d != 8 && d != 16)
2528  return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", __func__, NULL);
2529  if (type != L_WHITE_IS_MAX && type != L_BLACK_IS_MAX)
2530  return (NUMA *)ERROR_PTR("invalid type", __func__, NULL);
2531  if (pixGetColormap(pix) != NULL)
2532  return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL);
2533 
2534  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2535  &bw, &bh) == 1)
2536  return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL);
2537 
2538  if ((na = numaCreate(bw)) == NULL)
2539  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2540  numaSetParameters(na, xstart, 1);
2541  norm = 1. / (l_float32)bh;
2542  data = pixGetData(pix);
2543  wpl = pixGetWpl(pix);
2544  for (j = xstart; j < xend; j++) {
2545  sum = 0.0;
2546  if (d == 8) {
2547  for (i = ystart; i < yend; i++) {
2548  line = data + i * wpl;
2549  sum += GET_DATA_BYTE(line, j);
2550  }
2551  if (type == L_BLACK_IS_MAX)
2552  sum = bh * 255 - sum;
2553  } else { /* d == 16 */
2554  for (i = ystart; i < yend; i++) {
2555  line = data + i * wpl;
2556  sum += GET_DATA_TWO_BYTES(line, j);
2557  }
2558  if (type == L_BLACK_IS_MAX)
2559  sum = bh * 0xffff - sum;
2560  }
2561  numaAddNumber(na, (l_float32)(norm * sum));
2562  }
2563 
2564  return na;
2565 }
2566 
2567 
2598 l_ok
2600  PIX *pixm,
2601  BOX *box,
2602  l_int32 minval,
2603  l_int32 maxval,
2604  l_int32 subsamp,
2605  l_float32 *pave)
2606 {
2607 l_int32 w, h, d, wpls, wm, hm, dm, wplm, val, count;
2608 l_int32 i, j, xstart, xend, ystart, yend;
2609 l_uint32 *datas, *datam, *lines, *linem;
2610 l_float64 sum;
2611 
2612  if (!pave)
2613  return ERROR_INT("&ave not defined", __func__, 1);
2614  *pave = 0;
2615  if (!pixs)
2616  return ERROR_INT("pixs not defined", __func__, 1);
2617  if (pixGetColormap(pixs) != NULL)
2618  return ERROR_INT("pixs is colormapped", __func__, 1);
2619  pixGetDimensions(pixs, &w, &h, &d);
2620  if (d != 1 && d != 2 && d != 4 && d != 8)
2621  return ERROR_INT("pixs not 1, 2, 4 or 8 bpp", __func__, 1);
2622  if (pixm) {
2623  pixGetDimensions(pixm, &wm, &hm, &dm);
2624  if (dm != 1)
2625  return ERROR_INT("pixm not 1 bpp", __func__, 1);
2626  w = L_MIN(w, wm);
2627  h = L_MIN(h, hm);
2628  }
2629  if (subsamp < 1)
2630  return ERROR_INT("subsamp must be >= 1", __func__, 1);
2631 
2632  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2633  NULL, NULL) == 1)
2634  return ERROR_INT("invalid clipping box", __func__, 1);
2635 
2636  datas = pixGetData(pixs);
2637  wpls = pixGetWpl(pixs);
2638  if (pixm) {
2639  datam = pixGetData(pixm);
2640  wplm = pixGetWpl(pixm);
2641  }
2642  sum = 0.0;
2643  count = 0;
2644  for (i = ystart; i < yend; i += subsamp) {
2645  lines = datas + i * wpls;
2646  if (pixm)
2647  linem = datam + i * wplm;
2648  for (j = xstart; j < xend; j += subsamp) {
2649  if (pixm && (GET_DATA_BIT(linem, j) == 1))
2650  continue;
2651  if (d == 1)
2652  val = GET_DATA_BIT(lines, j);
2653  else if (d == 2)
2654  val = GET_DATA_DIBIT(lines, j);
2655  else if (d == 4)
2656  val = GET_DATA_QBIT(lines, j);
2657  else /* d == 8 */
2658  val = GET_DATA_BYTE(lines, j);
2659  if (val >= minval && val <= maxval) {
2660  sum += val;
2661  count++;
2662  }
2663  }
2664  }
2665 
2666  if (count == 0)
2667  return 2; /* not an error; don't use the average value (0.0) */
2668  *pave = sum / (l_float32)count;
2669  return 0;
2670 }
2671 
2672 
2673 /*-------------------------------------------------------------*
2674  * Average of pixel values in RGB images *
2675  *-------------------------------------------------------------*/
2703 l_ok
2705  PIX *pixm,
2706  BOX *box,
2707  l_int32 subsamp,
2708  l_uint32 *pave)
2709 {
2710 l_int32 w, h, wpls, wm, hm, dm, wplm, i, j, xstart, xend, ystart, yend;
2711 l_int32 rval, gval, bval, rave, gave, bave, count;
2712 l_uint32 *datas, *datam, *lines, *linem;
2713 l_uint32 pixel;
2714 l_float64 rsum, gsum, bsum;
2715 
2716  if (!pave)
2717  return ERROR_INT("&ave not defined", __func__, 1);
2718  *pave = 0;
2719  if (!pixs || pixGetDepth(pixs) != 32)
2720  return ERROR_INT("pixs undefined or not 32 bpp", __func__, 1);
2721  pixGetDimensions(pixs, &w, &h, NULL);
2722  if (pixm) {
2723  pixGetDimensions(pixm, &wm, &hm, &dm);
2724  if (dm != 1)
2725  return ERROR_INT("pixm not 1 bpp", __func__, 1);
2726  w = L_MIN(w, wm);
2727  h = L_MIN(h, hm);
2728  }
2729  if (subsamp < 1)
2730  return ERROR_INT("subsamp must be >= 1", __func__, 1);
2731 
2732  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2733  NULL, NULL) == 1)
2734  return ERROR_INT("invalid clipping box", __func__, 1);
2735 
2736  datas = pixGetData(pixs);
2737  wpls = pixGetWpl(pixs);
2738  if (pixm) {
2739  datam = pixGetData(pixm);
2740  wplm = pixGetWpl(pixm);
2741  }
2742  rsum = gsum = bsum = 0.0;
2743  count = 0;
2744  for (i = ystart; i < yend; i += subsamp) {
2745  lines = datas + i * wpls;
2746  if (pixm)
2747  linem = datam + i * wplm;
2748  for (j = xstart; j < xend; j += subsamp) {
2749  if (pixm && (GET_DATA_BIT(linem, j) == 1))
2750  continue;
2751  pixel = *(lines + j);
2752  extractRGBValues(pixel, &rval, &gval, &bval);
2753  rsum += rval;
2754  gsum += gval;
2755  bsum += bval;
2756  count++;
2757  }
2758  }
2759 
2760  if (count == 0)
2761  return 2; /* not an error */
2762  rave = (l_uint32)(rsum / (l_float64)count);
2763  gave = (l_uint32)(gsum / (l_float64)count);
2764  bave = (l_uint32)(bsum / (l_float64)count);
2765  composeRGBPixel(rave, gave, bave, pave);
2766  return 0;
2767 }
2768 
2769 
2770 /*------------------------------------------------------------------*
2771  * Variance of pixel values in gray images *
2772  *------------------------------------------------------------------*/
2788 NUMA *
2790  BOX *box)
2791 {
2792 l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val;
2793 l_uint32 *line, *data;
2794 l_float64 sum1, sum2, norm, ave, var, rootvar;
2795 NUMA *na;
2796 
2797  if (!pix)
2798  return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL);
2799  pixGetDimensions(pix, &w, &h, &d);
2800  if (d != 8 && d != 16)
2801  return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", __func__, NULL);
2802  if (pixGetColormap(pix) != NULL)
2803  return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL);
2804 
2805  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2806  &bw, &bh) == 1)
2807  return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL);
2808 
2809  if ((na = numaCreate(bh)) == NULL)
2810  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2811  numaSetParameters(na, ystart, 1);
2812  norm = 1. / (l_float32)bw;
2813  data = pixGetData(pix);
2814  wpl = pixGetWpl(pix);
2815  for (i = ystart; i < yend; i++) {
2816  sum1 = sum2 = 0.0;
2817  line = data + i * wpl;
2818  for (j = xstart; j < xend; j++) {
2819  if (d == 8)
2820  val = GET_DATA_BYTE(line, j);
2821  else /* d == 16 */
2822  val = GET_DATA_TWO_BYTES(line, j);
2823  sum1 += val;
2824  sum2 += (l_float64)(val) * val;
2825  }
2826  ave = norm * sum1;
2827  var = norm * sum2 - ave * ave;
2828  rootvar = sqrt(var);
2829  numaAddNumber(na, (l_float32)rootvar);
2830  }
2831 
2832  return na;
2833 }
2834 
2835 
2851 NUMA *
2853  BOX *box)
2854 {
2855 l_int32 i, j, w, h, d, wpl, xstart, xend, ystart, yend, bw, bh, val;
2856 l_uint32 *line, *data;
2857 l_float64 sum1, sum2, norm, ave, var, rootvar;
2858 NUMA *na;
2859 
2860  if (!pix)
2861  return (NUMA *)ERROR_PTR("pix not defined", __func__, NULL);
2862  pixGetDimensions(pix, &w, &h, &d);
2863  if (d != 8 && d != 16)
2864  return (NUMA *)ERROR_PTR("pix not 8 or 16 bpp", __func__, NULL);
2865  if (pixGetColormap(pix) != NULL)
2866  return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL);
2867 
2868  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2869  &bw, &bh) == 1)
2870  return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL);
2871 
2872  if ((na = numaCreate(bw)) == NULL)
2873  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
2874  numaSetParameters(na, xstart, 1);
2875  norm = 1. / (l_float32)bh;
2876  data = pixGetData(pix);
2877  wpl = pixGetWpl(pix);
2878  for (j = xstart; j < xend; j++) {
2879  sum1 = sum2 = 0.0;
2880  for (i = ystart; i < yend; i++) {
2881  line = data + wpl * i;
2882  if (d == 8)
2883  val = GET_DATA_BYTE(line, j);
2884  else /* d == 16 */
2885  val = GET_DATA_TWO_BYTES(line, j);
2886  sum1 += val;
2887  sum2 += (l_float64)(val) * val;
2888  }
2889  ave = norm * sum1;
2890  var = norm * sum2 - ave * ave;
2891  rootvar = sqrt(var);
2892  numaAddNumber(na, (l_float32)rootvar);
2893  }
2894 
2895  return na;
2896 }
2897 
2898 
2907 l_ok
2909  BOX *box,
2910  l_float32 *prootvar)
2911 {
2912 l_int32 w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val;
2913 l_uint32 *data, *line;
2914 l_float64 sum1, sum2, norm, ave, var;
2915 
2916  if (!prootvar)
2917  return ERROR_INT("&rootvar not defined", __func__, 1);
2918  *prootvar = 0.0;
2919  if (!pix)
2920  return ERROR_INT("pix not defined", __func__, 1);
2921  pixGetDimensions(pix, &w, &h, &d);
2922  if (d != 1 && d != 2 && d != 4 && d != 8)
2923  return ERROR_INT("pix not 1, 2, 4 or 8 bpp", __func__, 1);
2924  if (pixGetColormap(pix) != NULL)
2925  return ERROR_INT("pix is colormapped", __func__, 1);
2926 
2927  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2928  &bw, &bh) == 1)
2929  return ERROR_INT("invalid clipping box", __func__, 1);
2930 
2931  wpl = pixGetWpl(pix);
2932  data = pixGetData(pix);
2933  sum1 = sum2 = 0.0;
2934  for (i = ystart; i < yend; i++) {
2935  line = data + i * wpl;
2936  for (j = xstart; j < xend; j++) {
2937  if (d == 1) {
2938  val = GET_DATA_BIT(line, j);
2939  sum1 += val;
2940  sum2 += (l_float64)(val) * val;
2941  } else if (d == 2) {
2942  val = GET_DATA_DIBIT(line, j);
2943  sum1 += val;
2944  sum2 += (l_float64)(val) * val;
2945  } else if (d == 4) {
2946  val = GET_DATA_QBIT(line, j);
2947  sum1 += val;
2948  sum2 += (l_float64)(val) * val;
2949  } else { /* d == 8 */
2950  val = GET_DATA_BYTE(line, j);
2951  sum1 += val;
2952  sum2 += (l_float64)(val) * val;
2953  }
2954  }
2955  }
2956  norm = 1.0 / ((l_float64)(bw) * bh);
2957  ave = norm * sum1;
2958  var = norm * sum2 - ave * ave;
2959  *prootvar = (l_float32)sqrt(var);
2960  return 0;
2961 }
2962 
2963 
2964 /*---------------------------------------------------------------------*
2965  * Average of absolute value of pixel differences in gray images *
2966  *---------------------------------------------------------------------*/
2982 NUMA *
2984  BOX *box)
2985 {
2986 l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1;
2987 l_uint32 *line, *data;
2988 l_float64 norm, sum;
2989 NUMA *na;
2990 
2991  if (!pix || pixGetDepth(pix) != 8)
2992  return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", __func__, NULL);
2993  if (pixGetColormap(pix) != NULL)
2994  return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL);
2995 
2996  pixGetDimensions(pix, &w, &h, NULL);
2997  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
2998  &bw, &bh) == 1)
2999  return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL);
3000  if (bw < 2)
3001  return (NUMA *)ERROR_PTR("row width must be >= 2", __func__, NULL);
3002 
3003  norm = 1. / (l_float32)(bw - 1);
3004  if ((na = numaCreate(bh)) == NULL)
3005  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
3006  numaSetParameters(na, ystart, 1);
3007  data = pixGetData(pix);
3008  wpl = pixGetWpl(pix);
3009  for (i = ystart; i < yend; i++) {
3010  sum = 0.0;
3011  line = data + i * wpl;
3012  val0 = GET_DATA_BYTE(line, xstart);
3013  for (j = xstart + 1; j < xend; j++) {
3014  val1 = GET_DATA_BYTE(line, j);
3015  sum += L_ABS(val1 - val0);
3016  val0 = val1;
3017  }
3018  numaAddNumber(na, (l_float32)(norm * sum));
3019  }
3020 
3021  return na;
3022 }
3023 
3024 
3041 NUMA *
3043  BOX *box)
3044 {
3045 l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh, val0, val1;
3046 l_uint32 *line, *data;
3047 l_float64 norm, sum;
3048 NUMA *na;
3049 
3050  if (!pix || pixGetDepth(pix) != 8)
3051  return (NUMA *)ERROR_PTR("pix undefined or not 8 bpp", __func__, NULL);
3052  if (pixGetColormap(pix) != NULL)
3053  return (NUMA *)ERROR_PTR("pix colormapped", __func__, NULL);
3054 
3055  pixGetDimensions(pix, &w, &h, NULL);
3056  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
3057  &bw, &bh) == 1)
3058  return (NUMA *)ERROR_PTR("invalid clipping box", __func__, NULL);
3059  if (bh < 2)
3060  return (NUMA *)ERROR_PTR("column height must be >= 2", __func__, NULL);
3061 
3062  norm = 1. / (l_float32)(bh - 1);
3063  if ((na = numaCreate(bw)) == NULL)
3064  return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
3065  numaSetParameters(na, xstart, 1);
3066  data = pixGetData(pix);
3067  wpl = pixGetWpl(pix);
3068  for (j = xstart; j < xend; j++) {
3069  sum = 0.0;
3070  line = data + ystart * wpl;
3071  val0 = GET_DATA_BYTE(line, j);
3072  for (i = ystart + 1; i < yend; i++) {
3073  line = data + i * wpl;
3074  val1 = GET_DATA_BYTE(line, j);
3075  sum += L_ABS(val1 - val0);
3076  val0 = val1;
3077  }
3078  numaAddNumber(na, (l_float32)(norm * sum));
3079  }
3080 
3081  return na;
3082 }
3083 
3084 
3102 l_ok
3104  BOX *box,
3105  l_int32 dir,
3106  l_float32 *pabsdiff)
3107 {
3108 l_int32 w, h, wpl, i, j, xstart, xend, ystart, yend, bw, bh, val0, val1;
3109 l_uint32 *data, *line;
3110 l_float64 norm, sum;
3111 
3112  if (!pabsdiff)
3113  return ERROR_INT("&absdiff not defined", __func__, 1);
3114  *pabsdiff = 0.0;
3115  if (!pix || pixGetDepth(pix) != 8)
3116  return ERROR_INT("pix undefined or not 8 bpp", __func__, 1);
3117  if (dir != L_HORIZONTAL_LINE && dir != L_VERTICAL_LINE)
3118  return ERROR_INT("invalid direction", __func__, 1);
3119  if (pixGetColormap(pix) != NULL)
3120  return ERROR_INT("pix is colormapped", __func__, 1);
3121 
3122  pixGetDimensions(pix, &w, &h, NULL);
3123  if (boxClipToRectangleParams(box, w, h, &xstart, &ystart, &xend, &yend,
3124  &bw, &bh) == 1)
3125  return ERROR_INT("invalid clipping box", __func__, 1);
3126 
3127  wpl = pixGetWpl(pix);
3128  data = pixGetData(pix);
3129  if (dir == L_HORIZONTAL_LINE) {
3130  norm = 1. / (l_float32)(bh * (bw - 1));
3131  sum = 0.0;
3132  for (i = ystart; i < yend; i++) {
3133  line = data + i * wpl;
3134  val0 = GET_DATA_BYTE(line, xstart);
3135  for (j = xstart + 1; j < xend; j++) {
3136  val1 = GET_DATA_BYTE(line, j);
3137  sum += L_ABS(val1 - val0);
3138  val0 = val1;
3139  }
3140  }
3141  } else { /* vertical line */
3142  norm = 1. / (l_float32)(bw * (bh - 1));
3143  sum = 0.0;
3144  for (j = xstart; j < xend; j++) {
3145  line = data + ystart * wpl;
3146  val0 = GET_DATA_BYTE(line, j);
3147  for (i = ystart + 1; i < yend; i++) {
3148  line = data + i * wpl;
3149  val1 = GET_DATA_BYTE(line, j);
3150  sum += L_ABS(val1 - val0);
3151  val0 = val1;
3152  }
3153  }
3154  }
3155  *pabsdiff = (l_float32)(norm * sum);
3156  return 0;
3157 }
3158 
3159 
3177 l_ok
3179  l_int32 x1,
3180  l_int32 y1,
3181  l_int32 x2,
3182  l_int32 y2,
3183  l_float32 *pabsdiff)
3184 {
3185 l_int32 w, h, i, j, dir, size, sum;
3186 l_uint32 val0, val1;
3187 
3188  if (!pabsdiff)
3189  return ERROR_INT("&absdiff not defined", __func__, 1);
3190  *pabsdiff = 0.0;
3191  if (!pix || pixGetDepth(pix) != 8)
3192  return ERROR_INT("pix undefined or not 8 bpp", __func__, 1);
3193  if (y1 == y2) {
3194  dir = L_HORIZONTAL_LINE;
3195  } else if (x1 == x2) {
3196  dir = L_VERTICAL_LINE;
3197  } else {
3198  return ERROR_INT("line is neither horiz nor vert", __func__, 1);
3199  }
3200  if (pixGetColormap(pix) != NULL)
3201  return ERROR_INT("pix is colormapped", __func__, 1);
3202 
3203  pixGetDimensions(pix, &w, &h, NULL);
3204  sum = 0;
3205  if (dir == L_HORIZONTAL_LINE) {
3206  x1 = L_MAX(x1, 0);
3207  x2 = L_MIN(x2, w - 1);
3208  if (x1 >= x2)
3209  return ERROR_INT("x1 >= x2", __func__, 1);
3210  size = x2 - x1;
3211  pixGetPixel(pix, x1, y1, &val0);
3212  for (j = x1 + 1; j <= x2; j++) {
3213  pixGetPixel(pix, j, y1, &val1);
3214  sum += L_ABS((l_int32)val1 - (l_int32)val0);
3215  val0 = val1;
3216  }
3217  } else { /* vertical */
3218  y1 = L_MAX(y1, 0);
3219  y2 = L_MIN(y2, h - 1);
3220  if (y1 >= y2)
3221  return ERROR_INT("y1 >= y2", __func__, 1);
3222  size = y2 - y1;
3223  pixGetPixel(pix, x1, y1, &val0);
3224  for (i = y1 + 1; i <= y2; i++) {
3225  pixGetPixel(pix, x1, i, &val1);
3226  sum += L_ABS((l_int32)val1 - (l_int32)val0);
3227  val0 = val1;
3228  }
3229  }
3230  *pabsdiff = (l_float32)sum / (l_float32)size;
3231  return 0;
3232 }
3233 
3234 
3235 /*-------------------------------------------------------------*
3236  * Count of pixels with specific value *
3237  *-------------------------------------------------------------*/
3257 l_int32
3259  BOX *box,
3260  l_int32 val,
3261  l_int32 factor,
3262  l_int32 *pcount)
3263 {
3264 l_int32 i, j, bx, by, bw, bh, w, h, d, wpl, pixval;
3265 l_uint32 *data, *line;
3266 
3267  if (!pcount)
3268  return ERROR_INT("&count not defined", __func__, 1);
3269  *pcount = 0;
3270  if (!pixs)
3271  return ERROR_INT("pixs not defined", __func__, 1);
3272  d = pixGetDepth(pixs);
3273  if (d != 1 && d != 2 && d != 4 && d != 8)
3274  return ERROR_INT("pixs not 1, 2, 4 or 8 bpp", __func__, 1);
3275  if (val < 0)
3276  return ERROR_INT("val < 0", __func__, 1);
3277  if (val > (1 << d) - 1) {
3278  L_ERROR("invalid val = %d for depth %d\n", __func__, val, d);
3279  return 1;
3280  }
3281  if (factor < 1)
3282  return ERROR_INT("sampling factor < 1", __func__, 1);
3283 
3284  pixGetDimensions(pixs, &w, &h, NULL);
3285  data = pixGetData(pixs);
3286  wpl = pixGetWpl(pixs);
3287  if (!box) {
3288  for (i = 0; i < h; i += factor) {
3289  line = data + i * wpl;
3290  for (j = 0; j < w; j += factor) {
3291  if (d == 8) {
3292  pixval = GET_DATA_BYTE(line, j);
3293  } else if (d == 1) {
3294  pixval = GET_DATA_BIT(line, j);
3295  } else if (d == 2) {
3296  pixval = GET_DATA_DIBIT(line, j);
3297  } else /* d == 4 */ {
3298  pixval = GET_DATA_QBIT(line, j);
3299  }
3300  if (pixval == val) (*pcount)++;
3301  }
3302  }
3303  } else {
3304  boxGetGeometry(box, &bx, &by, &bw, &bh);
3305  for (i = 0; i < bh; i += factor) {
3306  if (by + i < 0 || by + i >= h) continue;
3307  line = data + (by + i) * wpl;
3308  for (j = 0; j < bw; j += factor) {
3309  if (bx + j < 0 || bx + j >= w) continue;
3310  if (d == 8) {
3311  pixval = GET_DATA_BYTE(line, bx + j);
3312  } else if (d == 1) {
3313  pixval = GET_DATA_BIT(line, bx + j);
3314  } else if (d == 2) {
3315  pixval = GET_DATA_DIBIT(line, bx + j);
3316  } else /* d == 4 */ {
3317  pixval = GET_DATA_QBIT(line, bx + j);
3318  }
3319  if (pixval == val) (*pcount)++;
3320  }
3321  }
3322  }
3323 
3324  if (factor > 1) /* assume pixel color is randomly distributed */
3325  *pcount = *pcount * factor * factor;
3326  return 0;
3327 }
3328 
3329 
3330 /*-------------------------------------------------------------*
3331  * Mirrored tiling of a smaller image *
3332  *-------------------------------------------------------------*/
3354 PIX *
3356  l_int32 w,
3357  l_int32 h)
3358 {
3359 l_int32 wt, ht, d, i, j, nx, ny;
3360 PIX *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix;
3361 
3362  if (!pixs)
3363  return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
3364  pixGetDimensions(pixs, &wt, &ht, &d);
3365  if (wt <= 0 || ht <= 0)
3366  return (PIX *)ERROR_PTR("pixs size illegal", __func__, NULL);
3367  if (d != 8 && d != 32)
3368  return (PIX *)ERROR_PTR("depth not 32 bpp", __func__, NULL);
3369 
3370  if ((pixd = pixCreate(w, h, d)) == NULL)
3371  return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
3372  pixCopySpp(pixd, pixs);
3373 
3374  nx = (w + wt - 1) / wt;
3375  ny = (h + ht - 1) / ht;
3376  pixsfx = pixFlipLR(NULL, pixs);
3377  pixsfy = pixFlipTB(NULL, pixs);
3378  pixsfxy = pixFlipTB(NULL, pixsfx);
3379  for (i = 0; i < ny; i++) {
3380  for (j = 0; j < nx; j++) {
3381  pix = pixs;
3382  if ((i & 1) && !(j & 1))
3383  pix = pixsfy;
3384  else if (!(i & 1) && (j & 1))
3385  pix = pixsfx;
3386  else if ((i & 1) && (j & 1))
3387  pix = pixsfxy;
3388  pixRasterop(pixd, j * wt, i * ht, wt, ht, PIX_SRC, pix, 0, 0);
3389  }
3390  }
3391 
3392  pixDestroy(&pixsfx);
3393  pixDestroy(&pixsfy);
3394  pixDestroy(&pixsfxy);
3395  return pixd;
3396 }
3397 
3398 
3427 l_ok
3429  BOX *box,
3430  l_int32 searchdir,
3431  l_int32 mindist,
3432  l_int32 tsize,
3433  l_int32 ntiles,
3434  BOX **pboxtile,
3435  l_int32 debug)
3436 {
3437 l_int32 w, h, i, n, bestindex;
3438 l_float32 var_of_mean, median_of_mean, median_of_stdev, mean_val, stdev_val;
3439 l_float32 mindels, bestdelm, delm, dels, mean, stdev;
3440 BOXA *boxa;
3441 NUMA *namean, *nastdev;
3442 PIX *pix, *pixg;
3443 PIXA *pixa;
3444 
3445  if (!pboxtile)
3446  return ERROR_INT("&boxtile not defined", __func__, 1);
3447  *pboxtile = NULL;
3448  if (!pixs)
3449  return ERROR_INT("pixs not defined", __func__, 1);
3450  if (!box)
3451  return ERROR_INT("box not defined", __func__, 1);
3452  if (searchdir != L_HORIZ && searchdir != L_VERT)
3453  return ERROR_INT("invalid searchdir", __func__, 1);
3454  if (mindist < 0)
3455  return ERROR_INT("mindist must be >= 0", __func__, 1);
3456  if (tsize < 2)
3457  return ERROR_INT("tsize must be > 1", __func__, 1);
3458  if (ntiles > 7) {
3459  L_WARNING("ntiles = %d; larger than suggested max of 7\n",
3460  __func__, ntiles);
3461  }
3462 
3463  /* Locate tile regions */
3464  pixGetDimensions(pixs, &w, &h, NULL);
3465  boxa = findTileRegionsForSearch(box, w, h, searchdir, mindist,
3466  tsize, ntiles);
3467  if (!boxa)
3468  return ERROR_INT("no tiles found", __func__, 1);
3469 
3470  /* Generate the tiles and the mean and stdev of intensity */
3471  pixa = pixClipRectangles(pixs, boxa);
3472  n = pixaGetCount(pixa);
3473  namean = numaCreate(n);
3474  nastdev = numaCreate(n);
3475  for (i = 0; i < n; i++) {
3476  pix = pixaGetPix(pixa, i, L_CLONE);
3477  pixg = pixConvertRGBToGray(pix, 0.33, 0.34, 0.33);
3478  pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &mean);
3479  pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_STANDARD_DEVIATION, &stdev);
3480  numaAddNumber(namean, mean);
3481  numaAddNumber(nastdev, stdev);
3482  pixDestroy(&pix);
3483  pixDestroy(&pixg);
3484  }
3485 
3486  /* Find the median and variance of the averages. We require
3487  * the best tile to have a mean pixel intensity within a standard
3488  * deviation of the median of mean intensities, and choose the
3489  * tile in that set with the smallest stdev of pixel intensities
3490  * (as a proxy for the tile with least visible structure).
3491  * The median of the stdev is used, for debugging, as a normalizing
3492  * factor for the stdev of intensities within a tile. */
3493  numaGetStatsUsingHistogram(namean, 256, NULL, NULL, NULL, &var_of_mean,
3494  &median_of_mean, 0.0, NULL, NULL);
3495  numaGetStatsUsingHistogram(nastdev, 256, NULL, NULL, NULL, NULL,
3496  &median_of_stdev, 0.0, NULL, NULL);
3497  mindels = 1000.0;
3498  bestdelm = 1000.0;
3499  bestindex = 0;
3500  for (i = 0; i < n; i++) {
3501  numaGetFValue(namean, i, &mean_val);
3502  numaGetFValue(nastdev, i, &stdev_val);
3503  if (var_of_mean == 0.0) { /* uniform color; any box will do */
3504  delm = 0.0; /* any value < 1.01 */
3505  dels = 1.0; /* n'importe quoi */
3506  } else {
3507  delm = L_ABS(mean_val - median_of_mean) / sqrt(var_of_mean);
3508  dels = stdev_val / median_of_stdev;
3509  }
3510  if (delm < 1.01) {
3511  if (dels < mindels) {
3512  if (debug) {
3513  lept_stderr("i = %d, mean = %7.3f, delm = %7.3f,"
3514  " stdev = %7.3f, dels = %7.3f\n",
3515  i, mean_val, delm, stdev_val, dels);
3516  }
3517  mindels = dels;
3518  bestdelm = delm;
3519  bestindex = i;
3520  }
3521  }
3522  }
3523  *pboxtile = boxaGetBox(boxa, bestindex, L_COPY);
3524 
3525  if (debug) {
3526  L_INFO("median of mean = %7.3f\n", __func__, median_of_mean);
3527  L_INFO("standard dev of mean = %7.3f\n", __func__, sqrt(var_of_mean));
3528  L_INFO("median of stdev = %7.3f\n", __func__, median_of_stdev);
3529  L_INFO("best tile: index = %d\n", __func__, bestindex);
3530  L_INFO("delta from median in units of stdev = %5.3f\n",
3531  __func__, bestdelm);
3532  L_INFO("stdev as fraction of median stdev = %5.3f\n",
3533  __func__, mindels);
3534  }
3535 
3536  numaDestroy(&namean);
3537  numaDestroy(&nastdev);
3538  pixaDestroy(&pixa);
3539  boxaDestroy(&boxa);
3540  return 0;
3541 }
3542 
3543 
3560 static BOXA *
3562  l_int32 w,
3563  l_int32 h,
3564  l_int32 searchdir,
3565  l_int32 mindist,
3566  l_int32 tsize,
3567  l_int32 ntiles)
3568 {
3569 l_int32 bx, by, bw, bh, left, right, top, bot, i, j, nrows, ncols;
3570 l_int32 x0, y0, x, y, w_avail, w_needed, h_avail, h_needed, t_avail;
3571 BOX *box1;
3572 BOXA *boxa;
3573 
3574  if (!box)
3575  return (BOXA *)ERROR_PTR("box not defined", __func__, NULL);
3576  if (ntiles == 0)
3577  return (BOXA *)ERROR_PTR("no tiles requested", __func__, NULL);
3578 
3579  boxGetGeometry(box, &bx, &by, &bw, &bh);
3580  if (searchdir == L_HORIZ) {
3581  /* Find the tile parameters for the search. Note that the
3582  * tiles are overlapping by 50% in each direction. */
3583  left = bx; /* distance to left of box */
3584  right = w - bx - bw + 1; /* distance to right of box */
3585  w_avail = L_MAX(left, right) - mindist;
3586  if (tsize & 1) tsize++; /* be sure it's even */
3587  if (w_avail < tsize) {
3588  L_ERROR("tsize = %d, w_avail = %d\n", __func__, tsize, w_avail);
3589  return NULL;
3590  }
3591  w_needed = tsize + (ntiles - 1) * (tsize / 2);
3592  if (w_needed > w_avail) {
3593  t_avail = 1 + 2 * (w_avail - tsize) / tsize;
3594  L_WARNING("ntiles = %d; room for only %d\n", __func__,
3595  ntiles, t_avail);
3596  ntiles = t_avail;
3597  w_needed = tsize + (ntiles - 1) * (tsize / 2);
3598  }
3599  nrows = L_MAX(1, 1 + 2 * (bh - tsize) / tsize);
3600 
3601  /* Generate the tile regions to search */
3602  boxa = boxaCreate(0);
3603  if (left > right) /* search to left */
3604  x0 = bx - w_needed;
3605  else /* search to right */
3606  x0 = bx + bw + mindist;
3607  for (i = 0; i < nrows; i++) {
3608  y = by + i * tsize / 2;
3609  for (j = 0; j < ntiles; j++) {
3610  x = x0 + j * tsize / 2;
3611  box1 = boxCreate(x, y, tsize, tsize);
3612  boxaAddBox(boxa, box1, L_INSERT);
3613  }
3614  }
3615  } else { /* L_VERT */
3616  /* Find the tile parameters for the search */
3617  top = by; /* distance above box */
3618  bot = h - by - bh + 1; /* distance below box */
3619  h_avail = L_MAX(top, bot) - mindist;
3620  if (h_avail < tsize) {
3621  L_ERROR("tsize = %d, h_avail = %d\n", __func__, tsize, h_avail);
3622  return NULL;
3623  }
3624  h_needed = tsize + (ntiles - 1) * (tsize / 2);
3625  if (h_needed > h_avail) {
3626  t_avail = 1 + 2 * (h_avail - tsize) / tsize;
3627  L_WARNING("ntiles = %d; room for only %d\n", __func__,
3628  ntiles, t_avail);
3629  ntiles = t_avail;
3630  h_needed = tsize + (ntiles - 1) * (tsize / 2);
3631  }
3632  ncols = L_MAX(1, 1 + 2 * (bw - tsize) / tsize);
3633 
3634  /* Generate the tile regions to search */
3635  boxa = boxaCreate(0);
3636  if (top > bot) /* search above */
3637  y0 = by - h_needed;
3638  else /* search below */
3639  y0 = by + bh + mindist;
3640  for (j = 0; j < ncols; j++) {
3641  x = bx + j * tsize / 2;
3642  for (i = 0; i < ntiles; i++) {
3643  y = y0 + i * tsize / 2;
3644  box1 = boxCreate(x, y, tsize, tsize);
3645  boxaAddBox(boxa, box1, L_INSERT);
3646  }
3647  }
3648  }
3649  return boxa;
3650 }
#define GET_DATA_QBIT(pdata, n)
Definition: arrayaccess.h:164
#define GET_DATA_TWO_BYTES(pdata, n)
Definition: arrayaccess.h:212
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
#define SET_DATA_DIBIT(pdata, n, val)
Definition: arrayaccess.h:149
#define SET_DATA_TWO_BYTES(pdata, n, val)
Definition: arrayaccess.h:222
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:145
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
#define SET_DATA_QBIT(pdata, n, val)
Definition: arrayaccess.h:168
PIX * pixBlendWithGrayMask(PIX *pixs1, PIX *pixs2, PIX *pixg, l_int32 x, l_int32 y)
pixBlendWithGrayMask()
Definition: blend.c:1692
PIX * pixBlend(PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract)
pixBlend()
Definition: blend.c:176
l_ok boxGetGeometry(const BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:301
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:273
l_ok boxaGetBoxGeometry(BOXA *boxa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxaGetBoxGeometry()
Definition: boxbasic.c:796
l_ok boxaAddBox(BOXA *boxa, BOX *box, l_int32 copyflag)
boxaAddBox()
Definition: boxbasic.c:553
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:519
l_int32 boxaGetCount(const BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:661
BOX * boxaGetBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetBox()
Definition: boxbasic.c:702
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:171
BOXA * boxaCreate(l_int32 n)
boxaCreate()
Definition: boxbasic.c:442
l_ok boxPrintStreamInfo(FILE *fp, BOX *box)
boxPrintStreamInfo()
Definition: boxbasic.c:2270
BOX * boxClipToRectangle(BOX *box, l_int32 wi, l_int32 hi)
boxClipToRectangle()
Definition: boxfunc1.c:1679
BOX * boxAdjustSides(BOX *boxd, BOX *boxs, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot)
boxAdjustSides()
Definition: boxfunc1.c:1932
l_ok boxClipToRectangleParams(BOX *box, l_int32 w, l_int32 h, l_int32 *pxstart, l_int32 *pystart, l_int32 *pxend, l_int32 *pyend, l_int32 *pbw, l_int32 *pbh)
boxClipToRectangleParams()
Definition: boxfunc1.c:1734
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:152
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:443
PIX * pixDilate(PIX *pixd, PIX *pixs, SEL *sel)
pixDilate()
Definition: morph.c:213
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:672
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:460
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:687
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:193
l_ok numaSetCount(NUMA *na, l_int32 newcount)
numaSetCount()
Definition: numabasic.c:655
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:357
l_ok numaSetParameters(NUMA *na, l_float32 startx, l_float32 delx)
numaSetParameters()
Definition: numabasic.c:910
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:850
l_ok numaGetStatsUsingHistogram(NUMA *na, l_int32 maxbins, l_float32 *pmin, l_float32 *pmax, l_float32 *pmean, l_float32 *pvariance, l_float32 *pmedian, l_float32 rank, l_float32 *prval, NUMA **phisto)
numaGetStatsUsingHistogram()
Definition: numafunc2.c:1227
l_ok pixSetMaskedCmap(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval)
pixSetMaskedCmap()
Definition: paintcmap.c:686
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1642
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1074
l_int32 pixSizesEqual(const PIX *pix1, const PIX *pix2)
pixSizesEqual()
Definition: pix1.c:1878
l_ok pixCopySpp(PIX *pixd, const PIX *pixs)
pixCopySpp()
Definition: pix1.c:1187
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:689
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:380
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:582
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2464
PIX * pixDisplayLayersRGBA(PIX *pixs, l_uint32 val, l_int32 maxw)
pixDisplayLayersRGBA()
Definition: pix2.c:2340
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:192
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:799
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2728
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2793
l_ok pixSetBlackOrWhite(PIX *pixs, l_int32 op)
pixSetBlackOrWhite()
Definition: pix2.c:997
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:929
NUMA * pixVarianceByRow(PIX *pix, BOX *box)
pixVarianceByRow()
Definition: pix3.c:2789
l_ok pixGetColorNearMaskBoundary(PIX *pixs, PIX *pixm, BOX *box, l_int32 dist, l_uint32 *pval, l_int32 debug)
pixGetColorNearMaskBoundary()
Definition: pix3.c:1329
PIX * pixMirroredTiling(PIX *pixs, l_int32 w, l_int32 h)
pixMirroredTiling()
Definition: pix3.c:3355
NUMA * pixVarianceByColumn(PIX *pix, BOX *box)
pixVarianceByColumn()
Definition: pix3.c:2852
NUMA * pixCountByColumn(PIX *pix, BOX *box)
pixCountByColumn()
Definition: pix3.c:2052
l_ok pixCombineMaskedGeneral(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 x, l_int32 y)
pixCombineMaskedGeneral()
Definition: pix3.c:495
l_int32 pixCountArbInRect(PIX *pixs, BOX *box, l_int32 val, l_int32 factor, l_int32 *pcount)
pixCountArbInRect()
Definition: pix3.c:3258
l_ok pixZero(PIX *pix, l_int32 *pempty)
pixZero()
Definition: pix3.c:1777
NUMA * pixaCountPixels(PIXA *pixa)
pixaCountPixels()
Definition: pix3.c:1850
NUMA * pixAverageByColumn(PIX *pix, BOX *box, l_int32 type)
pixAverageByColumn()
Definition: pix3.c:2514
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1481
PIX * pixCopyWithBoxa(PIX *pixs, BOXA *boxa, l_int32 background)
pixCopyWithBoxa()
Definition: pix3.c:749
l_ok pixPaintThroughMask(PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_uint32 val)
pixPaintThroughMask()
Definition: pix3.c:618
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1893
NUMA * pixAbsDiffByColumn(PIX *pix, BOX *box)
pixAbsDiffByColumn()
Definition: pix3.c:3042
PIX * pixSetUnderTransparency(PIX *pixs, l_uint32 val, l_int32 debug)
pixSetUnderTransparency()
Definition: pix3.c:1188
PIX * pixMakeAlphaFromMask(PIX *pixs, l_int32 dist, BOX **pbox)
pixMakeAlphaFromMask()
Definition: pix3.c:1262
PIX * pixDisplaySelectedPixels(PIX *pixs, PIX *pixm, SEL *sel, l_uint32 val)
pixDisplaySelectedPixels()
Definition: pix3.c:1427
PIX * pixOr(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixOr()
Definition: pix3.c:1530
NUMA * pixCountPixelsByColumn(PIX *pix)
pixCountPixelsByColumn()
Definition: pix3.c:2128
PIX * pixAnd(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixAnd()
Definition: pix3.c:1592
static BOXA * findTileRegionsForSearch(BOX *box, l_int32 w, l_int32 h, l_int32 searchdir, l_int32 mindist, l_int32 tsize, l_int32 ntiles)
findTileRegionsForSearch()
Definition: pix3.c:3561
l_ok pixCountPixelsInRect(PIX *pixs, BOX *box, l_int32 *pcount, l_int32 *tab8)
pixCountPixelsInRect()
Definition: pix3.c:1955
l_ok pixSetMaskedGeneral(PIX *pixd, PIX *pixm, l_uint32 val, l_int32 x, l_int32 y)
pixSetMaskedGeneral()
Definition: pix3.c:300
l_ok pixAbsDiffInRect(PIX *pix, BOX *box, l_int32 dir, l_float32 *pabsdiff)
pixAbsDiffInRect()
Definition: pix3.c:3103
l_ok 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)
pixPaintSelfThroughMask()
Definition: pix3.c:836
l_ok pixAverageInRectRGB(PIX *pixs, PIX *pixm, BOX *box, l_int32 subsamp, l_uint32 *pave)
pixAverageInRectRGB()
Definition: pix3.c:2704
l_ok pixCountPixelsInRow(PIX *pix, l_int32 row, l_int32 *pcount, l_int32 *tab8)
pixCountPixelsInRow()
Definition: pix3.c:2167
l_ok pixAverageInRect(PIX *pixs, PIX *pixm, BOX *box, l_int32 minval, l_int32 maxval, l_int32 subsamp, l_float32 *pave)
pixAverageInRect()
Definition: pix3.c:2599
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:163
NUMA * pixGetMomentByColumn(PIX *pix, l_int32 order)
pixGetMomentByColumn()
Definition: pix3.c:2228
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2354
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1654
l_int32 * makePixelCentroidTab8(void)
makePixelCentroidTab8()
Definition: pix3.c:2394
PIX * pixMakeMaskFromVal(PIX *pixs, l_int32 val)
pixMakeMaskFromVal()
Definition: pix3.c:991
l_ok pixForegroundFraction(PIX *pix, l_float32 *pfract)
pixForegroundFraction()
Definition: pix3.c:1825
l_ok pixCombineMasked(PIX *pixd, PIX *pixs, PIX *pixm)
pixCombineMasked()
Definition: pix3.c:378
NUMA * pixCountPixelsByRow(PIX *pix, l_int32 *tab8)
pixCountPixelsByRow()
Definition: pix3.c:2096
NUMA * pixCountByRow(PIX *pix, BOX *box)
pixCountByRow()
Definition: pix3.c:2002
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1717
l_ok pixVarianceInRect(PIX *pix, BOX *box, l_float32 *prootvar)
pixVarianceInRect()
Definition: pix3.c:2908
l_ok pixThresholdPixelSum(PIX *pix, l_int32 thresh, l_int32 *pabove, l_int32 *tab8)
pixThresholdPixelSum()
Definition: pix3.c:2284
PIX * pixMakeMaskFromLUT(PIX *pixs, l_int32 *tab)
pixMakeMaskFromLUT()
Definition: pix3.c:1046
l_ok pixFindRepCloseTile(PIX *pixs, BOX *box, l_int32 searchdir, l_int32 mindist, l_int32 tsize, l_int32 ntiles, BOX **pboxtile, l_int32 debug)
pixFindRepCloseTile()
Definition: pix3.c:3428
PIX * pixMakeArbMaskFromRGB(PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc, l_float32 thresh)
pixMakeArbMaskFromRGB()
Definition: pix3.c:1113
NUMA * pixAbsDiffByRow(PIX *pix, BOX *box)
pixAbsDiffByRow()
Definition: pix3.c:2983
NUMA * pixAverageByRow(PIX *pix, BOX *box, l_int32 type)
pixAverageByRow()
Definition: pix3.c:2447
l_ok pixAbsDiffOnLine(PIX *pix, l_int32 x1, l_int32 y1, l_int32 x2, l_int32 y2, l_float32 *pabsdiff)
pixAbsDiffOnLine()
Definition: pix3.c:3178
l_ok pixGetAverageMasked(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_int32 type, l_float32 *pval)
pixGetAverageMasked()
Definition: pix4.c:1488
l_ok pixGetAverageMaskedRGB(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_int32 type, l_float32 *prval, l_float32 *pgval, l_float32 *pbval)
pixGetAverageMaskedRGB()
Definition: pix4.c:1388
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:994
l_ok pixClipToForeground(PIX *pixs, PIX **ppixd, BOX **pbox)
pixClipToForeground()
Definition: pix5.c:1728
PIXA * pixClipRectangles(PIX *pixs, BOXA *boxa)
pixClipRectangles()
Definition: pix5.c:930
#define PIX_MASK
Definition: pix.h:451
#define PIX_DST
Definition: pix.h:445
@ L_ALPHA_CHANNEL
Definition: pix.h:331
@ L_HORIZONTAL_LINE
Definition: pix.h:806
@ L_VERTICAL_LINE
Definition: pix.h:808
@ L_COPY
Definition: pix.h:505
@ L_CLONE
Definition: pix.h:506
@ L_NOCOPY
Definition: pix.h:503
@ L_INSERT
Definition: pix.h:504
#define PIX_PAINT
Definition: pix.h:450
@ L_SET_WHITE
Definition: pix.h:699
@ L_SET_BLACK
Definition: pix.h:700
@ L_BLACK_IS_MAX
Definition: pix.h:718
@ L_WHITE_IS_MAX
Definition: pix.h:717
#define PIX_SRC
Definition: pix.h:444
#define PIX_NOT(op)
Definition: pix.h:446
@ L_MEAN_ABSVAL
Definition: pix.h:761
@ L_STANDARD_DEVIATION
Definition: pix.h:766
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:404
BOX * pixaGetBox(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetBox()
Definition: pixabasic.c:764
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:629
l_ok pixaGetBoxGeometry(PIXA *pixa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
pixaGetBoxGeometry()
Definition: pixabasic.c:800
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:647
l_ok pixMultConstantGray(PIX *pixs, l_float32 val)
pixMultConstantGray()
Definition: pixarith.c:188
PIX * pixUnpackBinary(PIX *pixs, l_int32 depth, l_int32 invert)
pixUnpackBinary()
Definition: pixconv.c:1873
PIX * pixConvertRGBToGray(PIX *pixs, l_float32 rwt, l_float32 gwt, l_float32 bwt)
pixConvertRGBToGray()
Definition: pixconv.c:815
PIX * pixConvert1To8(PIX *pixd, PIX *pixs, l_uint8 val0, l_uint8 val1)
pixConvert1To8()
Definition: pixconv.c:2345
PIX * pixConvertRGBToGrayArb(PIX *pixs, l_float32 rc, l_float32 gc, l_float32 bc)
pixConvertRGBToGrayArb()
Definition: pixconv.c:1108
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3246
l_ok pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy)
pixRasterop()
Definition: rop.c:204
PIX * pixFlipLR(PIX *pixd, PIX *pixs)
pixFlipLR()
Definition: rotateorth.c:421
PIX * pixFlipTB(PIX *pixd, PIX *pixs)
pixFlipTB()
Definition: rotateorth.c:597
PIX * pixDistanceFunction(PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond)
pixDistanceFunction()
Definition: seedfill.c:2499
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
l_int32 lept_rmdir(const char *subdir)
lept_rmdir()
Definition: utils2.c:2213
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2138