Leptonica  1.82.0
Image processing and image analysis suite
compare.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 
105 #ifdef HAVE_CONFIG_H
106 #include <config_auto.h>
107 #endif /* HAVE_CONFIG_H */
108 
109 #include <string.h>
110 #include <math.h>
111 #include "allheaders.h"
112 
113  /* Small enough to consider equal to 0.0, for plot output */
114 static const l_float32 TINY = 0.00001;
115 
116 static l_ok findHistoGridDimensions(l_int32 n, l_int32 w, l_int32 h,
117  l_int32 *pnx, l_int32 *pny, l_int32 debug);
118 static l_ok pixCompareTilesByHisto(PIX *pix1, PIX *pix2, l_int32 maxgray,
119  l_int32 factor, l_int32 n,
120  l_float32 *pscore, PIXA *pixadebug);
121 
122 /*------------------------------------------------------------------*
123  * Test for pix equality *
124  *------------------------------------------------------------------*/
155 l_ok
156 pixEqual(PIX *pix1,
157  PIX *pix2,
158  l_int32 *psame)
159 {
160  return pixEqualWithAlpha(pix1, pix2, 0, psame);
161 }
162 
163 
181 l_ok
183  PIX *pix2,
184  l_int32 use_alpha,
185  l_int32 *psame)
186 {
187 l_int32 w1, h1, d1, w2, h2, d2, wpl1, wpl2;
188 l_int32 spp1, spp2, i, j, color, mismatch, opaque;
189 l_int32 fullwords, linebits, endbits;
190 l_uint32 endmask, wordmask;
191 l_uint32 *data1, *data2, *line1, *line2;
192 PIX *pixs1, *pixs2, *pixt1, *pixt2, *pixalpha;
193 PIXCMAP *cmap1, *cmap2;
194 
195  PROCNAME("pixEqualWithAlpha");
196 
197  if (!psame)
198  return ERROR_INT("psame not defined", procName, 1);
199  *psame = 0; /* init to not equal */
200  if (!pix1 || !pix2)
201  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
202  pixGetDimensions(pix1, &w1, &h1, &d1);
203  pixGetDimensions(pix2, &w2, &h2, &d2);
204  if (w1 != w2 || h1 != h2) {
205  L_INFO("pix sizes differ\n", procName);
206  return 0;
207  }
208 
209  /* Suppose the use_alpha flag is true.
210  * If only one of two 32 bpp images has spp == 4, we call that
211  * a "mismatch" of the alpha component. In the case of a mismatch,
212  * if the 4 bpp pix does not have all alpha components opaque (255),
213  * the images are not-equal. However if they are all opaque,
214  * this image is equivalent to spp == 3, so we allow the
215  * comparison to go forward, testing only for the RGB equality. */
216  spp1 = pixGetSpp(pix1);
217  spp2 = pixGetSpp(pix2);
218  mismatch = 0;
219  if (use_alpha && d1 == 32 && d2 == 32) {
220  mismatch = ((spp1 == 4 && spp2 != 4) || (spp1 != 4 && spp2 == 4));
221  if (mismatch) {
222  pixalpha = (spp1 == 4) ? pix1 : pix2;
223  pixAlphaIsOpaque(pixalpha, &opaque);
224  if (!opaque) {
225  L_INFO("just one pix has a non-opaque alpha layer\n", procName);
226  return 0;
227  }
228  }
229  }
230 
231  cmap1 = pixGetColormap(pix1);
232  cmap2 = pixGetColormap(pix2);
233  if (!cmap1 && !cmap2 && (d1 != d2) && (d1 == 32 || d2 == 32)) {
234  L_INFO("no colormaps, pix depths unequal, and one of them is RGB\n",
235  procName);
236  return 0;
237  }
238 
239  if (cmap1 && cmap2 && (d1 == d2)) /* use special function */
240  return pixEqualWithCmap(pix1, pix2, psame);
241 
242  /* Must remove colormaps if they exist, and in the process
243  * end up with the resulting images having the same depth. */
244  if (cmap1 && !cmap2) {
245  pixUsesCmapColor(pix1, &color);
246  if (color && d2 <= 8) /* can't be equal */
247  return 0;
248  if (d2 < 8)
249  pixs2 = pixConvertTo8(pix2, FALSE);
250  else
251  pixs2 = pixClone(pix2);
252  if (d2 <= 8)
254  else
256  } else if (!cmap1 && cmap2) {
257  pixUsesCmapColor(pix2, &color);
258  if (color && d1 <= 8) /* can't be equal */
259  return 0;
260  if (d1 < 8)
261  pixs1 = pixConvertTo8(pix1, FALSE);
262  else
263  pixs1 = pixClone(pix1);
264  if (d1 <= 8)
266  else
268  } else if (cmap1 && cmap2) { /* depths not equal; use rgb */
271  } else { /* no colormaps */
272  pixs1 = pixClone(pix1);
273  pixs2 = pixClone(pix2);
274  }
275 
276  /* OK, we have no colormaps, but the depths may still be different */
277  d1 = pixGetDepth(pixs1);
278  d2 = pixGetDepth(pixs2);
279  if (d1 != d2) {
280  if (d1 == 16 || d2 == 16) {
281  L_INFO("one pix is 16 bpp\n", procName);
282  pixDestroy(&pixs1);
283  pixDestroy(&pixs2);
284  return 0;
285  }
286  pixt1 = pixConvertLossless(pixs1, 8);
287  pixt2 = pixConvertLossless(pixs2, 8);
288  if (!pixt1 || !pixt2) {
289  L_INFO("failure to convert to 8 bpp\n", procName);
290  pixDestroy(&pixs1);
291  pixDestroy(&pixs2);
292  pixDestroy(&pixt1);
293  pixDestroy(&pixt2);
294  return 0;
295  }
296  } else {
297  pixt1 = pixClone(pixs1);
298  pixt2 = pixClone(pixs2);
299  }
300  pixDestroy(&pixs1);
301  pixDestroy(&pixs2);
302 
303  /* No colormaps, equal depths; do pixel comparisons */
304  d1 = pixGetDepth(pixt1);
305  d2 = pixGetDepth(pixt2);
306  wpl1 = pixGetWpl(pixt1);
307  wpl2 = pixGetWpl(pixt2);
308  data1 = pixGetData(pixt1);
309  data2 = pixGetData(pixt2);
310 
311  if (d1 == 32) { /* test either RGB or RGBA pixels */
312  if (use_alpha && !mismatch)
313  wordmask = (spp1 == 3) ? 0xffffff00 : 0xffffffff;
314  else
315  wordmask = 0xffffff00;
316  for (i = 0; i < h1; i++) {
317  line1 = data1 + wpl1 * i;
318  line2 = data2 + wpl2 * i;
319  for (j = 0; j < wpl1; j++) {
320  if ((*line1 ^ *line2) & wordmask) {
321  pixDestroy(&pixt1);
322  pixDestroy(&pixt2);
323  return 0;
324  }
325  line1++;
326  line2++;
327  }
328  }
329  } else { /* all bits count */
330  linebits = d1 * w1;
331  fullwords = linebits / 32;
332  endbits = linebits & 31;
333  endmask = (endbits == 0) ? 0 : (0xffffffff << (32 - endbits));
334  for (i = 0; i < h1; i++) {
335  line1 = data1 + wpl1 * i;
336  line2 = data2 + wpl2 * i;
337  for (j = 0; j < fullwords; j++) {
338  if (*line1 ^ *line2) {
339  pixDestroy(&pixt1);
340  pixDestroy(&pixt2);
341  return 0;
342  }
343  line1++;
344  line2++;
345  }
346  if (endbits) {
347  if ((*line1 ^ *line2) & endmask) {
348  pixDestroy(&pixt1);
349  pixDestroy(&pixt2);
350  return 0;
351  }
352  }
353  }
354  }
355 
356  pixDestroy(&pixt1);
357  pixDestroy(&pixt2);
358  *psame = 1;
359  return 0;
360 }
361 
362 
383 l_ok
385  PIX *pix2,
386  l_int32 *psame)
387 {
388 l_int32 d, w, h, wpl1, wpl2, i, j, linebits, fullwords, endbits;
389 l_int32 rval1, rval2, gval1, gval2, bval1, bval2, samecmaps;
390 l_uint32 endmask, val1, val2;
391 l_uint32 *data1, *data2, *line1, *line2;
392 PIXCMAP *cmap1, *cmap2;
393 
394  PROCNAME("pixEqualWithCmap");
395 
396  if (!psame)
397  return ERROR_INT("&same not defined", procName, 1);
398  *psame = 0;
399  if (!pix1)
400  return ERROR_INT("pix1 not defined", procName, 1);
401  if (!pix2)
402  return ERROR_INT("pix2 not defined", procName, 1);
403 
404  if (pixSizesEqual(pix1, pix2) == 0)
405  return 0;
406  cmap1 = pixGetColormap(pix1);
407  cmap2 = pixGetColormap(pix2);
408  if (!cmap1 || !cmap2) {
409  L_INFO("both images don't have colormap\n", procName);
410  return 0;
411  }
412  pixGetDimensions(pix1, &w, &h, &d);
413  if (d != 1 && d != 2 && d != 4 && d != 8) {
414  L_INFO("pix depth not in {1, 2, 4, 8}\n", procName);
415  return 0;
416  }
417 
418  cmapEqual(cmap1, cmap2, 3, &samecmaps);
419  if (samecmaps == TRUE) { /* colormaps are identical; compare by words */
420  linebits = d * w;
421  wpl1 = pixGetWpl(pix1);
422  wpl2 = pixGetWpl(pix2);
423  data1 = pixGetData(pix1);
424  data2 = pixGetData(pix2);
425  fullwords = linebits / 32;
426  endbits = linebits & 31;
427  endmask = (endbits == 0) ? 0 : (0xffffffff << (32 - endbits));
428  for (i = 0; i < h; i++) {
429  line1 = data1 + wpl1 * i;
430  line2 = data2 + wpl2 * i;
431  for (j = 0; j < fullwords; j++) {
432  if (*line1 ^ *line2)
433  return 0;
434  line1++;
435  line2++;
436  }
437  if (endbits) {
438  if ((*line1 ^ *line2) & endmask)
439  return 0;
440  }
441  }
442  *psame = 1;
443  return 0;
444  }
445 
446  /* Colormaps aren't identical; compare pixel by pixel */
447  for (i = 0; i < h; i++) {
448  for (j = 0; j < w; j++) {
449  pixGetPixel(pix1, j, i, &val1);
450  pixGetPixel(pix2, j, i, &val2);
451  pixcmapGetColor(cmap1, val1, &rval1, &gval1, &bval1);
452  pixcmapGetColor(cmap2, val2, &rval2, &gval2, &bval2);
453  if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2)
454  return 0;
455  }
456  }
457 
458  *psame = 1;
459  return 0;
460 }
461 
462 
479 l_ok
481  PIXCMAP *cmap2,
482  l_int32 ncomps,
483  l_int32 *psame)
484 {
485 l_int32 n1, n2, i, rval1, rval2, gval1, gval2, bval1, bval2, aval1, aval2;
486 
487  PROCNAME("cmapEqual");
488 
489  if (!psame)
490  return ERROR_INT("&same not defined", procName, 1);
491  *psame = FALSE;
492  if (!cmap1)
493  return ERROR_INT("cmap1 not defined", procName, 1);
494  if (!cmap2)
495  return ERROR_INT("cmap2 not defined", procName, 1);
496  if (ncomps != 3 && ncomps != 4)
497  return ERROR_INT("ncomps not 3 or 4", procName, 1);
498 
499  n1 = pixcmapGetCount(cmap1);
500  n2 = pixcmapGetCount(cmap2);
501  if (n1 != n2) {
502  L_INFO("colormap sizes are different\n", procName);
503  return 0;
504  }
505 
506  for (i = 0; i < n1; i++) {
507  pixcmapGetRGBA(cmap1, i, &rval1, &gval1, &bval1, &aval1);
508  pixcmapGetRGBA(cmap2, i, &rval2, &gval2, &bval2, &aval2);
509  if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2)
510  return 0;
511  if (ncomps == 4 && aval1 != aval2)
512  return 0;
513  }
514  *psame = TRUE;
515  return 0;
516 }
517 
518 
537 l_ok
539  l_int32 *pcolor)
540 {
541 l_int32 n, i, rval, gval, bval, numpix;
542 NUMA *na;
543 PIXCMAP *cmap;
544 
545  PROCNAME("pixUsesCmapColor");
546 
547  if (!pcolor)
548  return ERROR_INT("&color not defined", procName, 1);
549  *pcolor = 0;
550  if (!pixs)
551  return ERROR_INT("pixs not defined", procName, 1);
552 
553  if ((cmap = pixGetColormap(pixs)) == NULL)
554  return 0;
555 
556  pixcmapHasColor(cmap, pcolor);
557  if (*pcolor == 0) /* no color */
558  return 0;
559 
560  /* The cmap has color entries. Are they used? */
561  na = pixGetGrayHistogram(pixs, 1);
562  n = pixcmapGetCount(cmap);
563  for (i = 0; i < n; i++) {
564  pixcmapGetColor(cmap, i, &rval, &gval, &bval);
565  numaGetIValue(na, i, &numpix);
566  if ((rval != gval || rval != bval) && numpix) { /* color found! */
567  *pcolor = 1;
568  break;
569  }
570  }
571  numaDestroy(&na);
572 
573  return 0;
574 }
575 
576 
577 /*------------------------------------------------------------------*
578  * Binary correlation *
579  *------------------------------------------------------------------*/
603 l_ok
605  PIX *pix2,
606  l_float32 *pval)
607 {
608 l_int32 count1, count2, countn;
609 l_int32 *tab8;
610 PIX *pixn;
611 
612  PROCNAME("pixCorrelationBinary");
613 
614  if (!pval)
615  return ERROR_INT("&pval not defined", procName, 1);
616  *pval = 0.0;
617  if (!pix1)
618  return ERROR_INT("pix1 not defined", procName, 1);
619  if (!pix2)
620  return ERROR_INT("pix2 not defined", procName, 1);
621 
622  tab8 = makePixelSumTab8();
623  pixCountPixels(pix1, &count1, tab8);
624  pixCountPixels(pix2, &count2, tab8);
625  if (count1 == 0 || count2 == 0) {
626  LEPT_FREE(tab8);
627  return 0;
628  }
629  pixn = pixAnd(NULL, pix1, pix2);
630  pixCountPixels(pixn, &countn, tab8);
631  *pval = (l_float32)countn * (l_float32)countn /
632  ((l_float32)count1 * (l_float32)count2);
633  LEPT_FREE(tab8);
634  pixDestroy(&pixn);
635  return 0;
636 }
637 
638 
639 /*------------------------------------------------------------------*
640  * Difference of two images *
641  *------------------------------------------------------------------*/
661 PIX *
663  PIX *pix2)
664 {
665 l_int32 w1, h1, d1, w2, h2, d2, minw, minh;
666 PIX *pixt, *pixd;
667 PIXCMAP *cmap;
668 
669  PROCNAME("pixDisplayDiffBinary");
670 
671  if (!pix1 || !pix2)
672  return (PIX *)ERROR_PTR("pix1, pix2 not both defined", procName, NULL);
673  pixGetDimensions(pix1, &w1, &h1, &d1);
674  pixGetDimensions(pix2, &w2, &h2, &d2);
675  if (d1 != 1 || d2 != 1)
676  return (PIX *)ERROR_PTR("pix1 and pix2 not 1 bpp", procName, NULL);
677  minw = L_MIN(w1, w2);
678  minh = L_MIN(h1, h2);
679 
680  pixd = pixCreate(minw, minh, 4);
681  cmap = pixcmapCreate(4);
682  pixcmapAddColor(cmap, 255, 255, 255); /* initialized to white */
683  pixcmapAddColor(cmap, 0, 0, 0);
684  pixcmapAddColor(cmap, 255, 0, 0);
685  pixcmapAddColor(cmap, 0, 255, 0);
686  pixSetColormap(pixd, cmap);
687 
688  pixt = pixAnd(NULL, pix1, pix2);
689  pixPaintThroughMask(pixd, pixt, 0, 0, 0x0); /* black */
690  pixSubtract(pixt, pix1, pix2);
691  pixPaintThroughMask(pixd, pixt, 0, 0, 0xff000000); /* red */
692  pixSubtract(pixt, pix2, pix1);
693  pixPaintThroughMask(pixd, pixt, 0, 0, 0x00ff0000); /* green */
694  pixDestroy(&pixt);
695  return pixd;
696 }
697 
698 
718 l_ok
720  PIX *pix2,
721  l_int32 comptype,
722  l_float32 *pfract,
723  PIX **ppixdiff)
724 {
725 l_int32 w, h, count;
726 PIX *pixt;
727 
728  PROCNAME("pixCompareBinary");
729 
730  if (ppixdiff) *ppixdiff = NULL;
731  if (!pfract)
732  return ERROR_INT("&pfract not defined", procName, 1);
733  *pfract = 1.0; /* initialize to max difference */
734  if (!pix1 || pixGetDepth(pix1) != 1)
735  return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1);
736  if (!pix2 || pixGetDepth(pix2) != 1)
737  return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1);
738  if (comptype != L_COMPARE_XOR && comptype != L_COMPARE_SUBTRACT)
739  return ERROR_INT("invalid comptype", procName, 1);
740 
741  if (comptype == L_COMPARE_XOR)
742  pixt = pixXor(NULL, pix1, pix2);
743  else /* comptype == L_COMPARE_SUBTRACT) */
744  pixt = pixSubtract(NULL, pix1, pix2);
745  pixCountPixels(pixt, &count, NULL);
746  pixGetDimensions(pix1, &w, &h, NULL);
747  *pfract = (l_float32)(count) / (l_float32)(w * h);
748 
749  if (ppixdiff)
750  *ppixdiff = pixt;
751  else
752  pixDestroy(&pixt);
753  return 0;
754 }
755 
756 
798 l_ok
800  PIX *pix2,
801  l_int32 comptype,
802  l_int32 plottype,
803  l_int32 *psame,
804  l_float32 *pdiff,
805  l_float32 *prmsdiff,
806  PIX **ppixdiff)
807 {
808 l_int32 retval, d1, d2;
809 PIX *pixt1, *pixt2, *pixs1, *pixs2;
810 
811  PROCNAME("pixCompareGrayOrRGB");
812 
813  if (psame) *psame = 0;
814  if (pdiff) *pdiff = 255.0;
815  if (prmsdiff) *prmsdiff = 255.0;
816  if (ppixdiff) *ppixdiff = NULL;
817  if (!pix1 || pixGetDepth(pix1) == 1)
818  return ERROR_INT("pix1 not defined or 1 bpp", procName, 1);
819  if (!pix2 || pixGetDepth(pix2) == 1)
820  return ERROR_INT("pix2 not defined or 1 bpp", procName, 1);
821  if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF)
822  return ERROR_INT("invalid comptype", procName, 1);
823  if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS)
824  return ERROR_INT("invalid plottype", procName, 1);
825 
828  d1 = pixGetDepth(pixt1);
829  d2 = pixGetDepth(pixt2);
830  if (d1 < 8)
831  pixs1 = pixConvertTo8(pixt1, FALSE);
832  else
833  pixs1 = pixClone(pixt1);
834  if (d2 < 8)
835  pixs2 = pixConvertTo8(pixt2, FALSE);
836  else
837  pixs2 = pixClone(pixt2);
838  pixDestroy(&pixt1);
839  pixDestroy(&pixt2);
840  d1 = pixGetDepth(pixs1);
841  d2 = pixGetDepth(pixs2);
842  if (d1 != d2) {
843  pixDestroy(&pixs1);
844  pixDestroy(&pixs2);
845  return ERROR_INT("intrinsic depths are not equal", procName, 1);
846  }
847 
848  if (d1 == 8 || d1 == 16)
849  retval = pixCompareGray(pixs1, pixs2, comptype, plottype, psame,
850  pdiff, prmsdiff, ppixdiff);
851  else /* d1 == 32 */
852  retval = pixCompareRGB(pixs1, pixs2, comptype, plottype, psame,
853  pdiff, prmsdiff, ppixdiff);
854  pixDestroy(&pixs1);
855  pixDestroy(&pixs2);
856  return retval;
857 }
858 
859 
881 l_ok
883  PIX *pix2,
884  l_int32 comptype,
885  l_int32 plottype,
886  l_int32 *psame,
887  l_float32 *pdiff,
888  l_float32 *prmsdiff,
889  PIX **ppixdiff)
890 {
891 char buf[64];
892 static l_int32 index = 0;
893 l_int32 d1, d2, same, first, last;
894 GPLOT *gplot;
895 NUMA *na, *nac;
896 PIX *pixt;
897 
898  PROCNAME("pixCompareGray");
899 
900  if (psame) *psame = 0;
901  if (pdiff) *pdiff = 255.0;
902  if (prmsdiff) *prmsdiff = 255.0;
903  if (ppixdiff) *ppixdiff = NULL;
904  if (!pix1)
905  return ERROR_INT("pix1 not defined", procName, 1);
906  if (!pix2)
907  return ERROR_INT("pix2 not defined", procName, 1);
908  d1 = pixGetDepth(pix1);
909  d2 = pixGetDepth(pix2);
910  if ((d1 != d2) || (d1 != 8 && d1 != 16))
911  return ERROR_INT("depths unequal or not 8 or 16 bpp", procName, 1);
912  if (pixGetColormap(pix1) || pixGetColormap(pix2))
913  return ERROR_INT("pix1 and/or pix2 are colormapped", procName, 1);
914  if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF)
915  return ERROR_INT("invalid comptype", procName, 1);
916  if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS)
917  return ERROR_INT("invalid plottype", procName, 1);
918 
919  lept_mkdir("lept/comp");
920 
921  if (comptype == L_COMPARE_SUBTRACT)
922  pixt = pixSubtractGray(NULL, pix1, pix2);
923  else /* comptype == L_COMPARE_ABS_DIFF) */
924  pixt = pixAbsDifference(pix1, pix2);
925 
926  pixZero(pixt, &same);
927  if (same)
928  L_INFO("Images are pixel-wise identical\n", procName);
929  if (psame) *psame = same;
930 
931  if (pdiff)
932  pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_MEAN_ABSVAL, pdiff);
933 
934  /* Don't bother to plot if the images are the same */
935  if (plottype && !same) {
936  L_INFO("Images differ: output plots will be generated\n", procName);
937  na = pixGetGrayHistogram(pixt, 1);
938  numaGetNonzeroRange(na, TINY, &first, &last);
939  nac = numaClipToInterval(na, 0, last);
940  snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_gray%d", index);
941  gplot = gplotCreate(buf, plottype,
942  "Pixel Difference Histogram", "diff val",
943  "number of pixels");
944  gplotAddPlot(gplot, NULL, nac, GPLOT_LINES, "gray");
945  gplotMakeOutput(gplot);
946  gplotDestroy(&gplot);
947  snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_gray%d.png",
948  index++);
949  l_fileDisplay(buf, 100, 100, 1.0);
950  numaDestroy(&na);
951  numaDestroy(&nac);
952  }
953 
954  if (ppixdiff)
955  *ppixdiff = pixCopy(NULL, pixt);
956 
957  if (prmsdiff) {
958  if (comptype == L_COMPARE_SUBTRACT) { /* wrong type for rms diff */
959  pixDestroy(&pixt);
960  pixt = pixAbsDifference(pix1, pix2);
961  }
962  pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, prmsdiff);
963  }
964 
965  pixDestroy(&pixt);
966  return 0;
967 }
968 
969 
990 l_ok
992  PIX *pix2,
993  l_int32 comptype,
994  l_int32 plottype,
995  l_int32 *psame,
996  l_float32 *pdiff,
997  l_float32 *prmsdiff,
998  PIX **ppixdiff)
999 {
1000 char buf[64];
1001 static l_int32 index = 0;
1002 l_int32 rsame, gsame, bsame, same, first, rlast, glast, blast, last;
1003 l_float32 rdiff, gdiff, bdiff;
1004 GPLOT *gplot;
1005 NUMA *nar, *nag, *nab, *narc, *nagc, *nabc;
1006 PIX *pixr1, *pixr2, *pixg1, *pixg2, *pixb1, *pixb2;
1007 PIX *pixr, *pixg, *pixb;
1008 
1009  PROCNAME("pixCompareRGB");
1010 
1011  if (psame) *psame = 0;
1012  if (pdiff) *pdiff = 0.0;
1013  if (prmsdiff) *prmsdiff = 0.0;
1014  if (ppixdiff) *ppixdiff = NULL;
1015  if (!pix1 || pixGetDepth(pix1) != 32)
1016  return ERROR_INT("pix1 not defined or not 32 bpp", procName, 1);
1017  if (!pix2 || pixGetDepth(pix2) != 32)
1018  return ERROR_INT("pix2 not defined or not ew bpp", procName, 1);
1019  if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF)
1020  return ERROR_INT("invalid comptype", procName, 1);
1021  if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS)
1022  return ERROR_INT("invalid plottype", procName, 1);
1023 
1024  lept_mkdir("lept/comp");
1025 
1026  pixr1 = pixGetRGBComponent(pix1, COLOR_RED);
1027  pixr2 = pixGetRGBComponent(pix2, COLOR_RED);
1028  pixg1 = pixGetRGBComponent(pix1, COLOR_GREEN);
1029  pixg2 = pixGetRGBComponent(pix2, COLOR_GREEN);
1030  pixb1 = pixGetRGBComponent(pix1, COLOR_BLUE);
1031  pixb2 = pixGetRGBComponent(pix2, COLOR_BLUE);
1032  if (comptype == L_COMPARE_SUBTRACT) {
1033  pixr = pixSubtractGray(NULL, pixr1, pixr2);
1034  pixg = pixSubtractGray(NULL, pixg1, pixg2);
1035  pixb = pixSubtractGray(NULL, pixb1, pixb2);
1036  } else { /* comptype == L_COMPARE_ABS_DIFF) */
1037  pixr = pixAbsDifference(pixr1, pixr2);
1038  pixg = pixAbsDifference(pixg1, pixg2);
1039  pixb = pixAbsDifference(pixb1, pixb2);
1040  }
1041 
1042  pixZero(pixr, &rsame);
1043  pixZero(pixg, &gsame);
1044  pixZero(pixb, &bsame);
1045  same = rsame && gsame && bsame;
1046  if (same)
1047  L_INFO("Images are pixel-wise identical\n", procName);
1048  if (psame) *psame = same;
1049 
1050  if (pdiff) {
1051  pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_MEAN_ABSVAL, &rdiff);
1052  pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &gdiff);
1053  pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_MEAN_ABSVAL, &bdiff);
1054  *pdiff = (rdiff + gdiff + bdiff) / 3.0;
1055  }
1056 
1057  /* Don't bother to plot if the images are the same */
1058  if (plottype && !same) {
1059  L_INFO("Images differ: output plots will be generated\n", procName);
1060  nar = pixGetGrayHistogram(pixr, 1);
1061  nag = pixGetGrayHistogram(pixg, 1);
1062  nab = pixGetGrayHistogram(pixb, 1);
1063  numaGetNonzeroRange(nar, TINY, &first, &rlast);
1064  numaGetNonzeroRange(nag, TINY, &first, &glast);
1065  numaGetNonzeroRange(nab, TINY, &first, &blast);
1066  last = L_MAX(rlast, glast);
1067  last = L_MAX(last, blast);
1068  narc = numaClipToInterval(nar, 0, last);
1069  nagc = numaClipToInterval(nag, 0, last);
1070  nabc = numaClipToInterval(nab, 0, last);
1071  snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_rgb%d", index);
1072  gplot = gplotCreate(buf, plottype,
1073  "Pixel Difference Histogram", "diff val",
1074  "number of pixels");
1075  gplotAddPlot(gplot, NULL, narc, GPLOT_LINES, "red");
1076  gplotAddPlot(gplot, NULL, nagc, GPLOT_LINES, "green");
1077  gplotAddPlot(gplot, NULL, nabc, GPLOT_LINES, "blue");
1078  gplotMakeOutput(gplot);
1079  gplotDestroy(&gplot);
1080  snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_rgb%d.png",
1081  index++);
1082  l_fileDisplay(buf, 100, 100, 1.0);
1083  numaDestroy(&nar);
1084  numaDestroy(&nag);
1085  numaDestroy(&nab);
1086  numaDestroy(&narc);
1087  numaDestroy(&nagc);
1088  numaDestroy(&nabc);
1089  }
1090 
1091  if (ppixdiff)
1092  *ppixdiff = pixCreateRGBImage(pixr, pixg, pixb);
1093 
1094  if (prmsdiff) {
1095  if (comptype == L_COMPARE_SUBTRACT) {
1096  pixDestroy(&pixr);
1097  pixDestroy(&pixg);
1098  pixDestroy(&pixb);
1099  pixr = pixAbsDifference(pixr1, pixr2);
1100  pixg = pixAbsDifference(pixg1, pixg2);
1101  pixb = pixAbsDifference(pixb1, pixb2);
1102  }
1103  pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &rdiff);
1104  pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &gdiff);
1105  pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &bdiff);
1106  *prmsdiff = (rdiff + gdiff + bdiff) / 3.0;
1107  }
1108 
1109  pixDestroy(&pixr1);
1110  pixDestroy(&pixr2);
1111  pixDestroy(&pixg1);
1112  pixDestroy(&pixg2);
1113  pixDestroy(&pixb1);
1114  pixDestroy(&pixb2);
1115  pixDestroy(&pixr);
1116  pixDestroy(&pixg);
1117  pixDestroy(&pixb);
1118  return 0;
1119 }
1120 
1121 
1146 l_ok
1148  PIX *pix2,
1149  l_int32 sx,
1150  l_int32 sy,
1151  l_int32 type,
1152  PIX **ppixdiff)
1153 {
1154 l_int32 d1, d2, w, h;
1155 PIX *pixt, *pixr, *pixg, *pixb;
1156 PIX *pixrdiff, *pixgdiff, *pixbdiff;
1157 PIXACC *pixacc;
1158 
1159  PROCNAME("pixCompareTiled");
1160 
1161  if (!ppixdiff)
1162  return ERROR_INT("&pixdiff not defined", procName, 1);
1163  *ppixdiff = NULL;
1164  if (!pix1)
1165  return ERROR_INT("pix1 not defined", procName, 1);
1166  if (!pix2)
1167  return ERROR_INT("pix2 not defined", procName, 1);
1168  d1 = pixGetDepth(pix1);
1169  d2 = pixGetDepth(pix2);
1170  if (d1 != d2)
1171  return ERROR_INT("depths not equal", procName, 1);
1172  if (d1 != 8 && d1 != 32)
1173  return ERROR_INT("pix1 not 8 or 32 bpp", procName, 1);
1174  if (d2 != 8 && d2 != 32)
1175  return ERROR_INT("pix2 not 8 or 32 bpp", procName, 1);
1176  if (sx < 2 || sy < 2)
1177  return ERROR_INT("sx and sy not both > 1", procName, 1);
1178  if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE)
1179  return ERROR_INT("invalid type", procName, 1);
1180 
1181  pixt = pixAbsDifference(pix1, pix2);
1182  if (d1 == 8) {
1183  *ppixdiff = pixGetAverageTiled(pixt, sx, sy, type);
1184  } else { /* d1 == 32 */
1185  pixr = pixGetRGBComponent(pixt, COLOR_RED);
1186  pixg = pixGetRGBComponent(pixt, COLOR_GREEN);
1187  pixb = pixGetRGBComponent(pixt, COLOR_BLUE);
1188  pixrdiff = pixGetAverageTiled(pixr, sx, sy, type);
1189  pixgdiff = pixGetAverageTiled(pixg, sx, sy, type);
1190  pixbdiff = pixGetAverageTiled(pixb, sx, sy, type);
1191  pixGetDimensions(pixrdiff, &w, &h, NULL);
1192  pixacc = pixaccCreate(w, h, 0);
1193  pixaccAdd(pixacc, pixrdiff);
1194  pixaccAdd(pixacc, pixgdiff);
1195  pixaccAdd(pixacc, pixbdiff);
1196  pixaccMultConst(pixacc, 1. / 3.);
1197  *ppixdiff = pixaccFinal(pixacc, 8);
1198  pixDestroy(&pixr);
1199  pixDestroy(&pixg);
1200  pixDestroy(&pixb);
1201  pixDestroy(&pixrdiff);
1202  pixDestroy(&pixgdiff);
1203  pixDestroy(&pixbdiff);
1204  pixaccDestroy(&pixacc);
1205  }
1206  pixDestroy(&pixt);
1207  return 0;
1208 }
1209 
1210 
1211 /*------------------------------------------------------------------*
1212  * Other measures of the difference of two images *
1213  *------------------------------------------------------------------*/
1240 NUMA *
1242  PIX *pix2,
1243  l_int32 factor)
1244 {
1245 l_int32 i;
1246 l_float32 *array1, *array2;
1247 NUMA *nah, *nan, *nad;
1248 
1249  PROCNAME("pixCompareRankDifference");
1250 
1251  if (!pix1)
1252  return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL);
1253  if (!pix2)
1254  return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL);
1255 
1256  if ((nah = pixGetDifferenceHistogram(pix1, pix2, factor)) == NULL)
1257  return (NUMA *)ERROR_PTR("na not made", procName, NULL);
1258 
1259  nan = numaNormalizeHistogram(nah, 1.0);
1260  array1 = numaGetFArray(nan, L_NOCOPY);
1261 
1262  nad = numaCreate(256);
1263  numaSetCount(nad, 256); /* all initialized to 0.0 */
1264  array2 = numaGetFArray(nad, L_NOCOPY);
1265 
1266  /* Do rank accumulation on normalized histo of diffs */
1267  array2[0] = 1.0;
1268  for (i = 1; i < 256; i++)
1269  array2[i] = array2[i - 1] - array1[i - 1];
1270 
1271  numaDestroy(&nah);
1272  numaDestroy(&nan);
1273  return nad;
1274 }
1275 
1276 
1325 l_ok
1327  PIX *pix2,
1328  l_int32 factor,
1329  l_int32 mindiff,
1330  l_float32 maxfract,
1331  l_float32 maxave,
1332  l_int32 *psimilar,
1333  l_int32 details)
1334 {
1335 l_float32 fractdiff, avediff;
1336 
1337  PROCNAME("pixTestForSimilarity");
1338 
1339  if (!psimilar)
1340  return ERROR_INT("&similar not defined", procName, 1);
1341  *psimilar = 0;
1342  if (!pix1)
1343  return ERROR_INT("pix1 not defined", procName, 1);
1344  if (!pix2)
1345  return ERROR_INT("pix2 not defined", procName, 1);
1346  if (pixSizesEqual(pix1, pix2) == 0)
1347  return ERROR_INT("pix sizes not equal", procName, 1);
1348  if (mindiff <= 0)
1349  return ERROR_INT("mindiff must be > 0", procName, 1);
1350 
1351  if (pixGetDifferenceStats(pix1, pix2, factor, mindiff,
1352  &fractdiff, &avediff, details))
1353  return ERROR_INT("diff stats not found", procName, 1);
1354 
1355  if (maxave <= 0.0) maxave = 256.0;
1356  if (fractdiff <= maxfract && avediff <= maxave)
1357  *psimilar = 1;
1358  return 0;
1359 }
1360 
1361 
1404 l_ok
1406  PIX *pix2,
1407  l_int32 factor,
1408  l_int32 mindiff,
1409  l_float32 *pfractdiff,
1410  l_float32 *pavediff,
1411  l_int32 details)
1412 {
1413 l_int32 i, first, last, diff;
1414 l_float32 fract, ave;
1415 l_float32 *array;
1416 NUMA *nah, *nan, *nac;
1417 
1418  PROCNAME("pixGetDifferenceStats");
1419 
1420  if (pfractdiff) *pfractdiff = 0.0;
1421  if (pavediff) *pavediff = 0.0;
1422  if (!pfractdiff)
1423  return ERROR_INT("&fractdiff not defined", procName, 1);
1424  if (!pavediff)
1425  return ERROR_INT("&avediff not defined", procName, 1);
1426  if (!pix1)
1427  return ERROR_INT("pix1 not defined", procName, 1);
1428  if (!pix2)
1429  return ERROR_INT("pix2 not defined", procName, 1);
1430  if (mindiff <= 0)
1431  return ERROR_INT("mindiff must be > 0", procName, 1);
1432 
1433  if ((nah = pixGetDifferenceHistogram(pix1, pix2, factor)) == NULL)
1434  return ERROR_INT("na not made", procName, 1);
1435 
1436  if ((nan = numaNormalizeHistogram(nah, 1.0)) == NULL) {
1437  numaDestroy(&nah);
1438  return ERROR_INT("nan not made", procName, 1);
1439  }
1440  array = numaGetFArray(nan, L_NOCOPY);
1441 
1442  if (details) {
1443  lept_mkdir("lept/comp");
1444  numaGetNonzeroRange(nan, 0.0, &first, &last);
1445  nac = numaClipToInterval(nan, first, last);
1446  gplotSimple1(nac, GPLOT_PNG, "/tmp/lept/comp/histo",
1447  "Difference histogram");
1448  l_fileDisplay("/tmp/lept/comp/histo.png", 500, 0, 1.0);
1449  lept_stderr("\nNonzero values in normalized histogram:");
1450  numaWriteStderr(nac);
1451  numaDestroy(&nac);
1452  lept_stderr(" Mindiff fractdiff avediff\n");
1453  lept_stderr(" -----------------------------------\n");
1454  for (diff = 1; diff < L_MIN(2 * mindiff, last); diff++) {
1455  fract = 0.0;
1456  ave = 0.0;
1457  for (i = diff; i <= last; i++) {
1458  fract += array[i];
1459  ave += (l_float32)i * array[i];
1460  }
1461  ave = (fract == 0.0) ? 0.0 : ave / fract;
1462  ave -= diff;
1463  lept_stderr("%5d %7.4f %7.4f\n",
1464  diff, fract, ave);
1465  }
1466  lept_stderr(" -----------------------------------\n");
1467  }
1468 
1469  fract = 0.0;
1470  ave = 0.0;
1471  for (i = mindiff; i < 256; i++) {
1472  fract += array[i];
1473  ave += (l_float32)i * array[i];
1474  }
1475  ave = (fract == 0.0) ? 0.0 : ave / fract;
1476  ave -= mindiff;
1477 
1478  *pfractdiff = fract;
1479  *pavediff = ave;
1480 
1481  numaDestroy(&nah);
1482  numaDestroy(&nan);
1483  return 0;
1484 }
1485 
1486 
1506 NUMA *
1508  PIX *pix2,
1509  l_int32 factor)
1510 {
1511 l_int32 w1, h1, d1, w2, h2, d2, w, h, wpl1, wpl2;
1512 l_int32 i, j, val, val1, val2;
1513 l_int32 rval1, rval2, gval1, gval2, bval1, bval2;
1514 l_int32 rdiff, gdiff, bdiff, maxdiff;
1515 l_uint32 *data1, *data2, *line1, *line2;
1516 l_float32 *array;
1517 NUMA *na;
1518 PIX *pixt1, *pixt2;
1519 
1520  PROCNAME("pixGetDifferenceHistogram");
1521 
1522  if (!pix1)
1523  return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL);
1524  if (!pix2)
1525  return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL);
1526  d1 = pixGetDepth(pix1);
1527  d2 = pixGetDepth(pix2);
1528  if (d1 == 16 || d2 == 16)
1529  return (NUMA *)ERROR_PTR("d == 16 not supported", procName, NULL);
1530  if (d1 < 8 && !pixGetColormap(pix1))
1531  return (NUMA *)ERROR_PTR("pix1 depth < 8 bpp and not cmapped",
1532  procName, NULL);
1533  if (d2 < 8 && !pixGetColormap(pix2))
1534  return (NUMA *)ERROR_PTR("pix2 depth < 8 bpp and not cmapped",
1535  procName, NULL);
1538  pixGetDimensions(pixt1, &w1, &h1, &d1);
1539  pixGetDimensions(pixt2, &w2, &h2, &d2);
1540  if (d1 != d2) {
1541  pixDestroy(&pixt1);
1542  pixDestroy(&pixt2);
1543  return (NUMA *)ERROR_PTR("pix depths not equal", procName, NULL);
1544  }
1545  if (factor < 1) factor = 1;
1546 
1547  na = numaCreate(256);
1548  numaSetCount(na, 256); /* all initialized to 0.0 */
1549  array = numaGetFArray(na, L_NOCOPY);
1550  w = L_MIN(w1, w2);
1551  h = L_MIN(h1, h2);
1552  data1 = pixGetData(pixt1);
1553  data2 = pixGetData(pixt2);
1554  wpl1 = pixGetWpl(pixt1);
1555  wpl2 = pixGetWpl(pixt2);
1556  if (d1 == 8) {
1557  for (i = 0; i < h; i += factor) {
1558  line1 = data1 + i * wpl1;
1559  line2 = data2 + i * wpl2;
1560  for (j = 0; j < w; j += factor) {
1561  val1 = GET_DATA_BYTE(line1, j);
1562  val2 = GET_DATA_BYTE(line2, j);
1563  val = L_ABS(val1 - val2);
1564  array[val]++;
1565  }
1566  }
1567  } else { /* d1 == 32 */
1568  for (i = 0; i < h; i += factor) {
1569  line1 = data1 + i * wpl1;
1570  line2 = data2 + i * wpl2;
1571  for (j = 0; j < w; j += factor) {
1572  extractRGBValues(line1[j], &rval1, &gval1, &bval1);
1573  extractRGBValues(line2[j], &rval2, &gval2, &bval2);
1574  rdiff = L_ABS(rval1 - rval2);
1575  gdiff = L_ABS(gval1 - gval2);
1576  bdiff = L_ABS(bval1 - bval2);
1577  maxdiff = L_MAX(rdiff, gdiff);
1578  maxdiff = L_MAX(maxdiff, bdiff);
1579  array[maxdiff]++;
1580  }
1581  }
1582  }
1583 
1584  pixDestroy(&pixt1);
1585  pixDestroy(&pixt2);
1586  return na;
1587 }
1588 
1589 
1637 l_ok
1639  PIX *pixs2,
1640  l_int32 sampling,
1641  l_int32 dilation,
1642  l_int32 mindiff,
1643  l_float32 *pfract,
1644  PIX **ppixdiff1,
1645  PIX **ppixdiff2)
1646 {
1647 l_int32 d1, d2, w, h, count;
1648 PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9;
1649 PIX *pix10, *pix11;
1650 
1651  PROCNAME("pixGetPerceptualDiff");
1652 
1653  if (ppixdiff1) *ppixdiff1 = NULL;
1654  if (ppixdiff2) *ppixdiff2 = NULL;
1655  if (!pfract)
1656  return ERROR_INT("&fract not defined", procName, 1);
1657  *pfract = 1.0; /* init to completely different */
1658  if ((dilation & 1) == 0)
1659  return ERROR_INT("dilation must be odd", procName, 1);
1660  if (!pixs1)
1661  return ERROR_INT("pixs1 not defined", procName, 1);
1662  if (!pixs2)
1663  return ERROR_INT("pixs2 not defined", procName, 1);
1664  d1 = pixGetDepth(pixs1);
1665  d2 = pixGetDepth(pixs2);
1666  if (!pixGetColormap(pixs1) && d1 < 8)
1667  return ERROR_INT("pixs1 not cmapped and < 8 bpp", procName, 1);
1668  if (!pixGetColormap(pixs2) && d2 < 8)
1669  return ERROR_INT("pixs2 not cmapped and < 8 bpp", procName, 1);
1670 
1671  /* Integer downsample if requested */
1672  if (sampling > 1) {
1673  pix1 = pixScaleByIntSampling(pixs1, sampling);
1674  pix2 = pixScaleByIntSampling(pixs2, sampling);
1675  } else {
1676  pix1 = pixClone(pixs1);
1677  pix2 = pixClone(pixs2);
1678  }
1679 
1680  /* Remove colormaps */
1681  if (pixGetColormap(pix1)) {
1683  d1 = pixGetDepth(pix3);
1684  } else {
1685  pix3 = pixClone(pix1);
1686  }
1687  if (pixGetColormap(pix2)) {
1689  d2 = pixGetDepth(pix4);
1690  } else {
1691  pix4 = pixClone(pix2);
1692  }
1693  pixDestroy(&pix1);
1694  pixDestroy(&pix2);
1695  if (d1 != d2 || (d1 != 8 && d1 != 32)) {
1696  pixDestroy(&pix3);
1697  pixDestroy(&pix4);
1698  L_INFO("depths unequal or not in {8,32}: d1 = %d, d2 = %d\n",
1699  procName, d1, d2);
1700  return 1;
1701  }
1702 
1703  /* In each direction, do a small dilation and subtract the dilated
1704  * image from the other image to get a one-sided difference.
1705  * Then take the max of the differences for each direction
1706  * and clipping each component to 255 if necessary. Note that
1707  * for RGB images, the dilations and max selection are done
1708  * component-wise, and the conversion to grayscale also uses the
1709  * maximum component. The resulting grayscale images are
1710  * thresholded using %mindiff. */
1711  if (d1 == 8) {
1712  pix5 = pixDilateGray(pix3, dilation, dilation);
1713  pixCompareGray(pix4, pix5, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL,
1714  &pix7);
1715  pix6 = pixDilateGray(pix4, dilation, dilation);
1716  pixCompareGray(pix3, pix6, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL,
1717  &pix8);
1718  pix9 = pixMinOrMax(NULL, pix7, pix8, L_CHOOSE_MAX);
1719  pix10 = pixThresholdToBinary(pix9, mindiff);
1720  pixInvert(pix10, pix10);
1721  pixCountPixels(pix10, &count, NULL);
1722  pixGetDimensions(pix10, &w, &h, NULL);
1723  *pfract = (w <= 0 || h <= 0) ? 0.0 :
1724  (l_float32)count / (l_float32)(w * h);
1725  pixDestroy(&pix5);
1726  pixDestroy(&pix6);
1727  pixDestroy(&pix7);
1728  pixDestroy(&pix8);
1729  if (ppixdiff1)
1730  *ppixdiff1 = pix9;
1731  else
1732  pixDestroy(&pix9);
1733  if (ppixdiff2)
1734  *ppixdiff2 = pix10;
1735  else
1736  pixDestroy(&pix10);
1737  } else { /* d1 == 32 */
1738  pix5 = pixColorMorph(pix3, L_MORPH_DILATE, dilation, dilation);
1739  pixCompareRGB(pix4, pix5, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL,
1740  &pix7);
1741  pix6 = pixColorMorph(pix4, L_MORPH_DILATE, dilation, dilation);
1742  pixCompareRGB(pix3, pix6, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL,
1743  &pix8);
1744  pix9 = pixMinOrMax(NULL, pix7, pix8, L_CHOOSE_MAX);
1745  pix10 = pixConvertRGBToGrayMinMax(pix9, L_CHOOSE_MAX);
1746  pix11 = pixThresholdToBinary(pix10, mindiff);
1747  pixInvert(pix11, pix11);
1748  pixCountPixels(pix11, &count, NULL);
1749  pixGetDimensions(pix11, &w, &h, NULL);
1750  *pfract = (w <= 0 || h <= 0) ? 0.0 :
1751  (l_float32)count / (l_float32)(w * h);
1752  pixDestroy(&pix5);
1753  pixDestroy(&pix6);
1754  pixDestroy(&pix7);
1755  pixDestroy(&pix8);
1756  pixDestroy(&pix10);
1757  if (ppixdiff1)
1758  *ppixdiff1 = pix9;
1759  else
1760  pixDestroy(&pix9);
1761  if (ppixdiff2)
1762  *ppixdiff2 = pix11;
1763  else
1764  pixDestroy(&pix11);
1765 
1766  }
1767  pixDestroy(&pix3);
1768  pixDestroy(&pix4);
1769  return 0;
1770 }
1771 
1772 
1804 l_ok
1806  PIX *pix2,
1807  l_int32 factor,
1808  l_float32 *ppsnr)
1809 {
1810 l_int32 same, i, j, w, h, d, wpl1, wpl2, v1, v2, r1, g1, b1, r2, g2, b2;
1811 l_uint32 *data1, *data2, *line1, *line2;
1812 l_float32 mse; /* mean squared error */
1813 
1814  PROCNAME("pixGetPSNR");
1815 
1816  if (!ppsnr)
1817  return ERROR_INT("&psnr not defined", procName, 1);
1818  *ppsnr = 0.0;
1819  if (!pix1 || !pix2)
1820  return ERROR_INT("empty input pix", procName, 1);
1821  if (!pixSizesEqual(pix1, pix2))
1822  return ERROR_INT("pix sizes unequal", procName, 1);
1823  if (pixGetColormap(pix1))
1824  return ERROR_INT("pix1 has colormap", procName, 1);
1825  if (pixGetColormap(pix2))
1826  return ERROR_INT("pix2 has colormap", procName, 1);
1827  pixGetDimensions(pix1, &w, &h, &d);
1828  if (d != 8 && d != 32)
1829  return ERROR_INT("pix not 8 or 32 bpp", procName, 1);
1830  if (factor < 1)
1831  return ERROR_INT("invalid sampling factor", procName, 1);
1832 
1833  pixEqual(pix1, pix2, &same);
1834  if (same) {
1835  *ppsnr = 1000.0; /* crazy big exponent */
1836  return 0;
1837  }
1838 
1839  data1 = pixGetData(pix1);
1840  data2 = pixGetData(pix2);
1841  wpl1 = pixGetWpl(pix1);
1842  wpl2 = pixGetWpl(pix2);
1843  mse = 0.0;
1844  if (d == 8) {
1845  for (i = 0; i < h; i += factor) {
1846  line1 = data1 + i * wpl1;
1847  line2 = data2 + i * wpl2;
1848  for (j = 0; j < w; j += factor) {
1849  v1 = GET_DATA_BYTE(line1, j);
1850  v2 = GET_DATA_BYTE(line2, j);
1851  mse += (l_float32)(v1 - v2) * (v1 - v2);
1852  }
1853  }
1854  } else { /* d == 32 */
1855  for (i = 0; i < h; i += factor) {
1856  line1 = data1 + i * wpl1;
1857  line2 = data2 + i * wpl2;
1858  for (j = 0; j < w; j += factor) {
1859  extractRGBValues(line1[j], &r1, &g1, &b1);
1860  extractRGBValues(line2[j], &r2, &g2, &b2);
1861  mse += ((l_float32)(r1 - r2) * (r1 - r2) +
1862  (g1 - g2) * (g1 - g2) +
1863  (b1 - b2) * (b1 - b2)) / 3.0;
1864  }
1865  }
1866  }
1867  mse = mse / ((l_float32)(w) * h);
1868 
1869  *ppsnr = -4.3429448 * log(mse / (255 * 255));
1870  return 0;
1871 }
1872 
1873 
1874 /*------------------------------------------------------------------*
1875  * Comparison of photo regions by histogram *
1876  *------------------------------------------------------------------*/
1929 l_ok
1931  l_float32 minratio,
1932  l_float32 textthresh,
1933  l_int32 factor,
1934  l_int32 n,
1935  l_float32 simthresh,
1936  NUMA **pnai,
1937  l_float32 **pscores,
1938  PIX **ppixd,
1939  l_int32 debug)
1940 {
1941 char *text;
1942 l_int32 i, j, nim, w, h, w1, h1, w2, h2, ival, index, classid;
1943 l_float32 score;
1944 l_float32 *scores;
1945 NUMA *nai, *naw, *nah;
1946 NUMAA *naa;
1947 NUMAA **n3a; /* array of naa */
1948 PIX *pix;
1949 
1950  PROCNAME("pixaComparePhotoRegionsByHisto");
1951 
1952  if (pscores) *pscores = NULL;
1953  if (ppixd) *ppixd = NULL;
1954  if (!pnai)
1955  return ERROR_INT("&na not defined", procName, 1);
1956  *pnai = NULL;
1957  if (!pixa)
1958  return ERROR_INT("pixa not defined", procName, 1);
1959  if (minratio < 0.0 || minratio > 1.0)
1960  return ERROR_INT("minratio not in [0.0 ... 1.0]", procName, 1);
1961  if (textthresh <= 0.0) textthresh = 1.3;
1962  if (factor < 1)
1963  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
1964  if (n < 1 || n > 7) {
1965  L_WARNING("n = %d is invalid; setting to 4\n", procName, n);
1966  n = 4;
1967  }
1968  if (simthresh <= 0.0) simthresh = 0.25;
1969  if (simthresh > 1.0)
1970  return ERROR_INT("simthresh invalid; should be near 0.25", procName, 1);
1971 
1972  /* Prepare the histograms */
1973  nim = pixaGetCount(pixa);
1974  if ((n3a = (NUMAA **)LEPT_CALLOC(nim, sizeof(NUMAA *))) == NULL)
1975  return ERROR_INT("calloc fail for n3a", procName, 1);
1976  naw = numaCreate(0);
1977  nah = numaCreate(0);
1978  for (i = 0; i < nim; i++) {
1979  pix = pixaGetPix(pixa, i, L_CLONE);
1980  text = pixGetText(pix);
1981  pixSetResolution(pix, 150, 150);
1982  index = (debug) ? i : 0;
1983  pixGenPhotoHistos(pix, NULL, factor, textthresh, n,
1984  &naa, &w, &h, index);
1985  n3a[i] = naa;
1986  numaAddNumber(naw, w);
1987  numaAddNumber(nah, h);
1988  if (naa)
1989  lept_stderr("Image %s is photo\n", text);
1990  else
1991  lept_stderr("Image %s is NOT photo\n", text);
1992  pixDestroy(&pix);
1993  }
1994 
1995  /* Do the comparisons. We are making a set of classes, where
1996  * all similar images are placed in the same class. There are
1997  * 'nim' input images. The classes are labeled by 'classid' (all
1998  * similar images get the same 'classid' value), and 'nai' maps
1999  * the classid of the image in the input array to the classid
2000  * of the similarity class. */
2001  if ((scores =
2002  (l_float32 *)LEPT_CALLOC((size_t)nim * nim, sizeof(l_float32)))
2003  == NULL) {
2004  L_ERROR("calloc fail for scores\n", procName);
2005  goto cleanup;
2006  }
2007  nai = numaMakeConstant(-1, nim); /* classid array */
2008  for (i = 0, classid = 0; i < nim; i++) {
2009  scores[nim * i + i] = 1.0;
2010  numaGetIValue(nai, i, &ival);
2011  if (ival != -1) /* already set */
2012  continue;
2013  numaSetValue(nai, i, classid);
2014  if (n3a[i] == NULL) { /* not a photo */
2015  classid++;
2016  continue;
2017  }
2018  numaGetIValue(naw, i, &w1);
2019  numaGetIValue(nah, i, &h1);
2020  for (j = i + 1; j < nim; j++) {
2021  numaGetIValue(nai, j, &ival);
2022  if (ival != -1) /* already set */
2023  continue;
2024  if (n3a[j] == NULL) /* not a photo */
2025  continue;
2026  numaGetIValue(naw, j, &w2);
2027  numaGetIValue(nah, j, &h2);
2028  compareTilesByHisto(n3a[i], n3a[j], minratio, w1, h1, w2, h2,
2029  &score, NULL);
2030  scores[nim * i + j] = score;
2031  scores[nim * j + i] = score; /* the score array is symmetric */
2032 /* lept_stderr("score = %5.3f\n", score); */
2033  if (score > simthresh) {
2034  numaSetValue(nai, j, classid);
2035  lept_stderr(
2036  "Setting %d similar to %d, in class %d; score %5.3f\n",
2037  j, i, classid, score);
2038  }
2039  }
2040  classid++;
2041  }
2042  *pnai = nai;
2043 
2044  /* Debug: optionally save and display the score array.
2045  * All images that are photos are represented by a point on
2046  * the diagonal. Other images in the same similarity class
2047  * are on the same horizontal raster line to the right.
2048  * The array has been symmetrized, so images in the same
2049  * same similarity class also appear on the same column below. */
2050  if (pscores) {
2051  l_int32 wpl, fact;
2052  l_uint32 *line, *data;
2053  PIX *pix2, *pix3;
2054  pix2 = pixCreate(nim, nim, 8);
2055  data = pixGetData(pix2);
2056  wpl = pixGetWpl(pix2);
2057  for (i = 0; i < nim; i++) {
2058  line = data + i * wpl;
2059  for (j = 0; j < nim; j++) {
2060  SET_DATA_BYTE(line, j,
2061  L_MIN(255, 4.0 * 255 * scores[nim * i + j]));
2062  }
2063  }
2064  fact = L_MAX(2, 1000 / nim);
2065  pix3 = pixExpandReplicate(pix2, fact);
2066  lept_stderr("Writing to /tmp/lept/comp/scorearray.png\n");
2067  lept_mkdir("lept/comp");
2068  pixWrite("/tmp/lept/comp/scorearray.png", pix3, IFF_PNG);
2069  pixDestroy(&pix2);
2070  pixDestroy(&pix3);
2071  *pscores = scores;
2072  } else {
2073  LEPT_FREE(scores);
2074  }
2075 
2076  /* Debug: optionally display and save the image comparisons.
2077  * Image similarity classes are displayed by column; similar
2078  * images are displayed in the same column. */
2079  if (ppixd)
2080  *ppixd = pixaDisplayTiledByIndex(pixa, nai, 200, 20, 2, 6, 0x0000ff00);
2081 
2082 cleanup:
2083  numaDestroy(&naw);
2084  numaDestroy(&nah);
2085  for (i = 0; i < nim; i++)
2086  numaaDestroy(&n3a[i]);
2087  LEPT_FREE(n3a);
2088  return 0;
2089 }
2090 
2091 
2151 l_ok
2153  PIX *pix2,
2154  BOX *box1,
2155  BOX *box2,
2156  l_float32 minratio,
2157  l_int32 factor,
2158  l_int32 n,
2159  l_float32 *pscore,
2160  l_int32 debugflag)
2161 {
2162 l_int32 w1, h1, w2, h2, w1c, h1c, w2c, h2c, debugindex;
2163 l_float32 wratio, hratio;
2164 NUMAA *naa1, *naa2;
2165 PIX *pix3, *pix4;
2166 PIXA *pixa;
2167 
2168  PROCNAME("pixComparePhotoRegionsByHisto");
2169 
2170  if (!pscore)
2171  return ERROR_INT("&score not defined", procName, 1);
2172  *pscore = 0.0;
2173  if (!pix1 || !pix2)
2174  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
2175  if (minratio < 0.5 || minratio > 1.0)
2176  return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1);
2177  if (factor < 1)
2178  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2179  if (n < 1 || n > 7) {
2180  L_WARNING("n = %d is invalid; setting to 4\n", procName, n);
2181  n = 4;
2182  }
2183 
2184  debugindex = 0;
2185  if (debugflag) {
2186  lept_mkdir("lept/comp");
2187  debugindex = 666; /* arbitrary number used for naming output */
2188  }
2189 
2190  /* Initial filter by size */
2191  if (box1)
2192  boxGetGeometry(box1, NULL, NULL, &w1, &h1);
2193  else
2194  pixGetDimensions(pix1, &w1, &h1, NULL);
2195  if (box2)
2196  boxGetGeometry(box2, NULL, NULL, &w2, &h2);
2197  else
2198  pixGetDimensions(pix1, &w2, &h2, NULL);
2199  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2200  (l_float32)w2 / (l_float32)w1;
2201  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2202  (l_float32)h2 / (l_float32)h1;
2203  if (wratio < minratio || hratio < minratio)
2204  return 0;
2205 
2206  /* Initial crop, if necessary, and make histos */
2207  if (box1)
2208  pix3 = pixClipRectangle(pix1, box1, NULL);
2209  else
2210  pix3 = pixClone(pix1);
2211  pixGenPhotoHistos(pix3, NULL, factor, 0, n, &naa1, &w1c, &h1c, debugindex);
2212  pixDestroy(&pix3);
2213  if (!naa1) return 0;
2214  if (box2)
2215  pix4 = pixClipRectangle(pix2, box2, NULL);
2216  else
2217  pix4 = pixClone(pix2);
2218  pixGenPhotoHistos(pix4, NULL, factor, 0, n, &naa2, &w2c, &h2c, debugindex);
2219  pixDestroy(&pix4);
2220  if (!naa2) return 0;
2221 
2222  /* Compare histograms */
2223  pixa = (debugflag) ? pixaCreate(0) : NULL;
2224  compareTilesByHisto(naa1, naa2, minratio, w1c, h1c, w2c, h2c, pscore, pixa);
2225  pixaDestroy(&pixa);
2226  return 0;
2227 }
2228 
2229 
2268 l_ok
2270  BOX *box,
2271  l_int32 factor,
2272  l_float32 thresh,
2273  l_int32 n,
2274  NUMAA **pnaa,
2275  l_int32 *pw,
2276  l_int32 *ph,
2277  l_int32 debugindex)
2278 {
2279 char buf[64];
2280 NUMAA *naa;
2281 PIX *pix1, *pix2, *pix3, *pixm;
2282 PIXA *pixa;
2283 
2284  PROCNAME("pixGenPhotoHistos");
2285 
2286  if (pnaa) *pnaa = NULL;
2287  if (pw) *pw = 0;
2288  if (ph) *ph = 0;
2289  if (!pnaa)
2290  return ERROR_INT("&naa not defined", procName, 1);
2291  if (!pw || !ph)
2292  return ERROR_INT("&w and &h not both defined", procName, 1);
2293  if (!pixs || pixGetDepth(pixs) == 1)
2294  return ERROR_INT("pixs not defined or 1 bpp", procName, 1);
2295  if (factor < 1)
2296  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2297  if (thresh <= 0.0) thresh = 1.3; /* default */
2298  if (n < 1 || n > 7) {
2299  L_WARNING("n = %d is invalid; setting to 4\n", procName, n);
2300  n = 4;
2301  }
2302 
2303  pixa = NULL;
2304  if (debugindex > 0) {
2305  pixa = pixaCreate(0);
2306  lept_mkdir("lept/comp");
2307  }
2308 
2309  /* Initial crop, if necessary */
2310  if (box)
2311  pix1 = pixClipRectangle(pixs, box, NULL);
2312  else
2313  pix1 = pixClone(pixs);
2314 
2315  /* Convert to 8 bpp and pad to center the centroid */
2316  pix2 = pixConvertTo8(pix1, FALSE);
2317  pix3 = pixPadToCenterCentroid(pix2, factor);
2318 
2319  /* Set to 255 all pixels above 230. Do this so that light gray
2320  * pixels do not enter into the comparison. */
2321  pixm = pixThresholdToBinary(pix3, 230);
2322  pixInvert(pixm, pixm);
2323  pixSetMaskedGeneral(pix3, pixm, 255, 0, 0);
2324  pixDestroy(&pixm);
2325 
2326  if (debugindex > 0) {
2327  PIX *pix4, *pix5, *pix6, *pix7, *pix8;
2328  PIXA *pixa2;
2329  pix4 = pixConvertTo32(pix2);
2330  pix5 = pixConvertTo32(pix3);
2331  pix6 = pixScaleToSize(pix4, 400, 0);
2332  pix7 = pixScaleToSize(pix5, 400, 0);
2333  pixa2 = pixaCreate(2);
2334  pixaAddPix(pixa2, pix6, L_INSERT);
2335  pixaAddPix(pixa2, pix7, L_INSERT);
2336  pix8 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 3);
2337  pixaAddPix(pixa, pix8, L_INSERT);
2338  pixDestroy(&pix4);
2339  pixDestroy(&pix5);
2340  pixaDestroy(&pixa2);
2341  }
2342  pixDestroy(&pix1);
2343  pixDestroy(&pix2);
2344 
2345  /* Test if this is a photoimage */
2346  pixDecideIfPhotoImage(pix3, factor, thresh, n, &naa, pixa);
2347  if (naa) {
2348  *pnaa = naa;
2349  *pw = pixGetWidth(pix3);
2350  *ph = pixGetHeight(pix3);
2351  }
2352 
2353  if (pixa) {
2354  snprintf(buf, sizeof(buf), "/tmp/lept/comp/tiledhistos.%d.pdf",
2355  debugindex);
2356  lept_stderr("Writing to %s\n", buf);
2357  pixaConvertToPdf(pixa, 300, 1.0, L_FLATE_ENCODE, 0, NULL, buf);
2358  pixaDestroy(&pixa);
2359  }
2360 
2361  pixDestroy(&pix3);
2362  return 0;
2363 }
2364 
2365 
2381 PIX *
2383  l_int32 factor)
2384 
2385 {
2386 l_float32 cx, cy;
2387 l_int32 xs, ys, delx, dely, icx, icy, ws, hs, wd, hd;
2388 PIX *pix1, *pixd;
2389 
2390  PROCNAME("pixPadToCenterCentroid");
2391 
2392  if (!pixs)
2393  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2394  if (factor < 1)
2395  return (PIX *)ERROR_PTR("invalid sampling factor", procName, NULL);
2396 
2397  pix1 = pixConvertTo8(pixs, FALSE);
2398  pixCentroid8(pix1, factor, &cx, &cy);
2399  icx = (l_int32)(cx + 0.5);
2400  icy = (l_int32)(cy + 0.5);
2401  pixGetDimensions(pix1, &ws, &hs, NULL);
2402  delx = ws - 2 * icx;
2403  dely = hs - 2 * icy;
2404  xs = L_MAX(0, delx);
2405  ys = L_MAX(0, dely);
2406  wd = 2 * L_MAX(icx, ws - icx);
2407  hd = 2 * L_MAX(icy, hs - icy);
2408  pixd = pixCreate(wd, hd, 8);
2409  pixSetAll(pixd); /* to white */
2410  pixCopyResolution(pixd, pixs);
2411  pixRasterop(pixd, xs, ys, ws, hs, PIX_SRC, pix1, 0, 0);
2412  pixDestroy(&pix1);
2413  return pixd;
2414 }
2415 
2416 
2435 l_ok
2437  l_int32 factor,
2438  l_float32 *pcx,
2439  l_float32 *pcy)
2440 {
2441 l_int32 i, j, w, h, wpl, val;
2442 l_float32 sumx, sumy, sumv;
2443 l_uint32 *data, *line;
2444 PIX *pix1;
2445 
2446  PROCNAME("pixCentroid8");
2447 
2448  if (pcx) *pcx = 0.0;
2449  if (pcy) *pcy = 0.0;
2450  if (!pixs || pixGetDepth(pixs) != 8)
2451  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
2452  if (factor < 1)
2453  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2454  if (!pcx || !pcy)
2455  return ERROR_INT("&cx and &cy not both defined", procName, 1);
2456 
2457  pix1 = pixInvert(NULL, pixs);
2458  pixGetDimensions(pix1, &w, &h, NULL);
2459  data = pixGetData(pix1);
2460  wpl = pixGetWpl(pix1);
2461  sumx = sumy = sumv = 0.0;
2462  for (i = 0; i < h; i++) {
2463  line = data + i * wpl;
2464  for (j = 0; j < w; j++) {
2465  val = GET_DATA_BYTE(line, j);
2466  sumx += val * j;
2467  sumy += val * i;
2468  sumv += val;
2469  }
2470  }
2471  pixDestroy(&pix1);
2472 
2473  if (sumv == 0) {
2474  L_INFO("input image is white\n", procName);
2475  *pcx = (l_float32)(w) / 2;
2476  *pcy = (l_float32)(h) / 2;
2477  } else {
2478  *pcx = sumx / sumv;
2479  *pcy = sumy / sumv;
2480  }
2481 
2482  return 0;
2483 }
2484 
2485 
2521 l_ok
2523  l_int32 factor,
2524  l_float32 thresh,
2525  l_int32 n,
2526  NUMAA **pnaa,
2527  PIXA *pixadebug)
2528 {
2529 char buf[64];
2530 l_int32 i, w, h, nx, ny, ngrids, istext, isphoto;
2531 l_float32 maxval, sum1, sum2, ratio;
2532 L_BMF *bmf;
2533 NUMA *na1, *na2, *na3, *narv;
2534 NUMAA *naa;
2535 PIX *pix1;
2536 PIXA *pixa1, *pixa2, *pixa3;
2537 
2538  PROCNAME("pixDecideIfPhotoImage");
2539 
2540  if (!pnaa)
2541  return ERROR_INT("&naa not defined", procName, 1);
2542  *pnaa = NULL;
2543  if (!pix || pixGetDepth(pix) != 8 || pixGetColormap(pix))
2544  return ERROR_INT("pix undefined or invalid", procName, 1);
2545  if (n < 1 || n > 7) {
2546  L_WARNING("n = %d is invalid; setting to 4\n", procName, n);
2547  n = 4;
2548  }
2549  if (thresh <= 0.0) thresh = 1.3; /* default */
2550 
2551  /* Look for text lines */
2552  pixDecideIfText(pix, NULL, &istext, pixadebug);
2553  if (istext) {
2554  L_INFO("Image is text\n", procName);
2555  return 0;
2556  }
2557 
2558  /* Determine grid from n */
2559  pixGetDimensions(pix, &w, &h, NULL);
2560  if (w == 0 || h == 0)
2561  return ERROR_INT("invalid pix dimension", procName, 1);
2562  findHistoGridDimensions(n, w, h, &nx, &ny, 1);
2563 
2564  /* Evaluate histograms in each tile */
2565  pixa1 = pixaSplitPix(pix, nx, ny, 0, 0);
2566  ngrids = nx * ny;
2567  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
2568  naa = numaaCreate(ngrids);
2569  if (pixadebug) {
2570  lept_rmdir("lept/compplot");
2571  lept_mkdir("lept/compplot");
2572  }
2573  for (i = 0; i < ngrids; i++) {
2574  pix1 = pixaGetPix(pixa1, i, L_CLONE);
2575 
2576  /* Get histograms, set white count to 0, normalize max to 255 */
2577  na1 = pixGetGrayHistogram(pix1, factor);
2578  numaSetValue(na1, 255, 0);
2579  na2 = numaWindowedMean(na1, 5); /* do some smoothing */
2580  numaGetMax(na2, &maxval, NULL);
2581  na3 = numaTransform(na2, 0, 255.0 / maxval);
2582  if (pixadebug) {
2583  snprintf(buf, sizeof(buf), "/tmp/lept/compplot/plot.%d", i);
2584  gplotSimple1(na3, GPLOT_PNG, buf, "Histos");
2585  }
2586 
2587  numaaAddNuma(naa, na3, L_INSERT);
2588  numaDestroy(&na1);
2589  numaDestroy(&na2);
2590  pixDestroy(&pix1);
2591  }
2592  if (pixadebug) {
2593  pix1 = pixaDisplayTiledInColumns(pixa1, nx, 1.0, 30, 2);
2594  pixaAddPix(pixadebug, pix1, L_INSERT);
2595  pixa2 = pixaReadFiles("/tmp/lept/compplot", ".png");
2596  pixa3 = pixaScale(pixa2, 0.4, 0.4);
2597  pix1 = pixaDisplayTiledInColumns(pixa3, nx, 1.0, 30, 2);
2598  pixaAddPix(pixadebug, pix1, L_INSERT);
2599  pixaDestroy(&pixa2);
2600  pixaDestroy(&pixa3);
2601  }
2602 
2603  /* Compute the standard deviation between these histos to decide
2604  * if the image is photo or something more like line art,
2605  * which does not support good comparison by tiled histograms. */
2606  grayInterHistogramStats(naa, 5, NULL, NULL, NULL, &narv);
2607 
2608  /* For photos, the root variance has a larger weight of
2609  * values in the range [50 ... 150] compared to [200 ... 230],
2610  * than text or line art. For the latter, most of the variance
2611  * between tiles is in the lightest parts of the image, well
2612  * above 150. */
2613  numaGetSumOnInterval(narv, 50, 150, &sum1);
2614  numaGetSumOnInterval(narv, 200, 230, &sum2);
2615  if (sum2 == 0.0) { /* shouldn't happen */
2616  ratio = 0.001; /* anything very small for debug output */
2617  isphoto = 0; /* be conservative */
2618  } else {
2619  ratio = sum1 / sum2;
2620  isphoto = (ratio > thresh) ? 1 : 0;
2621  }
2622  if (pixadebug) {
2623  if (isphoto)
2624  L_INFO("ratio %f > %f; isphoto is true\n",
2625  procName, ratio, thresh);
2626  else
2627  L_INFO("ratio %f < %f; isphoto is false\n",
2628  procName, ratio, thresh);
2629  }
2630  if (isphoto)
2631  *pnaa = naa;
2632  else
2633  numaaDestroy(&naa);
2634  bmfDestroy(&bmf);
2635  numaDestroy(&narv);
2636  pixaDestroy(&pixa1);
2637  return 0;
2638 }
2639 
2640 
2666 static l_ok
2668  l_int32 w,
2669  l_int32 h,
2670  l_int32 *pnx,
2671  l_int32 *pny,
2672  l_int32 debug)
2673 {
2674 l_int32 nx, ny, max;
2675 l_float32 ratio;
2676 
2677  ratio = (l_float32)w / (l_float32)h;
2678  max = n * n;
2679  nx = ny = n;
2680  while (nx > 1 && ny > 1) {
2681  if (ratio > 2.0) { /* reduce ny */
2682  ny--;
2683  nx = max / ny;
2684  if (debug)
2685  lept_stderr("nx = %d, ny = %d, ratio w/h = %4.2f\n",
2686  nx, ny, ratio);
2687  } else if (ratio < 0.5) { /* reduce nx */
2688  nx--;
2689  ny = max / nx;
2690  if (debug)
2691  lept_stderr("nx = %d, ny = %d, ratio w/h = %4.2f\n",
2692  nx, ny, ratio);
2693  } else { /* we're ok */
2694  if (debug)
2695  lept_stderr("nx = %d, ny = %d, ratio w/h = %4.2f\n",
2696  nx, ny, ratio);
2697  break;
2698  }
2699  ratio = (l_float32)(ny * w) / (l_float32)(nx * h);
2700  }
2701  *pnx = nx;
2702  *pny = ny;
2703  return 0;
2704 }
2705 
2706 
2729 l_ok
2731  NUMAA *naa2,
2732  l_float32 minratio,
2733  l_int32 w1,
2734  l_int32 h1,
2735  l_int32 w2,
2736  l_int32 h2,
2737  l_float32 *pscore,
2738  PIXA *pixadebug)
2739 {
2740 char buf1[128], buf2[128];
2741 l_int32 i, n;
2742 l_float32 wratio, hratio, score, minscore, dist;
2743 L_BMF *bmf;
2744 NUMA *na1, *na2, *nadist, *nascore;
2745 
2746  PROCNAME("compareTilesByHisto");
2747 
2748  if (!pscore)
2749  return ERROR_INT("&score not defined", procName, 1);
2750  *pscore = 0.0;
2751  if (!naa1 || !naa2)
2752  return ERROR_INT("naa1 and naa2 not both defined", procName, 1);
2753 
2754  /* Filter for different sizes */
2755  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2756  (l_float32)w2 / (l_float32)w1;
2757  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2758  (l_float32)h2 / (l_float32)h1;
2759  if (wratio < minratio || hratio < minratio) {
2760  if (pixadebug)
2761  L_INFO("Sizes differ: wratio = %f, hratio = %f\n",
2762  procName, wratio, hratio);
2763  return 0;
2764  }
2765  n = numaaGetCount(naa1);
2766  if (n != numaaGetCount(naa2)) { /* due to differing w/h ratio */
2767  L_INFO("naa1 and naa2 sizes are different\n", procName);
2768  return 0;
2769  }
2770 
2771  if (pixadebug) {
2772  lept_rmdir("lept/comptile");
2773  lept_mkdir("lept/comptile");
2774  }
2775 
2776 
2777  /* Evaluate histograms in each tile. Remove white before
2778  * computing EMD, because there are may be a lot of white
2779  * pixels due to padding, and we don't want to include them.
2780  * This also makes the debug histo plots more informative. */
2781  minscore = 1.0;
2782  nadist = numaCreate(n);
2783  nascore = numaCreate(n);
2784  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
2785  for (i = 0; i < n; i++) {
2786  na1 = numaaGetNuma(naa1, i, L_CLONE);
2787  na2 = numaaGetNuma(naa2, i, L_CLONE);
2788  numaSetValue(na1, 255, 0.0);
2789  numaSetValue(na2, 255, 0.0);
2790 
2791  /* To compare histograms, use the normalized earthmover distance.
2792  * Further normalize to get the EM distance as a fraction of the
2793  * maximum distance in the histogram (255). Finally, scale this
2794  * up by 10.0, and subtract from 1.0 to get a similarity score. */
2795  numaEarthMoverDistance(na1, na2, &dist);
2796  score = L_MAX(0.0, 1.0 - 10.0 * (dist / 255.));
2797  numaAddNumber(nadist, dist);
2798  numaAddNumber(nascore, score);
2799  minscore = L_MIN(minscore, score);
2800  if (pixadebug) {
2801  snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d", i);
2802  gplotSimple2(na1, na2, GPLOT_PNG, buf1, "Histos");
2803  }
2804  numaDestroy(&na1);
2805  numaDestroy(&na2);
2806  }
2807  *pscore = minscore;
2808 
2809  if (pixadebug) {
2810  for (i = 0; i < n; i++) {
2811  PIX *pix1, *pix2;
2812  snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d.png", i);
2813  pix1 = pixRead(buf1);
2814  numaGetFValue(nadist, i, &dist);
2815  numaGetFValue(nascore, i, &score);
2816  snprintf(buf2, sizeof(buf2),
2817  "Image %d\ndist = %5.3f, score = %5.3f", i, dist, score);
2818  pix2 = pixAddTextlines(pix1, bmf, buf2, 0x0000ff00, L_ADD_BELOW);
2819  pixaAddPix(pixadebug, pix2, L_INSERT);
2820  pixDestroy(&pix1);
2821  }
2822  lept_stderr("Writing to /tmp/lept/comptile/comparegray.pdf\n");
2823  pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
2824  "/tmp/lept/comptile/comparegray.pdf");
2825  numaWriteDebug("/tmp/lept/comptile/scores.na", nascore);
2826  numaWriteDebug("/tmp/lept/comptile/dists.na", nadist);
2827  }
2828 
2829  bmfDestroy(&bmf);
2830  numaDestroy(&nadist);
2831  numaDestroy(&nascore);
2832  return 0;
2833 }
2834 
2835 
2907 l_ok
2909  PIX *pix2,
2910  BOX *box1,
2911  BOX *box2,
2912  l_float32 minratio,
2913  l_int32 maxgray,
2914  l_int32 factor,
2915  l_int32 n,
2916  l_float32 *pscore,
2917  l_int32 debugflag)
2918 {
2919 l_int32 w1, h1, w2, h2;
2920 l_float32 wratio, hratio;
2921 BOX *box3, *box4;
2922 PIX *pix3, *pix4, *pix5, *pix6, *pix7, *pix8;
2923 PIXA *pixa;
2924 
2925  PROCNAME("pixCompareGrayByHisto");
2926 
2927  if (!pscore)
2928  return ERROR_INT("&score not defined", procName, 1);
2929  *pscore = 0.0;
2930  if (!pix1 || !pix2)
2931  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
2932  if (minratio < 0.5 || minratio > 1.0)
2933  return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1);
2934  if (maxgray < 200)
2935  return ERROR_INT("invalid maxgray; should be >= 200", procName, 1);
2936  maxgray = L_MIN(255, maxgray);
2937  if (factor < 1)
2938  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2939  if (n < 1 || n > 7) {
2940  L_WARNING("n = %d is invalid; setting to 4\n", procName, n);
2941  n = 4;
2942  }
2943 
2944  if (debugflag)
2945  lept_mkdir("lept/comp");
2946 
2947  /* Initial filter by size */
2948  if (box1)
2949  boxGetGeometry(box1, NULL, NULL, &w1, &h1);
2950  else
2951  pixGetDimensions(pix1, &w1, &h1, NULL);
2952  if (box2)
2953  boxGetGeometry(box2, NULL, NULL, &w2, &h2);
2954  else
2955  pixGetDimensions(pix1, &w2, &h2, NULL);
2956  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2957  (l_float32)w2 / (l_float32)w1;
2958  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2959  (l_float32)h2 / (l_float32)h1;
2960  if (wratio < minratio || hratio < minratio)
2961  return 0;
2962 
2963  /* Initial crop, if necessary */
2964  if (box1)
2965  pix3 = pixClipRectangle(pix1, box1, NULL);
2966  else
2967  pix3 = pixClone(pix1);
2968  if (box2)
2969  pix4 = pixClipRectangle(pix2, box2, NULL);
2970  else
2971  pix4 = pixClone(pix2);
2972 
2973  /* Convert to 8 bpp, align centroids and do maximal crop */
2974  pix5 = pixConvertTo8(pix3, FALSE);
2975  pix6 = pixConvertTo8(pix4, FALSE);
2976  pixCropAlignedToCentroid(pix5, pix6, factor, &box3, &box4);
2977  pix7 = pixClipRectangle(pix5, box3, NULL);
2978  pix8 = pixClipRectangle(pix6, box4, NULL);
2979  pixa = (debugflag) ? pixaCreate(0) : NULL;
2980  if (debugflag) {
2981  PIX *pix9, *pix10, *pix11, *pix12, *pix13;
2982  PIXA *pixa2;
2983  pix9 = pixConvertTo32(pix5);
2984  pix10 = pixConvertTo32(pix6);
2985  pixRenderBoxArb(pix9, box3, 2, 255, 0, 0);
2986  pixRenderBoxArb(pix10, box4, 2, 255, 0, 0);
2987  pix11 = pixScaleToSize(pix9, 400, 0);
2988  pix12 = pixScaleToSize(pix10, 400, 0);
2989  pixa2 = pixaCreate(2);
2990  pixaAddPix(pixa2, pix11, L_INSERT);
2991  pixaAddPix(pixa2, pix12, L_INSERT);
2992  pix13 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 0);
2993  pixaAddPix(pixa, pix13, L_INSERT);
2994  pixDestroy(&pix9);
2995  pixDestroy(&pix10);
2996  pixaDestroy(&pixa2);
2997  }
2998  pixDestroy(&pix3);
2999  pixDestroy(&pix4);
3000  pixDestroy(&pix5);
3001  pixDestroy(&pix6);
3002  boxDestroy(&box3);
3003  boxDestroy(&box4);
3004 
3005  /* Tile and compare histograms */
3006  pixCompareTilesByHisto(pix7, pix8, maxgray, factor, n, pscore, pixa);
3007  pixaDestroy(&pixa);
3008  pixDestroy(&pix7);
3009  pixDestroy(&pix8);
3010  return 0;
3011 }
3012 
3013 
3034 static l_ok
3036  PIX *pix2,
3037  l_int32 maxgray,
3038  l_int32 factor,
3039  l_int32 n,
3040  l_float32 *pscore,
3041  PIXA *pixadebug)
3042 {
3043 char buf[64];
3044 l_int32 w, h, i, j, nx, ny, ngr;
3045 l_float32 score, minscore, maxval1, maxval2, dist;
3046 L_BMF *bmf;
3047 NUMA *na1, *na2, *na3, *na4, *na5, *na6, *na7;
3048 PIX *pix3, *pix4;
3049 PIXA *pixa1, *pixa2;
3050 
3051  PROCNAME("pixCompareTilesByHisto");
3052 
3053  if (!pscore)
3054  return ERROR_INT("&score not defined", procName, 1);
3055  *pscore = 0.0;
3056  if (!pix1 || !pix2)
3057  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
3058 
3059  /* Determine grid from n */
3060  pixGetDimensions(pix1, &w, &h, NULL);
3061  findHistoGridDimensions(n, w, h, &nx, &ny, 1);
3062  ngr = nx * ny;
3063 
3064  /* Evaluate histograms in each tile */
3065  pixa1 = pixaSplitPix(pix1, nx, ny, 0, 0);
3066  pixa2 = pixaSplitPix(pix2, nx, ny, 0, 0);
3067  na7 = (pixadebug) ? numaCreate(ngr) : NULL;
3068  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
3069  minscore = 1.0;
3070  for (i = 0; i < ngr; i++) {
3071  pix3 = pixaGetPix(pixa1, i, L_CLONE);
3072  pix4 = pixaGetPix(pixa2, i, L_CLONE);
3073 
3074  /* Get histograms, set white count to 0, normalize max to 255 */
3075  na1 = pixGetGrayHistogram(pix3, factor);
3076  na2 = pixGetGrayHistogram(pix4, factor);
3077  if (maxgray < 255) {
3078  for (j = maxgray + 1; j <= 255; j++) {
3079  numaSetValue(na1, j, 0);
3080  numaSetValue(na2, j, 0);
3081  }
3082  }
3083  na3 = numaWindowedMean(na1, 5);
3084  na4 = numaWindowedMean(na2, 5);
3085  numaGetMax(na3, &maxval1, NULL);
3086  numaGetMax(na4, &maxval2, NULL);
3087  na5 = numaTransform(na3, 0, 255.0 / maxval1);
3088  na6 = numaTransform(na4, 0, 255.0 / maxval2);
3089  if (pixadebug) {
3090  gplotSimple2(na5, na6, GPLOT_PNG, "/tmp/lept/comp/plot1", "Histos");
3091  }
3092 
3093  /* To compare histograms, use the normalized earthmover distance.
3094  * Further normalize to get the EM distance as a fraction of the
3095  * maximum distance in the histogram (255). Finally, scale this
3096  * up by 10.0, and subtract from 1.0 to get a similarity score. */
3097  numaEarthMoverDistance(na5, na6, &dist);
3098  score = L_MAX(0.0, 1.0 - 8.0 * (dist / 255.));
3099  if (pixadebug) numaAddNumber(na7, score);
3100  minscore = L_MIN(minscore, score);
3101  if (pixadebug) {
3102  PIX *pix5, *pix6, *pix7, *pix8, *pix9, *pix10;
3103  PIXA *pixa3;
3104  l_int32 w, h, wscale;
3105  pixa3 = pixaCreate(3);
3106  pixGetDimensions(pix3, &w, &h, NULL);
3107  wscale = (w > h) ? 700 : 400;
3108  pix5 = pixScaleToSize(pix3, wscale, 0);
3109  pix6 = pixScaleToSize(pix4, wscale, 0);
3110  pixaAddPix(pixa3, pix5, L_INSERT);
3111  pixaAddPix(pixa3, pix6, L_INSERT);
3112  pix7 = pixRead("/tmp/lept/comp/plot1.png");
3113  pix8 = pixScaleToSize(pix7, 700, 0);
3114  snprintf(buf, sizeof(buf), "%5.3f", score);
3115  pix9 = pixAddTextlines(pix8, bmf, buf, 0x0000ff00, L_ADD_RIGHT);
3116  pixaAddPix(pixa3, pix9, L_INSERT);
3117  pix10 = pixaDisplayTiledInRows(pixa3, 32, 1000, 1.0, 0, 50, 0);
3118  pixaAddPix(pixadebug, pix10, L_INSERT);
3119  pixDestroy(&pix7);
3120  pixDestroy(&pix8);
3121  pixaDestroy(&pixa3);
3122  }
3123  numaDestroy(&na1);
3124  numaDestroy(&na2);
3125  numaDestroy(&na3);
3126  numaDestroy(&na4);
3127  numaDestroy(&na5);
3128  numaDestroy(&na6);
3129  pixDestroy(&pix3);
3130  pixDestroy(&pix4);
3131  }
3132  *pscore = minscore;
3133 
3134  if (pixadebug) {
3135  pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
3136  "/tmp/lept/comp/comparegray.pdf");
3137  numaWriteDebug("/tmp/lept/comp/tilescores.na", na7);
3138  }
3139 
3140  bmfDestroy(&bmf);
3141  numaDestroy(&na7);
3142  pixaDestroy(&pixa1);
3143  pixaDestroy(&pixa2);
3144  return 0;
3145 }
3146 
3147 
3164 l_ok
3166  PIX *pix2,
3167  l_int32 factor,
3168  BOX **pbox1,
3169  BOX **pbox2)
3170 {
3171 l_float32 cx1, cy1, cx2, cy2;
3172 l_int32 w1, h1, w2, h2, icx1, icy1, icx2, icy2;
3173 l_int32 xm, xm1, xm2, xp, xp1, xp2, ym, ym1, ym2, yp, yp1, yp2;
3174 PIX *pix3, *pix4;
3175 
3176  PROCNAME("pixCropAlignedToCentroid");
3177 
3178  if (pbox1) *pbox1 = NULL;
3179  if (pbox2) *pbox2 = NULL;
3180  if (!pix1 || !pix2)
3181  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
3182  if (factor < 1)
3183  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
3184  if (!pbox1 || !pbox2)
3185  return ERROR_INT("&box1 and &box2 not both defined", procName, 1);
3186 
3187  pix3 = pixConvertTo8(pix1, FALSE);
3188  pix4 = pixConvertTo8(pix2, FALSE);
3189  pixCentroid8(pix3, factor, &cx1, &cy1);
3190  pixCentroid8(pix4, factor, &cx2, &cy2);
3191  pixGetDimensions(pix3, &w1, &h1, NULL);
3192  pixGetDimensions(pix4, &w2, &h2, NULL);
3193  pixDestroy(&pix3);
3194  pixDestroy(&pix4);
3195 
3196  icx1 = (l_int32)(cx1 + 0.5);
3197  icy1 = (l_int32)(cy1 + 0.5);
3198  icx2 = (l_int32)(cx2 + 0.5);
3199  icy2 = (l_int32)(cy2 + 0.5);
3200  xm = L_MIN(icx1, icx2);
3201  xm1 = icx1 - xm;
3202  xm2 = icx2 - xm;
3203  xp = L_MIN(w1 - icx1, w2 - icx2); /* one pixel beyond to the right */
3204  xp1 = icx1 + xp;
3205  xp2 = icx2 + xp;
3206  ym = L_MIN(icy1, icy2);
3207  ym1 = icy1 - ym;
3208  ym2 = icy2 - ym;
3209  yp = L_MIN(h1 - icy1, h2 - icy2); /* one pixel below the bottom */
3210  yp1 = icy1 + yp;
3211  yp2 = icy2 + yp;
3212  *pbox1 = boxCreate(xm1, ym1, xp1 - xm1, yp1 - ym1);
3213  *pbox2 = boxCreate(xm2, ym2, xp2 - xm2, yp2 - ym2);
3214  return 0;
3215 }
3216 
3217 
3239 l_uint8 *
3241  l_int32 w,
3242  l_int32 h,
3243  size_t *psize)
3244 {
3245 l_uint8 *bytea;
3246 l_int32 i, j, n, nn, ival;
3247 l_float32 maxval;
3248 NUMA *na1, *na2;
3249 
3250  PROCNAME("l_compressGrayHistograms");
3251 
3252  if (!psize)
3253  return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL);
3254  *psize = 0;
3255  if (!naa)
3256  return (l_uint8 *)ERROR_PTR("naa not defined", procName, NULL);
3257  n = numaaGetCount(naa);
3258  for (i = 0; i < n; i++) {
3259  nn = numaaGetNumaCount(naa, i);
3260  if (nn != 256) {
3261  L_ERROR("%d numbers in numa[%d]\n", procName, nn, i);
3262  return NULL;
3263  }
3264  }
3265 
3266  if ((bytea = (l_uint8 *)LEPT_CALLOC(8 + 256 * n, sizeof(l_uint8))) == NULL)
3267  return (l_uint8 *)ERROR_PTR("bytea not made", procName, NULL);
3268  *psize = 8 + 256 * n;
3269  l_setDataFourBytes(bytea, 0, w);
3270  l_setDataFourBytes(bytea, 1, h);
3271  for (i = 0; i < n; i++) {
3272  na1 = numaaGetNuma(naa, i, L_COPY);
3273  numaGetMax(na1, &maxval, NULL);
3274  na2 = numaTransform(na1, 0, 255.0 / maxval);
3275  for (j = 0; j < 256; j++) {
3276  numaGetIValue(na2, j, &ival);
3277  bytea[8 + 256 * i + j] = ival;
3278  }
3279  numaDestroy(&na1);
3280  numaDestroy(&na2);
3281  }
3282 
3283  return bytea;
3284 }
3285 
3286 
3307 NUMAA *
3309  size_t size,
3310  l_int32 *pw,
3311  l_int32 *ph)
3312 {
3313 l_int32 i, j, n;
3314 NUMA *na;
3315 NUMAA *naa;
3316 
3317  PROCNAME("l_uncompressGrayHistograms");
3318 
3319  if (pw) *pw = 0;
3320  if (ph) *ph = 0;
3321  if (!pw || !ph)
3322  return (NUMAA *)ERROR_PTR("&w and &h not both defined", procName, NULL);
3323  if (!bytea)
3324  return (NUMAA *)ERROR_PTR("bytea not defined", procName, NULL);
3325  n = (size - 8) / 256;
3326  if ((size - 8) % 256 != 0)
3327  return (NUMAA *)ERROR_PTR("bytea size is invalid", procName, NULL);
3328 
3329  *pw = l_getDataFourBytes(bytea, 0);
3330  *ph = l_getDataFourBytes(bytea, 1);
3331  naa = numaaCreate(n);
3332  for (i = 0; i < n; i++) {
3333  na = numaCreate(256);
3334  for (j = 0; j < 256; j++)
3335  numaAddNumber(na, bytea[8 + 256 * i + j]);
3336  numaaAddNuma(naa, na, L_INSERT);
3337  }
3338 
3339  return naa;
3340 }
3341 
3342 
3343 /*------------------------------------------------------------------*
3344  * Translated images at the same resolution *
3345  *------------------------------------------------------------------*/
3376 l_ok
3378  PIX *pix2,
3379  l_int32 thresh,
3380  l_int32 *pdelx,
3381  l_int32 *pdely,
3382  l_float32 *pscore,
3383  l_int32 debugflag)
3384 {
3385 l_uint8 *subtab;
3386 l_int32 i, level, area1, area2, delx, dely;
3387 l_int32 etransx, etransy, maxshift, dbint;
3388 l_int32 *stab, *ctab;
3389 l_float32 cx1, cx2, cy1, cy2, score;
3390 PIX *pixb1, *pixb2, *pixt1, *pixt2, *pixt3, *pixt4;
3391 PIXA *pixa1, *pixa2, *pixadb;
3392 
3393  PROCNAME("pixCompareWithTranslation");
3394 
3395  if (pdelx) *pdelx = 0;
3396  if (pdely) *pdely = 0;
3397  if (pscore) *pscore = 0.0;
3398  if (!pdelx || !pdely)
3399  return ERROR_INT("&delx and &dely not defined", procName, 1);
3400  if (!pscore)
3401  return ERROR_INT("&score not defined", procName, 1);
3402  if (!pix1)
3403  return ERROR_INT("pix1 not defined", procName, 1);
3404  if (!pix2)
3405  return ERROR_INT("pix2 not defined", procName, 1);
3406 
3407  /* Make tables */
3408  subtab = makeSubsampleTab2x();
3409  stab = makePixelSumTab8();
3410  ctab = makePixelCentroidTab8();
3411 
3412  /* Binarize each image */
3413  pixb1 = pixConvertTo1(pix1, thresh);
3414  pixb2 = pixConvertTo1(pix2, thresh);
3415 
3416  /* Make a cascade of 2x reduced images for each, thresholding
3417  * with level 2 (neutral), down to 8x reduction */
3418  pixa1 = pixaCreate(4);
3419  pixa2 = pixaCreate(4);
3420  if (debugflag)
3421  pixadb = pixaCreate(4);
3422  pixaAddPix(pixa1, pixb1, L_INSERT);
3423  pixaAddPix(pixa2, pixb2, L_INSERT);
3424  for (i = 0; i < 3; i++) {
3425  pixt1 = pixReduceRankBinary2(pixb1, 2, subtab);
3426  pixt2 = pixReduceRankBinary2(pixb2, 2, subtab);
3427  pixaAddPix(pixa1, pixt1, L_INSERT);
3428  pixaAddPix(pixa2, pixt2, L_INSERT);
3429  pixb1 = pixt1;
3430  pixb2 = pixt2;
3431  }
3432 
3433  /* At the lowest level, use the centroids with a maxshift of 6
3434  * to search for the best alignment. Then at higher levels,
3435  * use the result from the level below as the initial approximation
3436  * for the alignment, and search with a maxshift of 2. */
3437  for (level = 3; level >= 0; level--) {
3438  pixt1 = pixaGetPix(pixa1, level, L_CLONE);
3439  pixt2 = pixaGetPix(pixa2, level, L_CLONE);
3440  pixCountPixels(pixt1, &area1, stab);
3441  pixCountPixels(pixt2, &area2, stab);
3442  if (level == 3) {
3443  pixCentroid(pixt1, ctab, stab, &cx1, &cy1);
3444  pixCentroid(pixt2, ctab, stab, &cx2, &cy2);
3445  etransx = lept_roundftoi(cx1 - cx2);
3446  etransy = lept_roundftoi(cy1 - cy2);
3447  maxshift = 6;
3448  } else {
3449  etransx = 2 * delx;
3450  etransy = 2 * dely;
3451  maxshift = 2;
3452  }
3453  dbint = (debugflag) ? level + 1 : 0;
3454  pixBestCorrelation(pixt1, pixt2, area1, area2, etransx, etransy,
3455  maxshift, stab, &delx, &dely, &score, dbint);
3456  if (debugflag) {
3457  lept_stderr("Level %d: delx = %d, dely = %d, score = %7.4f\n",
3458  level, delx, dely, score);
3459  pixRasteropIP(pixt2, delx, dely, L_BRING_IN_WHITE);
3460  pixt3 = pixDisplayDiffBinary(pixt1, pixt2);
3461  pixt4 = pixExpandReplicate(pixt3, 8 / (1 << (3 - level)));
3462  pixaAddPix(pixadb, pixt4, L_INSERT);
3463  pixDestroy(&pixt3);
3464  }
3465  pixDestroy(&pixt1);
3466  pixDestroy(&pixt2);
3467  }
3468 
3469  if (debugflag) {
3470  pixaConvertToPdf(pixadb, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
3471  "/tmp/lept/comp/compare.pdf");
3472  convertFilesToPdf("/tmp/lept/comp", "correl_", 30, 1.0, L_FLATE_ENCODE,
3473  0, "Correlation scores at levels 1 through 5",
3474  "/tmp/lept/comp/correl.pdf");
3475  pixaDestroy(&pixadb);
3476  }
3477 
3478  *pdelx = delx;
3479  *pdely = dely;
3480  *pscore = score;
3481  pixaDestroy(&pixa1);
3482  pixaDestroy(&pixa2);
3483  LEPT_FREE(subtab);
3484  LEPT_FREE(stab);
3485  LEPT_FREE(ctab);
3486  return 0;
3487 }
3488 
3489 
3530 l_ok
3532  PIX *pix2,
3533  l_int32 area1,
3534  l_int32 area2,
3535  l_int32 etransx,
3536  l_int32 etransy,
3537  l_int32 maxshift,
3538  l_int32 *tab8,
3539  l_int32 *pdelx,
3540  l_int32 *pdely,
3541  l_float32 *pscore,
3542  l_int32 debugflag)
3543 {
3544 l_int32 shiftx, shifty, delx, dely;
3545 l_int32 *tab;
3546 l_float32 maxscore, score;
3547 FPIX *fpix;
3548 PIX *pix3, *pix4;
3549 
3550  PROCNAME("pixBestCorrelation");
3551 
3552  if (pdelx) *pdelx = 0;
3553  if (pdely) *pdely = 0;
3554  if (pscore) *pscore = 0.0;
3555  if (!pix1 || pixGetDepth(pix1) != 1)
3556  return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1);
3557  if (!pix2 || pixGetDepth(pix2) != 1)
3558  return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1);
3559  if (!area1 || !area2)
3560  return ERROR_INT("areas must be > 0", procName, 1);
3561 
3562  if (debugflag > 0)
3563  fpix = fpixCreate(2 * maxshift + 1, 2 * maxshift + 1);
3564 
3565  if (!tab8)
3566  tab = makePixelSumTab8();
3567  else
3568  tab = tab8;
3569 
3570  /* Search over a set of {shiftx, shifty} for the max */
3571  maxscore = 0;
3572  delx = etransx;
3573  dely = etransy;
3574  for (shifty = -maxshift; shifty <= maxshift; shifty++) {
3575  for (shiftx = -maxshift; shiftx <= maxshift; shiftx++) {
3576  pixCorrelationScoreShifted(pix1, pix2, area1, area2,
3577  etransx + shiftx,
3578  etransy + shifty, tab, &score);
3579  if (debugflag > 0) {
3580  fpixSetPixel(fpix, maxshift + shiftx, maxshift + shifty,
3581  1000.0 * score);
3582 /* lept_stderr("(sx, sy) = (%d, %d): score = %6.4f\n",
3583  shiftx, shifty, score); */
3584  }
3585  if (score > maxscore) {
3586  maxscore = score;
3587  delx = etransx + shiftx;
3588  dely = etransy + shifty;
3589  }
3590  }
3591  }
3592 
3593  if (debugflag > 0) {
3594  char buf[128];
3595  lept_mkdir("lept/comp");
3596  pix3 = fpixDisplayMaxDynamicRange(fpix);
3597  pix4 = pixExpandReplicate(pix3, 20);
3598  snprintf(buf, sizeof(buf), "/tmp/lept/comp/correl_%d.png",
3599  debugflag);
3600  pixWrite(buf, pix4, IFF_PNG);
3601  pixDestroy(&pix3);
3602  pixDestroy(&pix4);
3603  fpixDestroy(&fpix);
3604  }
3605 
3606  if (pdelx) *pdelx = delx;
3607  if (pdely) *pdely = dely;
3608  if (pscore) *pscore = maxscore;
3609  if (!tab8) LEPT_FREE(tab);
3610  return 0;
3611 }
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:255
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:115
void bmfDestroy(L_BMF **pbmf)
bmfDestroy()
Definition: bmf.c:169
PIX * pixConvertTo1(PIX *pixs, l_int32 threshold)
pixConvertTo1()
Definition: pixconv.c:3026
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:719
l_ok pixCorrelationBinary(PIX *pix1, PIX *pix2, l_float32 *pval)
pixCorrelationBinary()
Definition: compare.c:604
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:2218
l_ok pixCentroid(PIX *pix, l_int32 *centtab, l_int32 *sumtab, l_float32 *pxave, l_float32 *pyave)
pixCentroid()
Definition: morphapp.c:1533
PIX * pixDilateGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateGray()
Definition: graymorph.c:278
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3332
l_int32 lept_roundftoi(l_float32 fval)
lept_roundftoi()
Definition: utils1.c:700
l_int32 * makePixelCentroidTab8(void)
makePixelCentroidTab8()
Definition: pix3.c:2451
Definition: pix.h:204
PIX * pixConvertLossless(PIX *pixs, l_int32 d)
pixConvertLossless()
Definition: pixconv.c:3840
l_ok pixUsesCmapColor(PIX *pixs, l_int32 *pcolor)
pixUsesCmapColor()
Definition: compare.c:538
PIXA * pixaReadFiles(const char *dirname, const char *substr)
pixaReadFiles()
Definition: readfile.c:127
l_uint8 * l_compressGrayHistograms(NUMAA *naa, l_int32 w, l_int32 h, size_t *psize)
l_compressGrayHistograms()
Definition: compare.c:3240
Definition: pix.h:713
l_ok numaWriteDebug(const char *filename, NUMA *na)
numaWriteDebug()
Definition: numabasic.c:1224
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:478
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
l_ok gplotMakeOutput(GPLOT *gplot)
gplotMakeOutput()
Definition: gplot.c:466
GPLOT * gplotCreate(const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel)
gplotCreate()
Definition: gplot.c:187
l_ok numaGetNonzeroRange(NUMA *na, l_float32 eps, l_int32 *pfirst, l_int32 *plast)
numaGetNonzeroRange()
Definition: numafunc1.c:1078
l_ok pixRasteropIP(PIX *pixd, l_int32 hshift, l_int32 vshift, l_int32 incolor)
pixRasteropIP()
Definition: rop.c:475
Definition: pix.h:712
l_ok numaEarthMoverDistance(NUMA *na1, NUMA *na2, l_float32 *pdist)
numaEarthMoverDistance()
Definition: numafunc2.c:2251
l_ok cmapEqual(PIXCMAP *cmap1, PIXCMAP *cmap2, l_int32 ncomps, l_int32 *psame)
cmapEqual()
Definition: compare.c:480
l_ok pixDecideIfText(PIX *pixs, BOX *box, l_int32 *pistext, PIXA *pixadb)
pixDecideIfText()
Definition: pageseg.c:1374
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 * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:851
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
Definition: pix.h:710
PIX * pixGetAverageTiled(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 type)
pixGetAverageTiled()
Definition: pix4.c:1727
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
l_ok pixSetMaskedGeneral(PIX *pixd, PIX *pixm, l_uint32 val, l_int32 x, l_int32 y)
pixSetMaskedGeneral()
Definition: pix3.c:302
PIXA * pixaSplitPix(PIX *pixs, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor)
pixaSplitPix()
Definition: pixabasic.c:350
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:817
l_ok pixaComparePhotoRegionsByHisto(PIXA *pixa, l_float32 minratio, l_float32 textthresh, l_int32 factor, l_int32 n, l_float32 simthresh, NUMA **pnai, l_float32 **pscores, PIX **ppixd, l_int32 debug)
pixaComparePhotoRegionsByHisto()
Definition: compare.c:1930
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:194
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
l_ok numaSetCount(NUMA *na, l_int32 newcount)
numaSetCount()
Definition: numabasic.c:685
l_ok pixCompareBinary(PIX *pix1, PIX *pix2, l_int32 comptype, l_float32 *pfract, PIX **ppixdiff)
pixCompareBinary()
Definition: compare.c:719
l_ok pixCompareGrayByHisto(PIX *pix1, PIX *pix2, BOX *box1, BOX *box2, l_float32 minratio, l_int32 maxgray, l_int32 factor, l_int32 n, l_float32 *pscore, l_int32 debugflag)
pixCompareGrayByHisto()
Definition: compare.c:2908
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:447
NUMA * numaClipToInterval(NUMA *nas, l_int32 first, l_int32 last)
numaClipToInterval()
Definition: numafunc1.c:1182
l_ok convertFilesToPdf(const char *dirname, const char *substr, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout)
convertFilesToPdf()
Definition: pdfio1.c:253
l_ok pixCompareRGB(PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff)
pixCompareRGB()
Definition: compare.c:991
PIX * pixPadToCenterCentroid(PIX *pixs, l_int32 factor)
pixPadToCenterCentroid()
Definition: compare.c:2382
NUMAA * numaaCreate(l_int32 n)
numaaCreate()
Definition: numabasic.c:1407
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1026
PIX * pixaDisplayTiledInColumns(PIXA *pixas, l_int32 nx, l_float32 scalefactor, l_int32 spacing, l_int32 border)
pixaDisplayTiledInColumns()
Definition: pixafunc2.c:930
l_ok pixaccMultConst(PIXACC *pixacc, l_float32 factor)
pixaccMultConst()
Definition: pixacc.c:298
l_ok numaSetValue(NUMA *na, l_int32 index, l_float32 val)
numaSetValue()
Definition: numabasic.c:786
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
PIX * pixDisplayDiffBinary(PIX *pix1, PIX *pix2)
pixDisplayDiffBinary()
Definition: compare.c:662
l_ok pixCentroid8(PIX *pixs, l_int32 factor, l_float32 *pcx, l_float32 *pcy)
pixCentroid8()
Definition: compare.c:2436
l_ok pixCompareWithTranslation(PIX *pix1, PIX *pix2, l_int32 thresh, l_int32 *pdelx, l_int32 *pdely, l_float32 *pscore, l_int32 debugflag)
pixCompareWithTranslation()
Definition: compare.c:3377
void numaaDestroy(NUMAA **pnaa)
numaaDestroy()
Definition: numabasic.c:1510
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2479
Definition: bmf.h:46
l_ok fpixSetPixel(FPIX *fpix, l_int32 x, l_int32 y, l_float32 val)
fpixSetPixel()
Definition: fpix1.c:600
l_ok pixPaintThroughMask(PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_uint32 val)
pixPaintThroughMask()
Definition: pix3.c:626
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:125
PIX * fpixDisplayMaxDynamicRange(FPIX *fpixs)
fpixDisplayMaxDynamicRange()
Definition: fpix2.c:428
void pixaccDestroy(PIXACC **ppixacc)
pixaccDestroy()
Definition: pixacc.c:162
NUMA * numaaGetNuma(NUMAA *naa, l_int32 index, l_int32 accessflag)
numaaGetNuma()
Definition: numabasic.c:1740
PIX * pixSubtractGray(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtractGray()
Definition: pixarith.c:357
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:754
l_ok pixCompareGray(PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff)
pixCompareGray()
Definition: compare.c:882
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
Definition: array.h:70
PIX * pixaDisplayTiledByIndex(PIXA *pixa, NUMA *na, l_int32 width, l_int32 spacing, l_int32 border, l_int32 fontsize, l_uint32 textcolor)
pixaDisplayTiledByIndex()
Definition: pixafunc2.c:1296
static l_ok findHistoGridDimensions(l_int32 n, l_int32 w, l_int32 h, l_int32 *pnx, l_int32 *pny, l_int32 debug)
findHistoGridDimensions()
Definition: compare.c:2667
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:1526
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1688
PIX * pixAbsDifference(PIX *pixs1, PIX *pixs2)
pixAbsDifference()
Definition: pixarith.c:970
PIX * pixAnd(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixAnd()
Definition: pix3.c:1624
PIX * pixaDisplayTiledInRows(PIXA *pixa, l_int32 outdepth, l_int32 maxwidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border)
pixaDisplayTiledInRows()
Definition: pixafunc2.c:746
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:824
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1937
l_ok gplotSimple2(NUMA *na1, NUMA *na2, l_int32 outformat, const char *outroot, const char *title)
gplotSimple2()
Definition: gplot.c:703
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2411
l_ok pixEqualWithAlpha(PIX *pix1, PIX *pix2, l_int32 use_alpha, l_int32 *psame)
pixEqualWithAlpha()
Definition: compare.c:182
Definition: pix.h:543
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:963
NUMA * pixCompareRankDifference(PIX *pix1, PIX *pix2, l_int32 factor)
pixCompareRankDifference()
Definition: compare.c:1241
l_ok pixComparePhotoRegionsByHisto(PIX *pix1, PIX *pix2, BOX *box1, BOX *box2, l_float32 minratio, l_int32 factor, l_int32 n, l_float32 *pscore, l_int32 debugflag)
pixComparePhotoRegionsByHisto()
Definition: compare.c:2152
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
Definition: gplot.h:76
l_ok gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plotlabel)
gplotAddPlot()
Definition: gplot.c:320
PIX * pixCreateRGBImage(PIX *pixr, PIX *pixg, PIX *pixb)
pixCreateRGBImage()
Definition: pix2.c:2423
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1753
PIX * pixScaleToSize(PIX *pixs, l_int32 wd, l_int32 hd)
pixScaleToSize()
Definition: scale1.c:323
Definition: array.h:82
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
Definition: pix.h:711
PIX * pixScaleByIntSampling(PIX *pixs, l_int32 factor)
pixScaleByIntSampling()
Definition: scale1.c:1444
l_ok pixcmapHasColor(PIXCMAP *cmap, l_int32 *pcolor)
pixcmapHasColor()
Definition: colormap.c:1075
l_ok pixCropAlignedToCentroid(PIX *pix1, PIX *pix2, l_int32 factor, BOX **pbox1, BOX **pbox2)
pixCropAlignedToCentroid()
Definition: compare.c:3165
PIX * pixaccFinal(PIXACC *pixacc, l_int32 outdepth)
pixaccFinal()
Definition: pixacc.c:193
Definition: pix.h:455
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:190
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
PIX * pixReduceRankBinary2(PIX *pixs, l_int32 level, l_uint8 *intab)
pixReduceRankBinary2()
Definition: binreduce.c:227
NUMA * numaTransform(NUMA *nas, l_float32 shift, l_float32 scale)
numaTransform()
Definition: numafunc2.c:415
l_ok compareTilesByHisto(NUMAA *naa1, NUMAA *naa2, l_float32 minratio, l_int32 w1, l_int32 h1, l_int32 w2, l_int32 h2, l_float32 *pscore, PIXA *pixadebug)
compareTilesByHisto()
Definition: compare.c:2730
l_int32 l_getDataFourBytes(const void *line, l_int32 n)
l_getDataFourBytes()
Definition: arrayaccess.c:343
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:193
NUMA * pixGetDifferenceHistogram(PIX *pix1, PIX *pix2, l_int32 factor)
pixGetDifferenceHistogram()
Definition: compare.c:1507
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1512
NUMA * numaWindowedMean(NUMA *nas, l_int32 wc)
numaWindowedMean()
Definition: numafunc2.c:588
PIX * pixExpandReplicate(PIX *pixs, l_int32 factor)
pixExpandReplicate()
Definition: scale2.c:872
FPIX * fpixCreate(l_int32 width, l_int32 height)
fpixCreate()
Definition: fpix1.c:156
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:892
l_ok pixDecideIfPhotoImage(PIX *pix, l_int32 factor, l_float32 thresh, l_int32 n, NUMAA **pnaa, PIXA *pixadebug)
pixDecideIfPhotoImage()
Definition: compare.c:2522
l_ok pixCompareTiled(PIX *pix1, PIX *pix2, l_int32 sx, l_int32 sy, l_int32 type, PIX **ppixdiff)
pixCompareTiled()
Definition: compare.c:1147
l_ok pixaConvertToPdf(PIXA *pixa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout)
pixaConvertToPdf()
Definition: pdfio1.c:790
l_int32 numaaGetCount(NUMAA *naa)
numaaGetCount()
Definition: numabasic.c:1631
PIXACC * pixaccCreate(l_int32 w, l_int32 h, l_int32 negflag)
pixaccCreate()
Definition: pixacc.c:92
PIXA * pixaScale(PIXA *pixas, l_float32 scalex, l_float32 scaley)
pixaScale()
Definition: pixafunc1.c:2111
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:691
l_int32 numaaGetNumaCount(NUMAA *naa, l_int32 index)
numaaGetNumaCount()
Definition: numabasic.c:1649
l_ok pixRenderBoxArb(PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval)
pixRenderBoxArb()
Definition: graphics.c:1655
l_ok pixBestCorrelation(PIX *pix1, PIX *pix2, l_int32 area1, l_int32 area2, l_int32 etransx, l_int32 etransy, l_int32 maxshift, l_int32 *tab8, l_int32 *pdelx, l_int32 *pdely, l_float32 *pscore, l_int32 debugflag)
pixBestCorrelation()
Definition: compare.c:3531
Definition: pix.h:138
l_ok pixGenPhotoHistos(PIX *pixs, BOX *box, l_int32 factor, l_float32 thresh, l_int32 n, NUMAA **pnaa, l_int32 *pw, l_int32 *ph, l_int32 debugindex)
pixGenPhotoHistos()
Definition: compare.c:2269
void l_setDataFourBytes(void *line, l_int32 n, l_int32 val)
l_setDataFourBytes()
Definition: arrayaccess.c:359
l_ok pixTestForSimilarity(PIX *pix1, PIX *pix2, l_int32 factor, l_int32 mindiff, l_float32 maxfract, l_float32 maxave, l_int32 *psimilar, l_int32 details)
pixTestForSimilarity()
Definition: compare.c:1326
l_ok pixZero(PIX *pix, l_int32 *pempty)
pixZero()
Definition: pix3.c:1815
NUMAA * l_uncompressGrayHistograms(l_uint8 *bytea, size_t size, l_int32 *pw, l_int32 *ph)
l_uncompressGrayHistograms()
Definition: compare.c:3308
#define PIX_SRC
Definition: pix.h:330
l_ok pixCompareGrayOrRGB(PIX *pix1, PIX *pix2, l_int32 comptype, l_int32 plottype, l_int32 *psame, l_float32 *pdiff, l_float32 *prmsdiff, PIX **ppixdiff)
pixCompareGrayOrRGB()
Definition: compare.c:799
static l_ok pixCompareTilesByHisto(PIX *pix1, PIX *pix2, l_int32 maxgray, l_int32 factor, l_int32 n, l_float32 *pscore, PIXA *pixadebug)
pixCompareTilesByHisto()
Definition: compare.c:3035
l_ok pixAlphaIsOpaque(PIX *pix, l_int32 *popaque)
pixAlphaIsOpaque()
Definition: pix2.c:3423
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:282
l_ok pixEqual(PIX *pix1, PIX *pix2, l_int32 *psame)
pixEqual()
Definition: compare.c:156
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
NUMA * numaNormalizeHistogram(NUMA *nas, l_float32 tsum)
numaNormalizeHistogram()
Definition: numafunc2.c:1179
PIX * pixAddTextlines(PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location)
pixAddTextlines()
Definition: textops.c:276
PIX * pixMinOrMax(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 type)
pixMinOrMax()
Definition: pixarith.c:1152
l_ok pixSetResolution(PIX *pix, l_int32 xres, l_int32 yres)
pixSetResolution()
Definition: pix1.c:1387
l_ok pixGetDifferenceStats(PIX *pix1, PIX *pix2, l_int32 factor, l_int32 mindiff, l_float32 *pfractdiff, l_float32 *pavediff, l_int32 details)
pixGetDifferenceStats()
Definition: compare.c:1405
l_ok numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:496
l_ok pixcmapGetRGBA(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *paval)
pixcmapGetRGBA()
Definition: colormap.c:892
l_ok pixaccAdd(PIXACC *pixacc, PIX *pix)
pixaccAdd()
Definition: pixacc.c:254
l_ok pixGetPerceptualDiff(PIX *pixs1, PIX *pixs2, l_int32 sampling, l_int32 dilation, l_int32 mindiff, l_float32 *pfract, PIX **ppixdiff1, PIX **ppixdiff2)
pixGetPerceptualDiff()
Definition: compare.c:1638
l_int32 lept_rmdir(const char *subdir)
lept_rmdir()
Definition: utils2.c:2295
l_int32 pixSizesEqual(const PIX *pix1, const PIX *pix2)
pixSizesEqual()
Definition: pix1.c:1985
l_ok pixEqualWithCmap(PIX *pix1, PIX *pix2, l_int32 *psame)
pixEqualWithCmap()
Definition: compare.c:384
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:292
l_ok boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:313
Definition: pix.h:480
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:412
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2820
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:172
l_ok grayInterHistogramStats(NUMAA *naa, l_int32 wc, NUMA **pnam, NUMA **pnams, NUMA **pnav, NUMA **pnarv)
grayInterHistogramStats()
Definition: numafunc2.c:2342
l_uint8 * makeSubsampleTab2x(void)
makeSubsampleTab2x()
Definition: binreduce.c:391
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:650
l_ok numaGetSumOnInterval(NUMA *na, l_int32 first, l_int32 last, l_float32 *psum)
numaGetSumOnInterval()
Definition: numafunc1.c:613
l_ok numaWriteStderr(NUMA *na)
numaWriteStderr()
Definition: numabasic.c:1313
l_ok gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:665
l_ok numaaAddNuma(NUMAA *naa, NUMA *na, l_int32 copyflag)
numaaAddNuma()
Definition: numabasic.c:1546
Definition: pix.h:578
PIX * pixColorMorph(PIX *pixs, l_int32 type, l_int32 hsize, l_int32 vsize)
pixColorMorph()
Definition: colormorph.c:69
l_ok pixGetPSNR(PIX *pix1, PIX *pix2, l_int32 factor, l_float32 *ppsnr)
pixGetPSNR()
Definition: compare.c:1805
L_BMF * bmfCreate(const char *dir, l_int32 fontsize)
bmfCreate()
Definition: bmf.c:117