Leptonica  1.77.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 
104 #include <string.h>
105 #include <math.h>
106 #include "allheaders.h"
107 
108  /* Small enough to consider equal to 0.0, for plot output */
109 static const l_float32 TINY = 0.00001;
110 
111 static l_int32 pixCompareTilesByHisto(PIX *pix1, PIX *pix2, l_int32 maxgray,
112  l_int32 factor, l_int32 nx, l_int32 ny,
113  l_float32 *pscore, PIXA *pixadebug);
114 
115 
116 /*------------------------------------------------------------------*
117  * Test for pix equality *
118  *------------------------------------------------------------------*/
149 l_ok
150 pixEqual(PIX *pix1,
151  PIX *pix2,
152  l_int32 *psame)
153 {
154  return pixEqualWithAlpha(pix1, pix2, 0, psame);
155 }
156 
157 
175 l_ok
177  PIX *pix2,
178  l_int32 use_alpha,
179  l_int32 *psame)
180 {
181 l_int32 w1, h1, d1, w2, h2, d2, wpl1, wpl2;
182 l_int32 spp1, spp2, i, j, color, mismatch, opaque;
183 l_int32 fullwords, linebits, endbits;
184 l_uint32 endmask, wordmask;
185 l_uint32 *data1, *data2, *line1, *line2;
186 PIX *pixs1, *pixs2, *pixt1, *pixt2, *pixalpha;
187 PIXCMAP *cmap1, *cmap2;
188 
189  PROCNAME("pixEqualWithAlpha");
190 
191  if (!psame)
192  return ERROR_INT("psame not defined", procName, 1);
193  *psame = 0; /* init to not equal */
194  if (!pix1 || !pix2)
195  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
196  pixGetDimensions(pix1, &w1, &h1, &d1);
197  pixGetDimensions(pix2, &w2, &h2, &d2);
198  if (w1 != w2 || h1 != h2) {
199  L_INFO("pix sizes differ\n", procName);
200  return 0;
201  }
202 
203  /* Suppose the use_alpha flag is true.
204  * If only one of two 32 bpp images has spp == 4, we call that
205  * a "mismatch" of the alpha component. In the case of a mismatch,
206  * if the 4 bpp pix does not have all alpha components opaque (255),
207  * the images are not-equal. However if they are all opaque,
208  * this image is equivalent to spp == 3, so we allow the
209  * comparison to go forward, testing only for the RGB equality. */
210  spp1 = pixGetSpp(pix1);
211  spp2 = pixGetSpp(pix2);
212  mismatch = 0;
213  if (use_alpha && d1 == 32 && d2 == 32) {
214  mismatch = ((spp1 == 4 && spp2 != 4) || (spp1 != 4 && spp2 == 4));
215  if (mismatch) {
216  pixalpha = (spp1 == 4) ? pix1 : pix2;
217  pixAlphaIsOpaque(pixalpha, &opaque);
218  if (!opaque) {
219  L_INFO("just one pix has a non-opaque alpha layer\n", procName);
220  return 0;
221  }
222  }
223  }
224 
225  cmap1 = pixGetColormap(pix1);
226  cmap2 = pixGetColormap(pix2);
227  if (!cmap1 && !cmap2 && (d1 != d2) && (d1 == 32 || d2 == 32)) {
228  L_INFO("no colormaps, pix depths unequal, and one of them is RGB\n",
229  procName);
230  return 0;
231  }
232 
233  if (cmap1 && cmap2 && (d1 == d2)) /* use special function */
234  return pixEqualWithCmap(pix1, pix2, psame);
235 
236  /* Must remove colormaps if they exist, and in the process
237  * end up with the resulting images having the same depth. */
238  if (cmap1 && !cmap2) {
239  pixUsesCmapColor(pix1, &color);
240  if (color && d2 <= 8) /* can't be equal */
241  return 0;
242  if (d2 < 8)
243  pixs2 = pixConvertTo8(pix2, FALSE);
244  else
245  pixs2 = pixClone(pix2);
246  if (d2 <= 8)
248  else
250  } else if (!cmap1 && cmap2) {
251  pixUsesCmapColor(pix2, &color);
252  if (color && d1 <= 8) /* can't be equal */
253  return 0;
254  if (d1 < 8)
255  pixs1 = pixConvertTo8(pix1, FALSE);
256  else
257  pixs1 = pixClone(pix1);
258  if (d1 <= 8)
260  else
262  } else if (cmap1 && cmap2) { /* depths not equal; use rgb */
265  } else { /* no colormaps */
266  pixs1 = pixClone(pix1);
267  pixs2 = pixClone(pix2);
268  }
269 
270  /* OK, we have no colormaps, but the depths may still be different */
271  d1 = pixGetDepth(pixs1);
272  d2 = pixGetDepth(pixs2);
273  if (d1 != d2) {
274  if (d1 == 16 || d2 == 16) {
275  L_INFO("one pix is 16 bpp\n", procName);
276  pixDestroy(&pixs1);
277  pixDestroy(&pixs2);
278  return 0;
279  }
280  pixt1 = pixConvertLossless(pixs1, 8);
281  pixt2 = pixConvertLossless(pixs2, 8);
282  if (!pixt1 || !pixt2) {
283  L_INFO("failure to convert to 8 bpp\n", procName);
284  pixDestroy(&pixs1);
285  pixDestroy(&pixs2);
286  pixDestroy(&pixt1);
287  pixDestroy(&pixt2);
288  return 0;
289  }
290  } else {
291  pixt1 = pixClone(pixs1);
292  pixt2 = pixClone(pixs2);
293  }
294  pixDestroy(&pixs1);
295  pixDestroy(&pixs2);
296 
297  /* No colormaps, equal depths; do pixel comparisons */
298  d1 = pixGetDepth(pixt1);
299  d2 = pixGetDepth(pixt2);
300  wpl1 = pixGetWpl(pixt1);
301  wpl2 = pixGetWpl(pixt2);
302  data1 = pixGetData(pixt1);
303  data2 = pixGetData(pixt2);
304 
305  if (d1 == 32) { /* test either RGB or RGBA pixels */
306  if (use_alpha && !mismatch)
307  wordmask = (spp1 == 3) ? 0xffffff00 : 0xffffffff;
308  else
309  wordmask = 0xffffff00;
310  for (i = 0; i < h1; i++) {
311  line1 = data1 + wpl1 * i;
312  line2 = data2 + wpl2 * i;
313  for (j = 0; j < wpl1; j++) {
314  if ((*line1 ^ *line2) & wordmask) {
315  pixDestroy(&pixt1);
316  pixDestroy(&pixt2);
317  return 0;
318  }
319  line1++;
320  line2++;
321  }
322  }
323  } else { /* all bits count */
324  linebits = d1 * w1;
325  fullwords = linebits / 32;
326  endbits = linebits & 31;
327  endmask = (endbits == 0) ? 0 : (0xffffffff << (32 - endbits));
328  for (i = 0; i < h1; i++) {
329  line1 = data1 + wpl1 * i;
330  line2 = data2 + wpl2 * i;
331  for (j = 0; j < fullwords; j++) {
332  if (*line1 ^ *line2) {
333  pixDestroy(&pixt1);
334  pixDestroy(&pixt2);
335  return 0;
336  }
337  line1++;
338  line2++;
339  }
340  if (endbits) {
341  if ((*line1 ^ *line2) & endmask) {
342  pixDestroy(&pixt1);
343  pixDestroy(&pixt2);
344  return 0;
345  }
346  }
347  }
348  }
349 
350  pixDestroy(&pixt1);
351  pixDestroy(&pixt2);
352  *psame = 1;
353  return 0;
354 }
355 
356 
377 l_ok
379  PIX *pix2,
380  l_int32 *psame)
381 {
382 l_int32 d, w, h, wpl1, wpl2, i, j, linebits, fullwords, endbits;
383 l_int32 rval1, rval2, gval1, gval2, bval1, bval2, samecmaps;
384 l_uint32 endmask, val1, val2;
385 l_uint32 *data1, *data2, *line1, *line2;
386 PIXCMAP *cmap1, *cmap2;
387 
388  PROCNAME("pixEqualWithCmap");
389 
390  if (!psame)
391  return ERROR_INT("&same not defined", procName, 1);
392  *psame = 0;
393  if (!pix1)
394  return ERROR_INT("pix1 not defined", procName, 1);
395  if (!pix2)
396  return ERROR_INT("pix2 not defined", procName, 1);
397 
398  if (pixSizesEqual(pix1, pix2) == 0)
399  return 0;
400  cmap1 = pixGetColormap(pix1);
401  cmap2 = pixGetColormap(pix2);
402  if (!cmap1 || !cmap2) {
403  L_INFO("both images don't have colormap\n", procName);
404  return 0;
405  }
406  pixGetDimensions(pix1, &w, &h, &d);
407  if (d != 1 && d != 2 && d != 4 && d != 8) {
408  L_INFO("pix depth not in {1, 2, 4, 8}\n", procName);
409  return 0;
410  }
411 
412  cmapEqual(cmap1, cmap2, 3, &samecmaps);
413  if (samecmaps == TRUE) { /* colormaps are identical; compare by words */
414  linebits = d * w;
415  wpl1 = pixGetWpl(pix1);
416  wpl2 = pixGetWpl(pix2);
417  data1 = pixGetData(pix1);
418  data2 = pixGetData(pix2);
419  fullwords = linebits / 32;
420  endbits = linebits & 31;
421  endmask = (endbits == 0) ? 0 : (0xffffffff << (32 - endbits));
422  for (i = 0; i < h; i++) {
423  line1 = data1 + wpl1 * i;
424  line2 = data2 + wpl2 * i;
425  for (j = 0; j < fullwords; j++) {
426  if (*line1 ^ *line2)
427  return 0;
428  line1++;
429  line2++;
430  }
431  if (endbits) {
432  if ((*line1 ^ *line2) & endmask)
433  return 0;
434  }
435  }
436  *psame = 1;
437  return 0;
438  }
439 
440  /* Colormaps aren't identical; compare pixel by pixel */
441  for (i = 0; i < h; i++) {
442  for (j = 0; j < w; j++) {
443  pixGetPixel(pix1, j, i, &val1);
444  pixGetPixel(pix2, j, i, &val2);
445  pixcmapGetColor(cmap1, val1, &rval1, &gval1, &bval1);
446  pixcmapGetColor(cmap2, val2, &rval2, &gval2, &bval2);
447  if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2)
448  return 0;
449  }
450  }
451 
452  *psame = 1;
453  return 0;
454 }
455 
456 
473 l_ok
475  PIXCMAP *cmap2,
476  l_int32 ncomps,
477  l_int32 *psame)
478 {
479 l_int32 n1, n2, i, rval1, rval2, gval1, gval2, bval1, bval2, aval1, aval2;
480 
481  PROCNAME("cmapEqual");
482 
483  if (!psame)
484  return ERROR_INT("&same not defined", procName, 1);
485  *psame = FALSE;
486  if (!cmap1)
487  return ERROR_INT("cmap1 not defined", procName, 1);
488  if (!cmap2)
489  return ERROR_INT("cmap2 not defined", procName, 1);
490  if (ncomps != 3 && ncomps != 4)
491  return ERROR_INT("ncomps not 3 or 4", procName, 1);
492 
493  n1 = pixcmapGetCount(cmap1);
494  n2 = pixcmapGetCount(cmap2);
495  if (n1 != n2) {
496  L_INFO("colormap sizes are different\n", procName);
497  return 0;
498  }
499 
500  for (i = 0; i < n1; i++) {
501  pixcmapGetRGBA(cmap1, i, &rval1, &gval1, &bval1, &aval1);
502  pixcmapGetRGBA(cmap2, i, &rval2, &gval2, &bval2, &aval2);
503  if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2)
504  return 0;
505  if (ncomps == 4 && aval1 != aval2)
506  return 0;
507  }
508  *psame = TRUE;
509  return 0;
510 }
511 
512 
531 l_ok
533  l_int32 *pcolor)
534 {
535 l_int32 n, i, rval, gval, bval, numpix;
536 NUMA *na;
537 PIXCMAP *cmap;
538 
539  PROCNAME("pixUsesCmapColor");
540 
541  if (!pcolor)
542  return ERROR_INT("&color not defined", procName, 1);
543  *pcolor = 0;
544  if (!pixs)
545  return ERROR_INT("pixs not defined", procName, 1);
546 
547  if ((cmap = pixGetColormap(pixs)) == NULL)
548  return 0;
549 
550  pixcmapHasColor(cmap, pcolor);
551  if (*pcolor == 0) /* no color */
552  return 0;
553 
554  /* The cmap has color entries. Are they used? */
555  na = pixGetGrayHistogram(pixs, 1);
556  n = pixcmapGetCount(cmap);
557  for (i = 0; i < n; i++) {
558  pixcmapGetColor(cmap, i, &rval, &gval, &bval);
559  numaGetIValue(na, i, &numpix);
560  if ((rval != gval || rval != bval) && numpix) { /* color found! */
561  *pcolor = 1;
562  break;
563  }
564  }
565  numaDestroy(&na);
566 
567  return 0;
568 }
569 
570 
571 /*------------------------------------------------------------------*
572  * Binary correlation *
573  *------------------------------------------------------------------*/
597 l_ok
599  PIX *pix2,
600  l_float32 *pval)
601 {
602 l_int32 count1, count2, countn;
603 l_int32 *tab8;
604 PIX *pixn;
605 
606  PROCNAME("pixCorrelationBinary");
607 
608  if (!pval)
609  return ERROR_INT("&pval not defined", procName, 1);
610  *pval = 0.0;
611  if (!pix1)
612  return ERROR_INT("pix1 not defined", procName, 1);
613  if (!pix2)
614  return ERROR_INT("pix2 not defined", procName, 1);
615 
616  tab8 = makePixelSumTab8();
617  pixCountPixels(pix1, &count1, tab8);
618  pixCountPixels(pix2, &count2, tab8);
619  if (count1 == 0 || count2 == 0) {
620  LEPT_FREE(tab8);
621  return 0;
622  }
623  pixn = pixAnd(NULL, pix1, pix2);
624  pixCountPixels(pixn, &countn, tab8);
625  *pval = (l_float32)countn * (l_float32)countn /
626  ((l_float32)count1 * (l_float32)count2);
627  LEPT_FREE(tab8);
628  pixDestroy(&pixn);
629  return 0;
630 }
631 
632 
633 /*------------------------------------------------------------------*
634  * Difference of two images *
635  *------------------------------------------------------------------*/
655 PIX *
657  PIX *pix2)
658 {
659 l_int32 w1, h1, d1, w2, h2, d2, minw, minh;
660 PIX *pixt, *pixd;
661 PIXCMAP *cmap;
662 
663  PROCNAME("pixDisplayDiffBinary");
664 
665  if (!pix1 || !pix2)
666  return (PIX *)ERROR_PTR("pix1, pix2 not both defined", procName, NULL);
667  pixGetDimensions(pix1, &w1, &h1, &d1);
668  pixGetDimensions(pix2, &w2, &h2, &d2);
669  if (d1 != 1 || d2 != 1)
670  return (PIX *)ERROR_PTR("pix1 and pix2 not 1 bpp", procName, NULL);
671  minw = L_MIN(w1, w2);
672  minh = L_MIN(h1, h2);
673 
674  pixd = pixCreate(minw, minh, 4);
675  cmap = pixcmapCreate(4);
676  pixcmapAddColor(cmap, 255, 255, 255); /* initialized to white */
677  pixcmapAddColor(cmap, 0, 0, 0);
678  pixcmapAddColor(cmap, 255, 0, 0);
679  pixcmapAddColor(cmap, 0, 255, 0);
680  pixSetColormap(pixd, cmap);
681 
682  pixt = pixAnd(NULL, pix1, pix2);
683  pixPaintThroughMask(pixd, pixt, 0, 0, 0x0); /* black */
684  pixSubtract(pixt, pix1, pix2);
685  pixPaintThroughMask(pixd, pixt, 0, 0, 0xff000000); /* red */
686  pixSubtract(pixt, pix2, pix1);
687  pixPaintThroughMask(pixd, pixt, 0, 0, 0x00ff0000); /* green */
688  pixDestroy(&pixt);
689  return pixd;
690 }
691 
692 
711 l_ok
713  PIX *pix2,
714  l_int32 comptype,
715  l_float32 *pfract,
716  PIX **ppixdiff)
717 {
718 l_int32 w, h, count;
719 PIX *pixt;
720 
721  PROCNAME("pixCompareBinary");
722 
723  if (ppixdiff) *ppixdiff = NULL;
724  if (!pfract)
725  return ERROR_INT("&pfract not defined", procName, 1);
726  *pfract = 0.0;
727  if (!pix1 || pixGetDepth(pix1) != 1)
728  return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1);
729  if (!pix2 || pixGetDepth(pix2) != 1)
730  return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1);
731  if (comptype != L_COMPARE_XOR && comptype != L_COMPARE_SUBTRACT)
732  return ERROR_INT("invalid comptype", procName, 1);
733 
734  if (comptype == L_COMPARE_XOR)
735  pixt = pixXor(NULL, pix1, pix2);
736  else /* comptype == L_COMPARE_SUBTRACT) */
737  pixt = pixSubtract(NULL, pix1, pix2);
738  pixCountPixels(pixt, &count, NULL);
739  pixGetDimensions(pix1, &w, &h, NULL);
740  *pfract = (l_float32)(count) / (l_float32)(w * h);
741 
742  if (ppixdiff)
743  *ppixdiff = pixt;
744  else
745  pixDestroy(&pixt);
746  return 0;
747 }
748 
749 
787 l_ok
789  PIX *pix2,
790  l_int32 comptype,
791  l_int32 plottype,
792  l_int32 *psame,
793  l_float32 *pdiff,
794  l_float32 *prmsdiff,
795  PIX **ppixdiff)
796 {
797 l_int32 retval, d;
798 PIX *pixt1, *pixt2;
799 
800  PROCNAME("pixCompareGrayOrRGB");
801 
802  if (ppixdiff) *ppixdiff = NULL;
803  if (!pix1)
804  return ERROR_INT("pix1 not defined", procName, 1);
805  if (!pix2)
806  return ERROR_INT("pix2 not defined", procName, 1);
807  if (pixGetDepth(pix1) < 8 && !pixGetColormap(pix1))
808  return ERROR_INT("pix1 depth < 8 bpp and not cmapped", procName, 1);
809  if (pixGetDepth(pix2) < 8 && !pixGetColormap(pix2))
810  return ERROR_INT("pix2 depth < 8 bpp and not cmapped", procName, 1);
811  if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF)
812  return ERROR_INT("invalid comptype", procName, 1);
813  if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS)
814  return ERROR_INT("invalid plottype", procName, 1);
815 
818  d = pixGetDepth(pixt1);
819  if (d != pixGetDepth(pixt2)) {
820  pixDestroy(&pixt1);
821  pixDestroy(&pixt2);
822  return ERROR_INT("intrinsic depths are not equal", procName, 1);
823  }
824 
825  if (d == 8 || d == 16)
826  retval = pixCompareGray(pixt1, pixt2, comptype, plottype, psame,
827  pdiff, prmsdiff, ppixdiff);
828  else /* d == 32 */
829  retval = pixCompareRGB(pixt1, pixt2, comptype, plottype, psame,
830  pdiff, prmsdiff, ppixdiff);
831  pixDestroy(&pixt1);
832  pixDestroy(&pixt2);
833  return retval;
834 }
835 
836 
858 l_ok
860  PIX *pix2,
861  l_int32 comptype,
862  l_int32 plottype,
863  l_int32 *psame,
864  l_float32 *pdiff,
865  l_float32 *prmsdiff,
866  PIX **ppixdiff)
867 {
868 char buf[64];
869 static l_int32 index = 0;
870 l_int32 d1, d2, same, first, last;
871 GPLOT *gplot;
872 NUMA *na, *nac;
873 PIX *pixt;
874 
875  PROCNAME("pixCompareGray");
876 
877  if (psame) *psame = 0;
878  if (pdiff) *pdiff = 0.0;
879  if (prmsdiff) *prmsdiff = 0.0;
880  if (ppixdiff) *ppixdiff = NULL;
881  if (!pix1)
882  return ERROR_INT("pix1 not defined", procName, 1);
883  if (!pix2)
884  return ERROR_INT("pix2 not defined", procName, 1);
885  d1 = pixGetDepth(pix1);
886  d2 = pixGetDepth(pix2);
887  if ((d1 != d2) || (d1 != 8 && d1 != 16))
888  return ERROR_INT("depths unequal or not 8 or 16 bpp", procName, 1);
889  if (pixGetColormap(pix1) || pixGetColormap(pix2))
890  return ERROR_INT("pix1 and/or pix2 are colormapped", procName, 1);
891  if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF)
892  return ERROR_INT("invalid comptype", procName, 1);
893  if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS)
894  return ERROR_INT("invalid plottype", procName, 1);
895 
896  lept_mkdir("lept/comp");
897 
898  if (comptype == L_COMPARE_SUBTRACT)
899  pixt = pixSubtractGray(NULL, pix1, pix2);
900  else /* comptype == L_COMPARE_ABS_DIFF) */
901  pixt = pixAbsDifference(pix1, pix2);
902 
903  pixZero(pixt, &same);
904  if (same)
905  L_INFO("Images are pixel-wise identical\n", procName);
906  if (psame) *psame = same;
907 
908  if (pdiff)
909  pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_MEAN_ABSVAL, pdiff);
910 
911  /* Don't bother to plot if the images are the same */
912  if (plottype && !same) {
913  L_INFO("Images differ: output plots will be generated\n", procName);
914  na = pixGetGrayHistogram(pixt, 1);
915  numaGetNonzeroRange(na, TINY, &first, &last);
916  nac = numaClipToInterval(na, 0, last);
917  snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_gray%d", index);
918  gplot = gplotCreate(buf, plottype,
919  "Pixel Difference Histogram", "diff val",
920  "number of pixels");
921  gplotAddPlot(gplot, NULL, nac, GPLOT_LINES, "gray");
922  gplotMakeOutput(gplot);
923  gplotDestroy(&gplot);
924  snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_gray%d.png",
925  index++);
926  l_fileDisplay(buf, 100, 100, 1.0);
927  numaDestroy(&na);
928  numaDestroy(&nac);
929  }
930 
931  if (ppixdiff)
932  *ppixdiff = pixCopy(NULL, pixt);
933 
934  if (prmsdiff) {
935  if (comptype == L_COMPARE_SUBTRACT) { /* wrong type for rms diff */
936  pixDestroy(&pixt);
937  pixt = pixAbsDifference(pix1, pix2);
938  }
939  pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, prmsdiff);
940  }
941 
942  pixDestroy(&pixt);
943  return 0;
944 }
945 
946 
967 l_ok
969  PIX *pix2,
970  l_int32 comptype,
971  l_int32 plottype,
972  l_int32 *psame,
973  l_float32 *pdiff,
974  l_float32 *prmsdiff,
975  PIX **ppixdiff)
976 {
977 char buf[64];
978 static l_int32 index = 0;
979 l_int32 rsame, gsame, bsame, same, first, rlast, glast, blast, last;
980 l_float32 rdiff, gdiff, bdiff;
981 GPLOT *gplot;
982 NUMA *nar, *nag, *nab, *narc, *nagc, *nabc;
983 PIX *pixr1, *pixr2, *pixg1, *pixg2, *pixb1, *pixb2;
984 PIX *pixr, *pixg, *pixb;
985 
986  PROCNAME("pixCompareRGB");
987 
988  if (psame) *psame = 0;
989  if (pdiff) *pdiff = 0.0;
990  if (prmsdiff) *prmsdiff = 0.0;
991  if (ppixdiff) *ppixdiff = NULL;
992  if (!pix1 || pixGetDepth(pix1) != 32)
993  return ERROR_INT("pix1 not defined or not 32 bpp", procName, 1);
994  if (!pix2 || pixGetDepth(pix2) != 32)
995  return ERROR_INT("pix2 not defined or not ew bpp", procName, 1);
996  if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF)
997  return ERROR_INT("invalid comptype", procName, 1);
998  if (plottype < 0 || plottype >= NUM_GPLOT_OUTPUTS)
999  return ERROR_INT("invalid plottype", procName, 1);
1000 
1001  lept_mkdir("lept/comp");
1002 
1003  pixr1 = pixGetRGBComponent(pix1, COLOR_RED);
1004  pixr2 = pixGetRGBComponent(pix2, COLOR_RED);
1005  pixg1 = pixGetRGBComponent(pix1, COLOR_GREEN);
1006  pixg2 = pixGetRGBComponent(pix2, COLOR_GREEN);
1007  pixb1 = pixGetRGBComponent(pix1, COLOR_BLUE);
1008  pixb2 = pixGetRGBComponent(pix2, COLOR_BLUE);
1009  if (comptype == L_COMPARE_SUBTRACT) {
1010  pixr = pixSubtractGray(NULL, pixr1, pixr2);
1011  pixg = pixSubtractGray(NULL, pixg1, pixg2);
1012  pixb = pixSubtractGray(NULL, pixb1, pixb2);
1013  } else { /* comptype == L_COMPARE_ABS_DIFF) */
1014  pixr = pixAbsDifference(pixr1, pixr2);
1015  pixg = pixAbsDifference(pixg1, pixg2);
1016  pixb = pixAbsDifference(pixb1, pixb2);
1017  }
1018 
1019  pixZero(pixr, &rsame);
1020  pixZero(pixg, &gsame);
1021  pixZero(pixb, &bsame);
1022  same = rsame && gsame && bsame;
1023  if (same)
1024  L_INFO("Images are pixel-wise identical\n", procName);
1025  if (psame) *psame = same;
1026 
1027  if (pdiff) {
1028  pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_MEAN_ABSVAL, &rdiff);
1029  pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &gdiff);
1030  pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_MEAN_ABSVAL, &bdiff);
1031  *pdiff = (rdiff + gdiff + bdiff) / 3.0;
1032  }
1033 
1034  /* Don't bother to plot if the images are the same */
1035  if (plottype && !same) {
1036  L_INFO("Images differ: output plots will be generated\n", procName);
1037  nar = pixGetGrayHistogram(pixr, 1);
1038  nag = pixGetGrayHistogram(pixg, 1);
1039  nab = pixGetGrayHistogram(pixb, 1);
1040  numaGetNonzeroRange(nar, TINY, &first, &rlast);
1041  numaGetNonzeroRange(nag, TINY, &first, &glast);
1042  numaGetNonzeroRange(nab, TINY, &first, &blast);
1043  last = L_MAX(rlast, glast);
1044  last = L_MAX(last, blast);
1045  narc = numaClipToInterval(nar, 0, last);
1046  nagc = numaClipToInterval(nag, 0, last);
1047  nabc = numaClipToInterval(nab, 0, last);
1048  snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_rgb%d", index);
1049  gplot = gplotCreate(buf, plottype,
1050  "Pixel Difference Histogram", "diff val",
1051  "number of pixels");
1052  gplotAddPlot(gplot, NULL, narc, GPLOT_LINES, "red");
1053  gplotAddPlot(gplot, NULL, nagc, GPLOT_LINES, "green");
1054  gplotAddPlot(gplot, NULL, nabc, GPLOT_LINES, "blue");
1055  gplotMakeOutput(gplot);
1056  gplotDestroy(&gplot);
1057  snprintf(buf, sizeof(buf), "/tmp/lept/comp/compare_rgb%d.png",
1058  index++);
1059  l_fileDisplay(buf, 100, 100, 1.0);
1060  numaDestroy(&nar);
1061  numaDestroy(&nag);
1062  numaDestroy(&nab);
1063  numaDestroy(&narc);
1064  numaDestroy(&nagc);
1065  numaDestroy(&nabc);
1066  }
1067 
1068  if (ppixdiff)
1069  *ppixdiff = pixCreateRGBImage(pixr, pixg, pixb);
1070 
1071  if (prmsdiff) {
1072  if (comptype == L_COMPARE_SUBTRACT) {
1073  pixDestroy(&pixr);
1074  pixDestroy(&pixg);
1075  pixDestroy(&pixb);
1076  pixr = pixAbsDifference(pixr1, pixr2);
1077  pixg = pixAbsDifference(pixg1, pixg2);
1078  pixb = pixAbsDifference(pixb1, pixb2);
1079  }
1080  pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &rdiff);
1081  pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &gdiff);
1082  pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &bdiff);
1083  *prmsdiff = (rdiff + gdiff + bdiff) / 3.0;
1084  }
1085 
1086  pixDestroy(&pixr1);
1087  pixDestroy(&pixr2);
1088  pixDestroy(&pixg1);
1089  pixDestroy(&pixg2);
1090  pixDestroy(&pixb1);
1091  pixDestroy(&pixb2);
1092  pixDestroy(&pixr);
1093  pixDestroy(&pixg);
1094  pixDestroy(&pixb);
1095  return 0;
1096 }
1097 
1098 
1123 l_ok
1125  PIX *pix2,
1126  l_int32 sx,
1127  l_int32 sy,
1128  l_int32 type,
1129  PIX **ppixdiff)
1130 {
1131 l_int32 d1, d2, w, h;
1132 PIX *pixt, *pixr, *pixg, *pixb;
1133 PIX *pixrdiff, *pixgdiff, *pixbdiff;
1134 PIXACC *pixacc;
1135 
1136  PROCNAME("pixCompareTiled");
1137 
1138  if (!ppixdiff)
1139  return ERROR_INT("&pixdiff not defined", procName, 1);
1140  *ppixdiff = NULL;
1141  if (!pix1)
1142  return ERROR_INT("pix1 not defined", procName, 1);
1143  if (!pix2)
1144  return ERROR_INT("pix2 not defined", procName, 1);
1145  d1 = pixGetDepth(pix1);
1146  d2 = pixGetDepth(pix2);
1147  if (d1 != d2)
1148  return ERROR_INT("depths not equal", procName, 1);
1149  if (d1 != 8 && d1 != 32)
1150  return ERROR_INT("pix1 not 8 or 32 bpp", procName, 1);
1151  if (d2 != 8 && d2 != 32)
1152  return ERROR_INT("pix2 not 8 or 32 bpp", procName, 1);
1153  if (sx < 2 || sy < 2)
1154  return ERROR_INT("sx and sy not both > 1", procName, 1);
1155  if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE)
1156  return ERROR_INT("invalid type", procName, 1);
1157 
1158  pixt = pixAbsDifference(pix1, pix2);
1159  if (d1 == 8) {
1160  *ppixdiff = pixGetAverageTiled(pixt, sx, sy, type);
1161  } else { /* d1 == 32 */
1162  pixr = pixGetRGBComponent(pixt, COLOR_RED);
1163  pixg = pixGetRGBComponent(pixt, COLOR_GREEN);
1164  pixb = pixGetRGBComponent(pixt, COLOR_BLUE);
1165  pixrdiff = pixGetAverageTiled(pixr, sx, sy, type);
1166  pixgdiff = pixGetAverageTiled(pixg, sx, sy, type);
1167  pixbdiff = pixGetAverageTiled(pixb, sx, sy, type);
1168  pixGetDimensions(pixrdiff, &w, &h, NULL);
1169  pixacc = pixaccCreate(w, h, 0);
1170  pixaccAdd(pixacc, pixrdiff);
1171  pixaccAdd(pixacc, pixgdiff);
1172  pixaccAdd(pixacc, pixbdiff);
1173  pixaccMultConst(pixacc, 1. / 3.);
1174  *ppixdiff = pixaccFinal(pixacc, 8);
1175  pixDestroy(&pixr);
1176  pixDestroy(&pixg);
1177  pixDestroy(&pixb);
1178  pixDestroy(&pixrdiff);
1179  pixDestroy(&pixgdiff);
1180  pixDestroy(&pixbdiff);
1181  pixaccDestroy(&pixacc);
1182  }
1183  pixDestroy(&pixt);
1184  return 0;
1185 }
1186 
1187 
1188 /*------------------------------------------------------------------*
1189  * Other measures of the difference of two images *
1190  *------------------------------------------------------------------*/
1217 NUMA *
1219  PIX *pix2,
1220  l_int32 factor)
1221 {
1222 l_int32 i;
1223 l_float32 *array1, *array2;
1224 NUMA *nah, *nan, *nad;
1225 
1226  PROCNAME("pixCompareRankDifference");
1227 
1228  if (!pix1)
1229  return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL);
1230  if (!pix2)
1231  return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL);
1232 
1233  if ((nah = pixGetDifferenceHistogram(pix1, pix2, factor)) == NULL)
1234  return (NUMA *)ERROR_PTR("na not made", procName, NULL);
1235 
1236  nan = numaNormalizeHistogram(nah, 1.0);
1237  array1 = numaGetFArray(nan, L_NOCOPY);
1238 
1239  nad = numaCreate(256);
1240  numaSetCount(nad, 256); /* all initialized to 0.0 */
1241  array2 = numaGetFArray(nad, L_NOCOPY);
1242 
1243  /* Do rank accumulation on normalized histo of diffs */
1244  array2[0] = 1.0;
1245  for (i = 1; i < 256; i++)
1246  array2[i] = array2[i - 1] - array1[i - 1];
1247 
1248  numaDestroy(&nah);
1249  numaDestroy(&nan);
1250  return nad;
1251 }
1252 
1253 
1302 l_ok
1304  PIX *pix2,
1305  l_int32 factor,
1306  l_int32 mindiff,
1307  l_float32 maxfract,
1308  l_float32 maxave,
1309  l_int32 *psimilar,
1310  l_int32 details)
1311 {
1312 l_float32 fractdiff, avediff;
1313 
1314  PROCNAME("pixTestForSimilarity");
1315 
1316  if (!psimilar)
1317  return ERROR_INT("&similar not defined", procName, 1);
1318  *psimilar = 0;
1319  if (!pix1)
1320  return ERROR_INT("pix1 not defined", procName, 1);
1321  if (!pix2)
1322  return ERROR_INT("pix2 not defined", procName, 1);
1323  if (pixSizesEqual(pix1, pix2) == 0)
1324  return ERROR_INT("pix sizes not equal", procName, 1);
1325  if (mindiff <= 0)
1326  return ERROR_INT("mindiff must be > 0", procName, 1);
1327 
1328  if (pixGetDifferenceStats(pix1, pix2, factor, mindiff,
1329  &fractdiff, &avediff, details))
1330  return ERROR_INT("diff stats not found", procName, 1);
1331 
1332  if (maxave <= 0.0) maxave = 256.0;
1333  if (fractdiff <= maxfract && avediff <= maxave)
1334  *psimilar = 1;
1335  return 0;
1336 }
1337 
1338 
1381 l_ok
1383  PIX *pix2,
1384  l_int32 factor,
1385  l_int32 mindiff,
1386  l_float32 *pfractdiff,
1387  l_float32 *pavediff,
1388  l_int32 details)
1389 {
1390 l_int32 i, first, last, diff;
1391 l_float32 fract, ave;
1392 l_float32 *array;
1393 NUMA *nah, *nan, *nac;
1394 
1395  PROCNAME("pixGetDifferenceStats");
1396 
1397  if (pfractdiff) *pfractdiff = 0.0;
1398  if (pavediff) *pavediff = 0.0;
1399  if (!pfractdiff)
1400  return ERROR_INT("&fractdiff not defined", procName, 1);
1401  if (!pavediff)
1402  return ERROR_INT("&avediff not defined", procName, 1);
1403  if (!pix1)
1404  return ERROR_INT("pix1 not defined", procName, 1);
1405  if (!pix2)
1406  return ERROR_INT("pix2 not defined", procName, 1);
1407  if (mindiff <= 0)
1408  return ERROR_INT("mindiff must be > 0", procName, 1);
1409 
1410  if ((nah = pixGetDifferenceHistogram(pix1, pix2, factor)) == NULL)
1411  return ERROR_INT("na not made", procName, 1);
1412 
1413  if ((nan = numaNormalizeHistogram(nah, 1.0)) == NULL) {
1414  numaDestroy(&nah);
1415  return ERROR_INT("nan not made", procName, 1);
1416  }
1417  array = numaGetFArray(nan, L_NOCOPY);
1418 
1419  if (details) {
1420  lept_mkdir("lept/comp");
1421  numaGetNonzeroRange(nan, 0.0, &first, &last);
1422  nac = numaClipToInterval(nan, first, last);
1423  gplotSimple1(nac, GPLOT_PNG, "/tmp/lept/comp/histo",
1424  "Difference histogram");
1425  l_fileDisplay("/tmp/lept/comp/histo.png", 500, 0, 1.0);
1426  fprintf(stderr, "\nNonzero values in normalized histogram:");
1427  numaWriteStream(stderr, nac);
1428  numaDestroy(&nac);
1429  fprintf(stderr, " Mindiff fractdiff avediff\n");
1430  fprintf(stderr, " -----------------------------------\n");
1431  for (diff = 1; diff < L_MIN(2 * mindiff, last); diff++) {
1432  fract = 0.0;
1433  ave = 0.0;
1434  for (i = diff; i <= last; i++) {
1435  fract += array[i];
1436  ave += (l_float32)i * array[i];
1437  }
1438  ave = (fract == 0.0) ? 0.0 : ave / fract;
1439  ave -= diff;
1440  fprintf(stderr, "%5d %7.4f %7.4f\n",
1441  diff, fract, ave);
1442  }
1443  fprintf(stderr, " -----------------------------------\n");
1444  }
1445 
1446  fract = 0.0;
1447  ave = 0.0;
1448  for (i = mindiff; i < 256; i++) {
1449  fract += array[i];
1450  ave += (l_float32)i * array[i];
1451  }
1452  ave = (fract == 0.0) ? 0.0 : ave / fract;
1453  ave -= mindiff;
1454 
1455  *pfractdiff = fract;
1456  *pavediff = ave;
1457 
1458  numaDestroy(&nah);
1459  numaDestroy(&nan);
1460  return 0;
1461 }
1462 
1463 
1483 NUMA *
1485  PIX *pix2,
1486  l_int32 factor)
1487 {
1488 l_int32 w1, h1, d1, w2, h2, d2, w, h, wpl1, wpl2;
1489 l_int32 i, j, val, val1, val2;
1490 l_int32 rval1, rval2, gval1, gval2, bval1, bval2;
1491 l_int32 rdiff, gdiff, bdiff, maxdiff;
1492 l_uint32 *data1, *data2, *line1, *line2;
1493 l_float32 *array;
1494 NUMA *na;
1495 PIX *pixt1, *pixt2;
1496 
1497  PROCNAME("pixGetDifferenceHistogram");
1498 
1499  if (!pix1)
1500  return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL);
1501  if (!pix2)
1502  return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL);
1503  d1 = pixGetDepth(pix1);
1504  d2 = pixGetDepth(pix2);
1505  if (d1 == 16 || d2 == 16)
1506  return (NUMA *)ERROR_PTR("d == 16 not supported", procName, NULL);
1507  if (d1 < 8 && !pixGetColormap(pix1))
1508  return (NUMA *)ERROR_PTR("pix1 depth < 8 bpp and not cmapped",
1509  procName, NULL);
1510  if (d2 < 8 && !pixGetColormap(pix2))
1511  return (NUMA *)ERROR_PTR("pix2 depth < 8 bpp and not cmapped",
1512  procName, NULL);
1515  pixGetDimensions(pixt1, &w1, &h1, &d1);
1516  pixGetDimensions(pixt2, &w2, &h2, &d2);
1517  if (d1 != d2) {
1518  pixDestroy(&pixt1);
1519  pixDestroy(&pixt2);
1520  return (NUMA *)ERROR_PTR("pix depths not equal", procName, NULL);
1521  }
1522  if (factor < 1) factor = 1;
1523 
1524  na = numaCreate(256);
1525  numaSetCount(na, 256); /* all initialized to 0.0 */
1526  array = numaGetFArray(na, L_NOCOPY);
1527  w = L_MIN(w1, w2);
1528  h = L_MIN(h1, h2);
1529  data1 = pixGetData(pixt1);
1530  data2 = pixGetData(pixt2);
1531  wpl1 = pixGetWpl(pixt1);
1532  wpl2 = pixGetWpl(pixt2);
1533  if (d1 == 8) {
1534  for (i = 0; i < h; i += factor) {
1535  line1 = data1 + i * wpl1;
1536  line2 = data2 + i * wpl2;
1537  for (j = 0; j < w; j += factor) {
1538  val1 = GET_DATA_BYTE(line1, j);
1539  val2 = GET_DATA_BYTE(line2, j);
1540  val = L_ABS(val1 - val2);
1541  array[val]++;
1542  }
1543  }
1544  } else { /* d1 == 32 */
1545  for (i = 0; i < h; i += factor) {
1546  line1 = data1 + i * wpl1;
1547  line2 = data2 + i * wpl2;
1548  for (j = 0; j < w; j += factor) {
1549  extractRGBValues(line1[j], &rval1, &gval1, &bval1);
1550  extractRGBValues(line2[j], &rval2, &gval2, &bval2);
1551  rdiff = L_ABS(rval1 - rval2);
1552  gdiff = L_ABS(gval1 - gval2);
1553  bdiff = L_ABS(bval1 - bval2);
1554  maxdiff = L_MAX(rdiff, gdiff);
1555  maxdiff = L_MAX(maxdiff, bdiff);
1556  array[maxdiff]++;
1557  }
1558  }
1559  }
1560 
1561  pixDestroy(&pixt1);
1562  pixDestroy(&pixt2);
1563  return na;
1564 }
1565 
1566 
1614 l_ok
1616  PIX *pixs2,
1617  l_int32 sampling,
1618  l_int32 dilation,
1619  l_int32 mindiff,
1620  l_float32 *pfract,
1621  PIX **ppixdiff1,
1622  PIX **ppixdiff2)
1623 {
1624 l_int32 d1, d2, w, h, count;
1625 PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9;
1626 PIX *pix10, *pix11;
1627 
1628  PROCNAME("pixGetPerceptualDiff");
1629 
1630  if (ppixdiff1) *ppixdiff1 = NULL;
1631  if (ppixdiff2) *ppixdiff2 = NULL;
1632  if (!pfract)
1633  return ERROR_INT("&fract not defined", procName, 1);
1634  *pfract = 1.0; /* init to completely different */
1635  if ((dilation & 1) == 0)
1636  return ERROR_INT("dilation must be odd", procName, 1);
1637  if (!pixs1)
1638  return ERROR_INT("pixs1 not defined", procName, 1);
1639  if (!pixs2)
1640  return ERROR_INT("pixs2 not defined", procName, 1);
1641  d1 = pixGetDepth(pixs1);
1642  d2 = pixGetDepth(pixs2);
1643  if (!pixGetColormap(pixs1) && d1 < 8)
1644  return ERROR_INT("pixs1 not cmapped or >=8 bpp", procName, 1);
1645  if (!pixGetColormap(pixs2) && d2 < 8)
1646  return ERROR_INT("pixs2 not cmapped or >=8 bpp", procName, 1);
1647 
1648  /* Integer downsample if requested */
1649  if (sampling > 1) {
1650  pix1 = pixScaleByIntSampling(pixs1, sampling);
1651  pix2 = pixScaleByIntSampling(pixs2, sampling);
1652  } else {
1653  pix1 = pixClone(pixs1);
1654  pix2 = pixClone(pixs2);
1655  }
1656 
1657  /* Remove colormaps */
1658  if (pixGetColormap(pix1)) {
1660  d1 = pixGetDepth(pix3);
1661  } else {
1662  pix3 = pixClone(pix1);
1663  }
1664  if (pixGetColormap(pix2)) {
1666  d2 = pixGetDepth(pix4);
1667  } else {
1668  pix4 = pixClone(pix2);
1669  }
1670  pixDestroy(&pix1);
1671  pixDestroy(&pix2);
1672  if (d1 != d2) {
1673  pixDestroy(&pix3);
1674  pixDestroy(&pix4);
1675  return ERROR_INT("pix3 and pix4 depths not equal", procName, 1);
1676  }
1677 
1678  /* In each direction, do a small dilation and subtract the dilated
1679  * image from the other image to get a one-sided difference.
1680  * Then take the max of the differences for each direction
1681  * and clipping each component to 255 if necessary. Note that
1682  * for RGB images, the dilations and max selection are done
1683  * component-wise, and the conversion to grayscale also uses the
1684  * maximum component. The resulting grayscale images are
1685  * thresholded using %mindiff. */
1686  if (d1 == 8) {
1687  pix5 = pixDilateGray(pix3, dilation, dilation);
1688  pixCompareGray(pix4, pix5, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL,
1689  &pix7);
1690  pix6 = pixDilateGray(pix4, dilation, dilation);
1691  pixCompareGray(pix3, pix6, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL,
1692  &pix8);
1693  pix9 = pixMinOrMax(NULL, pix7, pix8, L_CHOOSE_MAX);
1694  pix10 = pixThresholdToBinary(pix9, mindiff);
1695  pixInvert(pix10, pix10);
1696  pixCountPixels(pix10, &count, NULL);
1697  pixGetDimensions(pix10, &w, &h, NULL);
1698  *pfract = (l_float32)count / (l_float32)(w * h);
1699  pixDestroy(&pix5);
1700  pixDestroy(&pix6);
1701  pixDestroy(&pix7);
1702  pixDestroy(&pix8);
1703  if (ppixdiff1)
1704  *ppixdiff1 = pix9;
1705  else
1706  pixDestroy(&pix9);
1707  if (ppixdiff2)
1708  *ppixdiff2 = pix10;
1709  else
1710  pixDestroy(&pix10);
1711  } else { /* d1 == 32 */
1712  pix5 = pixColorMorph(pix3, L_MORPH_DILATE, dilation, dilation);
1713  pixCompareRGB(pix4, pix5, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL,
1714  &pix7);
1715  pix6 = pixColorMorph(pix4, L_MORPH_DILATE, dilation, dilation);
1716  pixCompareRGB(pix3, pix6, L_COMPARE_SUBTRACT, 0, NULL, NULL, NULL,
1717  &pix8);
1718  pix9 = pixMinOrMax(NULL, pix7, pix8, L_CHOOSE_MAX);
1719  pix10 = pixConvertRGBToGrayMinMax(pix9, L_CHOOSE_MAX);
1720  pix11 = pixThresholdToBinary(pix10, mindiff);
1721  pixInvert(pix11, pix11);
1722  pixCountPixels(pix11, &count, NULL);
1723  pixGetDimensions(pix11, &w, &h, NULL);
1724  *pfract = (l_float32)count / (l_float32)(w * h);
1725  pixDestroy(&pix5);
1726  pixDestroy(&pix6);
1727  pixDestroy(&pix7);
1728  pixDestroy(&pix8);
1729  pixDestroy(&pix10);
1730  if (ppixdiff1)
1731  *ppixdiff1 = pix9;
1732  else
1733  pixDestroy(&pix9);
1734  if (ppixdiff2)
1735  *ppixdiff2 = pix11;
1736  else
1737  pixDestroy(&pix11);
1738 
1739  }
1740  pixDestroy(&pix3);
1741  pixDestroy(&pix4);
1742  return 0;
1743 }
1744 
1745 
1777 l_ok
1779  PIX *pix2,
1780  l_int32 factor,
1781  l_float32 *ppsnr)
1782 {
1783 l_int32 same, i, j, w, h, d, wpl1, wpl2, v1, v2, r1, g1, b1, r2, g2, b2;
1784 l_uint32 *data1, *data2, *line1, *line2;
1785 l_float32 mse; /* mean squared error */
1786 
1787  PROCNAME("pixGetPSNR");
1788 
1789  if (!ppsnr)
1790  return ERROR_INT("&psnr not defined", procName, 1);
1791  *ppsnr = 0.0;
1792  if (!pix1 || !pix2)
1793  return ERROR_INT("empty input pix", procName, 1);
1794  if (!pixSizesEqual(pix1, pix2))
1795  return ERROR_INT("pix sizes unequal", procName, 1);
1796  if (pixGetColormap(pix1))
1797  return ERROR_INT("pix1 has colormap", procName, 1);
1798  if (pixGetColormap(pix2))
1799  return ERROR_INT("pix2 has colormap", procName, 1);
1800  pixGetDimensions(pix1, &w, &h, &d);
1801  if (d != 8 && d != 32)
1802  return ERROR_INT("pix not 8 or 32 bpp", procName, 1);
1803  if (factor < 1)
1804  return ERROR_INT("invalid sampling factor", procName, 1);
1805 
1806  pixEqual(pix1, pix2, &same);
1807  if (same) {
1808  *ppsnr = 1000.0; /* crazy big exponent */
1809  return 0;
1810  }
1811 
1812  data1 = pixGetData(pix1);
1813  data2 = pixGetData(pix2);
1814  wpl1 = pixGetWpl(pix1);
1815  wpl2 = pixGetWpl(pix2);
1816  mse = 0.0;
1817  if (d == 8) {
1818  for (i = 0; i < h; i += factor) {
1819  line1 = data1 + i * wpl1;
1820  line2 = data2 + i * wpl2;
1821  for (j = 0; j < w; j += factor) {
1822  v1 = GET_DATA_BYTE(line1, j);
1823  v2 = GET_DATA_BYTE(line2, j);
1824  mse += (l_float32)(v1 - v2) * (v1 - v2);
1825  }
1826  }
1827  } else { /* d == 32 */
1828  for (i = 0; i < h; i += factor) {
1829  line1 = data1 + i * wpl1;
1830  line2 = data2 + i * wpl2;
1831  for (j = 0; j < w; j += factor) {
1832  extractRGBValues(line1[j], &r1, &g1, &b1);
1833  extractRGBValues(line2[j], &r2, &g2, &b2);
1834  mse += ((l_float32)(r1 - r2) * (r1 - r2) +
1835  (g1 - g2) * (g1 - g2) +
1836  (b1 - b2) * (b1 - b2)) / 3.0;
1837  }
1838  }
1839  }
1840  mse = mse / ((l_float32)(w) * h);
1841 
1842  *ppsnr = -4.3429448 * log(mse / (255 * 255));
1843  return 0;
1844 }
1845 
1846 
1847 /*------------------------------------------------------------------*
1848  * Comparison of photo regions by histogram *
1849  *------------------------------------------------------------------*/
1892 l_ok
1894  l_float32 minratio,
1895  l_float32 textthresh,
1896  l_int32 factor,
1897  l_int32 nx,
1898  l_int32 ny,
1899  l_float32 simthresh,
1900  NUMA **pnai,
1901  l_float32 **pscores,
1902  PIX **ppixd,
1903  l_int32 debug)
1904 {
1905 char *text;
1906 l_int32 i, j, n, w, h, w1, h1, w2, h2, ival, index, classid;
1907 l_float32 score;
1908 l_float32 *scores;
1909 NUMA *nai, *naw, *nah;
1910 NUMAA *naa;
1911 NUMAA **n3a; /* array of naa */
1912 PIX *pix;
1913 
1914  PROCNAME("pixaComparePhotoRegionsByHisto");
1915 
1916  if (pscores) *pscores = NULL;
1917  if (ppixd) *ppixd = NULL;
1918  if (!pnai)
1919  return ERROR_INT("&na not defined", procName, 1);
1920  *pnai = NULL;
1921  if (!pixa)
1922  return ERROR_INT("pixa not defined", procName, 1);
1923  if (minratio < 0.0 || minratio > 1.0)
1924  return ERROR_INT("minratio not in [0.0 ... 1.0]", procName, 1);
1925  if (textthresh <= 0.0) textthresh = 1.3;
1926  if (factor < 1)
1927  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
1928  if (nx < 1 || ny < 1)
1929  return ERROR_INT("nx and ny must both be > 0", procName, 1);
1930  if (simthresh <= 0.0) simthresh = 0.25;
1931  if (simthresh > 1.0)
1932  return ERROR_INT("simthresh invalid; should be near 0.25", procName, 1);
1933 
1934  /* Prepare the histograms */
1935  n = pixaGetCount(pixa);
1936  if ((n3a = (NUMAA **)LEPT_CALLOC(n, sizeof(NUMAA *))) == NULL)
1937  return ERROR_INT("calloc fail for n3a", procName, 1);
1938  naw = numaCreate(0);
1939  nah = numaCreate(0);
1940  for (i = 0; i < n; i++) {
1941  pix = pixaGetPix(pixa, i, L_CLONE);
1942  text = pixGetText(pix);
1943  pixSetResolution(pix, 150, 150);
1944  index = (debug) ? i : 0;
1945  pixGenPhotoHistos(pix, NULL, factor, textthresh, nx, ny,
1946  &naa, &w, &h, index);
1947  n3a[i] = naa;
1948  numaAddNumber(naw, w);
1949  numaAddNumber(nah, h);
1950  if (naa)
1951  fprintf(stderr, "Image %s is photo\n", text);
1952  else
1953  fprintf(stderr, "Image %s is NOT photo\n", text);
1954  pixDestroy(&pix);
1955  }
1956 
1957  /* Do the comparisons. We are making a set of classes, where
1958  * all similar images are placed in the same class. There are
1959  * 'n' input images. The classes are labeled by 'classid' (all
1960  * similar images get the same 'classid' value), and 'nai' maps
1961  * the classid of the image in the input array to the classid
1962  * of the similarity class. */
1963  if ((scores = (l_float32 *)LEPT_CALLOC((size_t)n * n, sizeof(l_float32)))
1964  == NULL) {
1965  L_ERROR("calloc fail for scores\n", procName);
1966  goto cleanup;
1967  }
1968  nai = numaMakeConstant(-1, n); /* classid array */
1969  for (i = 0, classid = 0; i < n; i++) {
1970  scores[n * i + i] = 1.0;
1971  numaGetIValue(nai, i, &ival);
1972  if (ival != -1) /* already set */
1973  continue;
1974  numaSetValue(nai, i, classid);
1975  if (n3a[i] == NULL) { /* not a photo */
1976  classid++;
1977  continue;
1978  }
1979  numaGetIValue(naw, i, &w1);
1980  numaGetIValue(nah, i, &h1);
1981  for (j = i + 1; j < n; j++) {
1982  numaGetIValue(nai, j, &ival);
1983  if (ival != -1) /* already set */
1984  continue;
1985  if (n3a[j] == NULL) /* not a photo */
1986  continue;
1987  numaGetIValue(naw, j, &w2);
1988  numaGetIValue(nah, j, &h2);
1989  compareTilesByHisto(n3a[i], n3a[j], minratio, w1, h1, w2, h2,
1990  &score, NULL);
1991  scores[n * i + j] = score;
1992  scores[n * j + i] = score; /* the score array is symmetric */
1993 /* fprintf(stderr, "score = %5.3f\n", score); */
1994  if (score > simthresh) {
1995  numaSetValue(nai, j, classid);
1996  fprintf(stderr,
1997  "Setting %d similar to %d, in class %d; score %5.3f\n",
1998  j, i, classid, score);
1999  }
2000  }
2001  classid++;
2002  }
2003  *pnai = nai;
2004 
2005  /* Debug: optionally save and display the score array.
2006  * All images that are photos are represented by a point on
2007  * the diagonal. Other images in the same similarity class
2008  * are on the same horizontal raster line to the right.
2009  * The array has been symmetrized, so images in the same
2010  * same similarity class also appear on the same column below. */
2011  if (pscores) {
2012  l_int32 wpl, fact;
2013  l_uint32 *line, *data;
2014  PIX *pix2, *pix3;
2015  pix2 = pixCreate(n, n, 8);
2016  data = pixGetData(pix2);
2017  wpl = pixGetWpl(pix2);
2018  for (i = 0; i < n; i++) {
2019  line = data + i * wpl;
2020  for (j = 0; j < n; j++) {
2021  SET_DATA_BYTE(line, j,
2022  L_MIN(255, 4.0 * 255 * scores[n * i + j]));
2023  }
2024  }
2025  fact = L_MAX(2, 1000 / n);
2026  pix3 = pixExpandReplicate(pix2, fact);
2027  fprintf(stderr, "Writing to /tmp/lept/comp/scorearray.png\n");
2028  lept_mkdir("lept/comp");
2029  pixWrite("/tmp/lept/comp/scorearray.png", pix3, IFF_PNG);
2030  pixDestroy(&pix2);
2031  pixDestroy(&pix3);
2032  *pscores = scores;
2033  } else {
2034  LEPT_FREE(scores);
2035  }
2036 
2037  /* Debug: optionally display and save the image comparisons.
2038  * Image similarity classes are displayed by column; similar
2039  * images are displayed in the same column. */
2040  if (ppixd)
2041  *ppixd = pixaDisplayTiledByIndex(pixa, nai, 200, 20, 2, 6, 0x0000ff00);
2042 
2043 cleanup:
2044  numaDestroy(&naw);
2045  numaDestroy(&nah);
2046  for (i = 0; i < n; i++)
2047  numaaDestroy(&n3a[i]);
2048  LEPT_FREE(n3a);
2049  return 0;
2050 }
2051 
2052 
2104 l_ok
2106  PIX *pix2,
2107  BOX *box1,
2108  BOX *box2,
2109  l_float32 minratio,
2110  l_int32 factor,
2111  l_int32 nx,
2112  l_int32 ny,
2113  l_float32 *pscore,
2114  l_int32 debugflag)
2115 {
2116 l_int32 w1, h1, w2, h2, w1c, h1c, w2c, h2c, debugindex;
2117 l_float32 wratio, hratio;
2118 NUMAA *naa1, *naa2;
2119 PIX *pix3, *pix4;
2120 PIXA *pixa;
2121 
2122  PROCNAME("pixComparePhotoRegionsByHisto");
2123 
2124  if (!pscore)
2125  return ERROR_INT("&score not defined", procName, 1);
2126  *pscore = 0.0;
2127  if (!pix1 || !pix2)
2128  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
2129  if (minratio < 0.5 || minratio > 1.0)
2130  return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1);
2131  if (factor < 1)
2132  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2133  if (nx < 1 || ny < 1)
2134  return ERROR_INT("nx and ny must both be > 0", procName, 1);
2135 
2136  debugindex = 0;
2137  if (debugflag) {
2138  lept_mkdir("lept/comp");
2139  debugindex = 666; /* arbitrary number used for naming output */
2140  }
2141 
2142  /* Initial filter by size */
2143  if (box1)
2144  boxGetGeometry(box1, NULL, NULL, &w1, &h1);
2145  else
2146  pixGetDimensions(pix1, &w1, &h1, NULL);
2147  if (box2)
2148  boxGetGeometry(box2, NULL, NULL, &w2, &h2);
2149  else
2150  pixGetDimensions(pix1, &w2, &h2, NULL);
2151  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2152  (l_float32)w2 / (l_float32)w1;
2153  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2154  (l_float32)h2 / (l_float32)h1;
2155  if (wratio < minratio || hratio < minratio)
2156  return 0;
2157 
2158  /* Initial crop, if necessary, and make histos */
2159  if (box1)
2160  pix3 = pixClipRectangle(pix1, box1, NULL);
2161  else
2162  pix3 = pixClone(pix1);
2163  pixGenPhotoHistos(pix3, NULL, factor, 0, nx, ny,
2164  &naa1, &w1c, &h1c, debugindex);
2165  pixDestroy(&pix3);
2166  if (!naa1) return 0;
2167  if (box2)
2168  pix4 = pixClipRectangle(pix2, box2, NULL);
2169  else
2170  pix4 = pixClone(pix2);
2171  pixGenPhotoHistos(pix4, NULL, factor, 0, nx, ny,
2172  &naa2, &w2c, &h2c, debugindex);
2173  pixDestroy(&pix4);
2174  if (!naa2) return 0;
2175 
2176  /* Compare histograms */
2177  pixa = (debugflag) ? pixaCreate(0) : NULL;
2178  compareTilesByHisto(naa1, naa2, minratio, w1c, h1c, w2c, h2c, pscore, pixa);
2179  pixaDestroy(&pixa);
2180  return 0;
2181 }
2182 
2183 
2214 l_ok
2216  BOX *box,
2217  l_int32 factor,
2218  l_float32 thresh,
2219  l_int32 nx,
2220  l_int32 ny,
2221  NUMAA **pnaa,
2222  l_int32 *pw,
2223  l_int32 *ph,
2224  l_int32 debugindex)
2225 {
2226 char buf[64];
2227 NUMAA *naa;
2228 PIX *pix1, *pix2, *pix3, *pixm;
2229 PIXA *pixa;
2230 
2231  PROCNAME("pixGenPhotoHistos");
2232 
2233  if (pnaa) *pnaa = NULL;
2234  if (pw) *pw = 0;
2235  if (ph) *ph = 0;
2236  if (!pnaa)
2237  return ERROR_INT("&naa not defined", procName, 1);
2238  if (!pw || !ph)
2239  return ERROR_INT("&w and &h not both defined", procName, 1);
2240  if (!pixs || pixGetDepth(pixs) == 1)
2241  return ERROR_INT("pixs not defined or 1 bpp", procName, 1);
2242  if (factor < 1)
2243  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2244  if (nx < 1 || ny < 1)
2245  return ERROR_INT("nx and ny must both be > 0", procName, 1);
2246  if (thresh <= 0.0) thresh = 1.3; /* default */
2247 
2248  pixa = NULL;
2249  if (debugindex > 0) {
2250  pixa = pixaCreate(0);
2251  lept_mkdir("lept/comp");
2252  }
2253 
2254  /* Initial crop, if necessary */
2255  if (box)
2256  pix1 = pixClipRectangle(pixs, box, NULL);
2257  else
2258  pix1 = pixClone(pixs);
2259 
2260  /* Convert to 8 bpp and pad to center the centroid */
2261  pix2 = pixConvertTo8(pix1, FALSE);
2262  pix3 = pixPadToCenterCentroid(pix2, factor);
2263 
2264  /* Set to 255 all pixels above 230. Do this so that light gray
2265  * pixels do not enter into the comparison. */
2266  pixm = pixThresholdToBinary(pix3, 230);
2267  pixInvert(pixm, pixm);
2268  pixSetMaskedGeneral(pix3, pixm, 255, 0, 0);
2269  pixDestroy(&pixm);
2270 
2271  if (debugindex > 0) {
2272  PIX *pix4, *pix5, *pix6, *pix7, *pix8;
2273  PIXA *pixa2;
2274  pix4 = pixConvertTo32(pix2);
2275  pix5 = pixConvertTo32(pix3);
2276  pix6 = pixScaleToSize(pix4, 400, 0);
2277  pix7 = pixScaleToSize(pix5, 400, 0);
2278  pixa2 = pixaCreate(2);
2279  pixaAddPix(pixa2, pix6, L_INSERT);
2280  pixaAddPix(pixa2, pix7, L_INSERT);
2281  pix8 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 3);
2282  pixaAddPix(pixa, pix8, L_INSERT);
2283  pixDestroy(&pix4);
2284  pixDestroy(&pix5);
2285  pixaDestroy(&pixa2);
2286  }
2287  pixDestroy(&pix1);
2288  pixDestroy(&pix2);
2289 
2290  /* Test if this is a photoimage */
2291  pixDecideIfPhotoImage(pix3, factor, nx, ny, thresh, &naa, pixa);
2292  if (naa) {
2293  *pnaa = naa;
2294  *pw = pixGetWidth(pix3);
2295  *ph = pixGetHeight(pix3);
2296  }
2297 
2298  if (pixa) {
2299  snprintf(buf, sizeof(buf), "/tmp/lept/comp/tiledhistos.%d.pdf",
2300  debugindex);
2301  fprintf(stderr, "Writing to %s\n", buf);
2302  pixaConvertToPdf(pixa, 300, 1.0, L_FLATE_ENCODE, 0, NULL, buf);
2303  pixaDestroy(&pixa);
2304  }
2305 
2306  pixDestroy(&pix3);
2307  return 0;
2308 }
2309 
2310 
2326 PIX *
2328  l_int32 factor)
2329 
2330 {
2331 l_float32 cx, cy;
2332 l_int32 xs, ys, delx, dely, icx, icy, ws, hs, wd, hd;
2333 PIX *pix1, *pixd;
2334 
2335  PROCNAME("pixPadToCenterCentroid");
2336 
2337  if (!pixs)
2338  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2339  if (factor < 1)
2340  return (PIX *)ERROR_PTR("invalid sampling factor", procName, NULL);
2341 
2342  pix1 = pixConvertTo8(pixs, FALSE);
2343  pixCentroid8(pix1, factor, &cx, &cy);
2344  icx = (l_int32)(cx + 0.5);
2345  icy = (l_int32)(cy + 0.5);
2346  pixGetDimensions(pix1, &ws, &hs, NULL);
2347  delx = ws - 2 * icx;
2348  dely = hs - 2 * icy;
2349  xs = L_MAX(0, delx);
2350  ys = L_MAX(0, dely);
2351  wd = 2 * L_MAX(icx, ws - icx);
2352  hd = 2 * L_MAX(icy, hs - icy);
2353  pixd = pixCreate(wd, hd, 8);
2354  pixSetAll(pixd); /* to white */
2355  pixCopyResolution(pixd, pixs);
2356  pixRasterop(pixd, xs, ys, ws, hs, PIX_SRC, pix1, 0, 0);
2357  pixDestroy(&pix1);
2358  return pixd;
2359 }
2360 
2361 
2380 l_ok
2382  l_int32 factor,
2383  l_float32 *pcx,
2384  l_float32 *pcy)
2385 {
2386 l_int32 i, j, w, h, wpl, val;
2387 l_float32 sumx, sumy, sumv;
2388 l_uint32 *data, *line;
2389 PIX *pix1;
2390 
2391  PROCNAME("pixCentroid8");
2392 
2393  if (pcx) *pcx = 0.0;
2394  if (pcy) *pcy = 0.0;
2395  if (!pixs || pixGetDepth(pixs) != 8)
2396  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
2397  if (factor < 1)
2398  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2399  if (!pcx || !pcy)
2400  return ERROR_INT("&cx and &cy not both defined", procName, 1);
2401 
2402  pix1 = pixInvert(NULL, pixs);
2403  pixGetDimensions(pix1, &w, &h, NULL);
2404  data = pixGetData(pix1);
2405  wpl = pixGetWpl(pix1);
2406  sumx = sumy = sumv = 0.0;
2407  for (i = 0; i < h; i++) {
2408  line = data + i * wpl;
2409  for (j = 0; j < w; j++) {
2410  val = GET_DATA_BYTE(line, j);
2411  sumx += val * j;
2412  sumy += val * i;
2413  sumv += val;
2414  }
2415  }
2416  pixDestroy(&pix1);
2417 
2418  if (sumv == 0) {
2419  L_INFO("input image is white\n", procName);
2420  *pcx = (l_float32)(w) / 2;
2421  *pcy = (l_float32)(h) / 2;
2422  } else {
2423  *pcx = sumx / sumv;
2424  *pcy = sumy / sumv;
2425  }
2426 
2427  return 0;
2428 }
2429 
2430 
2458 l_ok
2460  l_int32 factor,
2461  l_int32 nx,
2462  l_int32 ny,
2463  l_float32 thresh,
2464  NUMAA **pnaa,
2465  PIXA *pixadebug)
2466 {
2467 char buf[64];
2468 l_int32 i, n, istext, isphoto;
2469 l_float32 maxval, sum1, sum2, ratio;
2470 L_BMF *bmf;
2471 NUMA *na1, *na2, *na3, *narv;
2472 NUMAA *naa;
2473 PIX *pix1;
2474 PIXA *pixa, *pixa2, *pixa3;
2475 
2476  PROCNAME("pixDecideIfPhotoImage");
2477 
2478  if (!pnaa)
2479  return ERROR_INT("&naa not defined", procName, 1);
2480  *pnaa = NULL;
2481  if (!pix || pixGetDepth(pix) != 8 || pixGetColormap(pix))
2482  return ERROR_INT("pix undefined or invalid", procName, 1);
2483  if (thresh <= 0.0) thresh = 1.3; /* default */
2484 
2485  /* Look for text lines */
2486  pixDecideIfText(pix, NULL, &istext, pixadebug);
2487  if (istext) {
2488  L_INFO("Image is text\n", procName);
2489  return 0;
2490  }
2491 
2492  /* Evaluate histograms in each tile */
2493  pixa = pixaSplitPix(pix, nx, ny, 0, 0);
2494  n = nx * ny;
2495  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
2496  naa = numaaCreate(n);
2497  if (pixadebug) {
2498  lept_rmdir("lept/compplot");
2499  lept_mkdir("lept/compplot");
2500  }
2501  for (i = 0; i < n; i++) {
2502  pix1 = pixaGetPix(pixa, i, L_CLONE);
2503 
2504  /* Get histograms, set white count to 0, normalize max to 255 */
2505  na1 = pixGetGrayHistogram(pix1, factor);
2506  numaSetValue(na1, 255, 0);
2507  na2 = numaWindowedMean(na1, 5); /* do some smoothing */
2508  numaGetMax(na2, &maxval, NULL);
2509  na3 = numaTransform(na2, 0, 255.0 / maxval);
2510  if (pixadebug) {
2511  snprintf(buf, sizeof(buf), "/tmp/lept/compplot/plot.%d", i);
2512  gplotSimple1(na3, GPLOT_PNG, buf, "Histos");
2513  }
2514 
2515  numaaAddNuma(naa, na3, L_INSERT);
2516  numaDestroy(&na1);
2517  numaDestroy(&na2);
2518  pixDestroy(&pix1);
2519  }
2520  if (pixadebug) {
2521  pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 30, 2);
2522  pixaAddPix(pixadebug, pix1, L_INSERT);
2523  pixa2 = pixaReadFiles("/tmp/lept/compplot", ".png");
2524  pixa3 = pixaScale(pixa2, 0.4, 0.4);
2525  pix1 = pixaDisplayTiledInColumns(pixa3, 3, 1.0, 30, 2);
2526  pixaAddPix(pixadebug, pix1, L_INSERT);
2527  pixaDestroy(&pixa2);
2528  pixaDestroy(&pixa3);
2529  }
2530 
2531  /* Compute the standard deviation between these histos to decide
2532  * if the image is photo or something more like line art,
2533  * which does not support good comparison by tiled histograms. */
2534  grayInterHistogramStats(naa, 5, NULL, NULL, NULL, &narv);
2535 
2536  /* For photos, the root variance has a larger weight of
2537  * values in the range [50 ... 150] compared to [200 ... 230],
2538  * than text or line art. For the latter, most of the variance
2539  * between tiles is in the lightest parts of the image, well
2540  * above 150. */
2541  numaGetSumOnInterval(narv, 50, 150, &sum1);
2542  numaGetSumOnInterval(narv, 200, 230, &sum2);
2543  if (sum2 == 0.0) /* shouldn't happen */
2544  isphoto = 0; /* be conservative */
2545  else {
2546  ratio = sum1 / sum2;
2547  isphoto = (ratio > thresh) ? 1 : 0;
2548  if (pixadebug) {
2549  if (isphoto)
2550  L_INFO("ratio %f > %f; isphoto is true\n",
2551  procName, ratio, thresh);
2552  else
2553  L_INFO("ratio %f < %f; isphoto is false\n",
2554  procName, ratio, thresh);
2555  }
2556  }
2557  if (isphoto)
2558  *pnaa = naa;
2559  else
2560  numaaDestroy(&naa);
2561  bmfDestroy(&bmf);
2562  numaDestroy(&narv);
2563  pixaDestroy(&pixa);
2564  return 0;
2565 }
2566 
2567 
2590 l_ok
2592  NUMAA *naa2,
2593  l_float32 minratio,
2594  l_int32 w1,
2595  l_int32 h1,
2596  l_int32 w2,
2597  l_int32 h2,
2598  l_float32 *pscore,
2599  PIXA *pixadebug)
2600 {
2601 char buf1[128], buf2[128];
2602 l_int32 i, n;
2603 l_float32 wratio, hratio, score, minscore, dist;
2604 L_BMF *bmf;
2605 NUMA *na1, *na2, *nadist, *nascore;
2606 
2607  PROCNAME("compareTilesByHisto");
2608 
2609  if (!pscore)
2610  return ERROR_INT("&score not defined", procName, 1);
2611  *pscore = 0.0;
2612  if (!naa1 || !naa2)
2613  return ERROR_INT("naa1 and naa2 not both defined", procName, 1);
2614 
2615  /* Filter for different sizes */
2616  n = numaaGetCount(naa1);
2617  if (n != numaaGetCount(naa2))
2618  return ERROR_INT("naa1 and naa2 are different size", procName, 1);
2619 
2620  if (pixadebug) {
2621  lept_rmdir("lept/comptile");
2622  lept_mkdir("lept/comptile");
2623  }
2624 
2625  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2626  (l_float32)w2 / (l_float32)w1;
2627  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2628  (l_float32)h2 / (l_float32)h1;
2629  if (wratio < minratio || hratio < minratio) {
2630  if (pixadebug)
2631  L_INFO("Sizes differ: wratio = %f, hratio = %f\n",
2632  procName, wratio, hratio);
2633  return 0;
2634  }
2635 
2636  /* Evaluate histograms in each tile. Remove white before
2637  * computing EMD, because there are may be a lot of white
2638  * pixels due to padding, and we don't want to include them.
2639  * This also makes the debug histo plots more informative. */
2640  minscore = 1.0;
2641  nadist = numaCreate(n);
2642  nascore = numaCreate(n);
2643  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
2644  for (i = 0; i < n; i++) {
2645  na1 = numaaGetNuma(naa1, i, L_CLONE);
2646  na2 = numaaGetNuma(naa2, i, L_CLONE);
2647  numaSetValue(na1, 255, 0.0);
2648  numaSetValue(na2, 255, 0.0);
2649 
2650  /* To compare histograms, use the normalized earthmover distance.
2651  * Further normalize to get the EM distance as a fraction of the
2652  * maximum distance in the histogram (255). Finally, scale this
2653  * up by 10.0, and subtract from 1.0 to get a similarity score. */
2654  numaEarthMoverDistance(na1, na2, &dist);
2655  score = L_MAX(0.0, 1.0 - 10.0 * (dist / 255.));
2656  numaAddNumber(nadist, dist);
2657  numaAddNumber(nascore, score);
2658  minscore = L_MIN(minscore, score);
2659  if (pixadebug) {
2660  snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d", i);
2661  gplotSimple2(na1, na2, GPLOT_PNG, buf1, "Histos");
2662  }
2663  numaDestroy(&na1);
2664  numaDestroy(&na2);
2665  }
2666  *pscore = minscore;
2667 
2668  if (pixadebug) {
2669  for (i = 0; i < n; i++) {
2670  PIX *pix1, *pix2;
2671  snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d.png", i);
2672  pix1 = pixRead(buf1);
2673  numaGetFValue(nadist, i, &dist);
2674  numaGetFValue(nascore, i, &score);
2675  snprintf(buf2, sizeof(buf2),
2676  "Image %d\ndist = %5.3f, score = %5.3f", i, dist, score);
2677  pix2 = pixAddTextlines(pix1, bmf, buf2, 0x0000ff00, L_ADD_BELOW);
2678  pixaAddPix(pixadebug, pix2, L_INSERT);
2679  pixDestroy(&pix1);
2680  }
2681  fprintf(stderr, "Writing to /tmp/lept/comptile/comparegray.pdf\n");
2682  pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
2683  "/tmp/lept/comptile/comparegray.pdf");
2684  numaWriteDebug("/tmp/lept/comptile/scores.na", nascore);
2685  numaWriteDebug("/tmp/lept/comptile/dists.na", nadist);
2686  }
2687 
2688  bmfDestroy(&bmf);
2689  numaDestroy(&nadist);
2690  numaDestroy(&nascore);
2691  return 0;
2692 }
2693 
2694 
2758 l_ok
2760  PIX *pix2,
2761  BOX *box1,
2762  BOX *box2,
2763  l_float32 minratio,
2764  l_int32 maxgray,
2765  l_int32 factor,
2766  l_int32 nx,
2767  l_int32 ny,
2768  l_float32 *pscore,
2769  l_int32 debugflag)
2770 {
2771 l_int32 w1, h1, w2, h2;
2772 l_float32 wratio, hratio;
2773 BOX *box3, *box4;
2774 PIX *pix3, *pix4, *pix5, *pix6, *pix7, *pix8;
2775 PIXA *pixa;
2776 
2777  PROCNAME("pixCompareGrayByHisto");
2778 
2779  if (!pscore)
2780  return ERROR_INT("&score not defined", procName, 1);
2781  *pscore = 0.0;
2782  if (!pix1 || !pix2)
2783  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
2784  if (minratio < 0.5 || minratio > 1.0)
2785  return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1);
2786  if (maxgray < 200)
2787  return ERROR_INT("invalid maxgray; should be >= 200", procName, 1);
2788  maxgray = L_MIN(255, maxgray);
2789  if (factor < 1)
2790  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2791  if (nx < 1 || ny < 1)
2792  return ERROR_INT("nx and ny must both be > 0", procName, 1);
2793 
2794  if (debugflag)
2795  lept_mkdir("lept/comp");
2796 
2797  /* Initial filter by size */
2798  if (box1)
2799  boxGetGeometry(box1, NULL, NULL, &w1, &h1);
2800  else
2801  pixGetDimensions(pix1, &w1, &h1, NULL);
2802  if (box2)
2803  boxGetGeometry(box2, NULL, NULL, &w2, &h2);
2804  else
2805  pixGetDimensions(pix1, &w2, &h2, NULL);
2806  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2807  (l_float32)w2 / (l_float32)w1;
2808  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2809  (l_float32)h2 / (l_float32)h1;
2810  if (wratio < minratio || hratio < minratio)
2811  return 0;
2812 
2813  /* Initial crop, if necessary */
2814  if (box1)
2815  pix3 = pixClipRectangle(pix1, box1, NULL);
2816  else
2817  pix3 = pixClone(pix1);
2818  if (box2)
2819  pix4 = pixClipRectangle(pix2, box2, NULL);
2820  else
2821  pix4 = pixClone(pix2);
2822 
2823  /* Convert to 8 bpp, align centroids and do maximal crop */
2824  pix5 = pixConvertTo8(pix3, FALSE);
2825  pix6 = pixConvertTo8(pix4, FALSE);
2826  pixCropAlignedToCentroid(pix5, pix6, factor, &box3, &box4);
2827  pix7 = pixClipRectangle(pix5, box3, NULL);
2828  pix8 = pixClipRectangle(pix6, box4, NULL);
2829  pixa = (debugflag) ? pixaCreate(0) : NULL;
2830  if (debugflag) {
2831  PIX *pix9, *pix10, *pix11, *pix12, *pix13;
2832  PIXA *pixa2;
2833  pix9 = pixConvertTo32(pix5);
2834  pix10 = pixConvertTo32(pix6);
2835  pixRenderBoxArb(pix9, box3, 2, 255, 0, 0);
2836  pixRenderBoxArb(pix10, box4, 2, 255, 0, 0);
2837  pix11 = pixScaleToSize(pix9, 400, 0);
2838  pix12 = pixScaleToSize(pix10, 400, 0);
2839  pixa2 = pixaCreate(2);
2840  pixaAddPix(pixa2, pix11, L_INSERT);
2841  pixaAddPix(pixa2, pix12, L_INSERT);
2842  pix13 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 0);
2843  pixaAddPix(pixa, pix13, L_INSERT);
2844  pixDestroy(&pix9);
2845  pixDestroy(&pix10);
2846  pixaDestroy(&pixa2);
2847  }
2848  pixDestroy(&pix3);
2849  pixDestroy(&pix4);
2850  pixDestroy(&pix5);
2851  pixDestroy(&pix6);
2852  boxDestroy(&box3);
2853  boxDestroy(&box4);
2854 
2855  /* Tile and compare histograms */
2856  pixCompareTilesByHisto(pix7, pix8, maxgray, factor, nx, ny, pscore, pixa);
2857  pixaDestroy(&pixa);
2858  pixDestroy(&pix7);
2859  pixDestroy(&pix8);
2860  return 0;
2861 }
2862 
2863 
2884 static l_int32
2886  PIX *pix2,
2887  l_int32 maxgray,
2888  l_int32 factor,
2889  l_int32 nx,
2890  l_int32 ny,
2891  l_float32 *pscore,
2892  PIXA *pixadebug)
2893 {
2894 char buf[64];
2895 l_int32 i, j, n;
2896 l_float32 score, minscore, maxval1, maxval2, dist;
2897 L_BMF *bmf;
2898 NUMA *na1, *na2, *na3, *na4, *na5, *na6, *na7;
2899 PIX *pix3, *pix4;
2900 PIXA *pixa1, *pixa2;
2901 
2902  PROCNAME("pixCompareTilesByHisto");
2903 
2904  if (!pscore)
2905  return ERROR_INT("&score not defined", procName, 1);
2906  *pscore = 0.0;
2907  if (!pix1 || !pix2)
2908  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
2909 
2910  /* Evaluate histograms in each tile */
2911  pixa1 = pixaSplitPix(pix1, nx, ny, 0, 0);
2912  pixa2 = pixaSplitPix(pix2, nx, ny, 0, 0);
2913  n = nx * ny;
2914  na7 = (pixadebug) ? numaCreate(n) : NULL;
2915  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
2916  minscore = 1.0;
2917  for (i = 0; i < n; i++) {
2918  pix3 = pixaGetPix(pixa1, i, L_CLONE);
2919  pix4 = pixaGetPix(pixa2, i, L_CLONE);
2920 
2921  /* Get histograms, set white count to 0, normalize max to 255 */
2922  na1 = pixGetGrayHistogram(pix3, factor);
2923  na2 = pixGetGrayHistogram(pix4, factor);
2924  if (maxgray < 255) {
2925  for (j = maxgray + 1; j <= 255; j++) {
2926  numaSetValue(na1, j, 0);
2927  numaSetValue(na2, j, 0);
2928  }
2929  }
2930  na3 = numaWindowedMean(na1, 5);
2931  na4 = numaWindowedMean(na2, 5);
2932  numaGetMax(na3, &maxval1, NULL);
2933  numaGetMax(na4, &maxval2, NULL);
2934  na5 = numaTransform(na3, 0, 255.0 / maxval1);
2935  na6 = numaTransform(na4, 0, 255.0 / maxval2);
2936  if (pixadebug) {
2937  gplotSimple2(na5, na6, GPLOT_PNG, "/tmp/lept/comp/plot1", "Histos");
2938  }
2939 
2940  /* To compare histograms, use the normalized earthmover distance.
2941  * Further normalize to get the EM distance as a fraction of the
2942  * maximum distance in the histogram (255). Finally, scale this
2943  * up by 10.0, and subtract from 1.0 to get a similarity score. */
2944  numaEarthMoverDistance(na5, na6, &dist);
2945  score = L_MAX(0.0, 1.0 - 8.0 * (dist / 255.));
2946  if (pixadebug) numaAddNumber(na7, score);
2947  minscore = L_MIN(minscore, score);
2948  if (pixadebug) {
2949  PIX *pix5, *pix6, *pix7, *pix8, *pix9, *pix10;
2950  PIXA *pixa3;
2951  l_int32 w, h, wscale;
2952  pixa3 = pixaCreate(3);
2953  pixGetDimensions(pix3, &w, &h, NULL);
2954  wscale = (w > h) ? 700 : 400;
2955  pix5 = pixScaleToSize(pix3, wscale, 0);
2956  pix6 = pixScaleToSize(pix4, wscale, 0);
2957  pixaAddPix(pixa3, pix5, L_INSERT);
2958  pixaAddPix(pixa3, pix6, L_INSERT);
2959  pix7 = pixRead("/tmp/lept/comp/plot1.png");
2960  pix8 = pixScaleToSize(pix7, 700, 0);
2961  snprintf(buf, sizeof(buf), "%5.3f", score);
2962  pix9 = pixAddTextlines(pix8, bmf, buf, 0x0000ff00, L_ADD_RIGHT);
2963  pixaAddPix(pixa3, pix9, L_INSERT);
2964  pix10 = pixaDisplayTiledInRows(pixa3, 32, 1000, 1.0, 0, 50, 0);
2965  pixaAddPix(pixadebug, pix10, L_INSERT);
2966  pixDestroy(&pix7);
2967  pixDestroy(&pix8);
2968  pixaDestroy(&pixa3);
2969  }
2970  numaDestroy(&na1);
2971  numaDestroy(&na2);
2972  numaDestroy(&na3);
2973  numaDestroy(&na4);
2974  numaDestroy(&na5);
2975  numaDestroy(&na6);
2976  pixDestroy(&pix3);
2977  pixDestroy(&pix4);
2978  }
2979  *pscore = minscore;
2980 
2981  if (pixadebug) {
2982  pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
2983  "/tmp/lept/comp/comparegray.pdf");
2984  numaWriteDebug("/tmp/lept/comp/tilescores.na", na7);
2985  }
2986 
2987  bmfDestroy(&bmf);
2988  numaDestroy(&na7);
2989  pixaDestroy(&pixa1);
2990  pixaDestroy(&pixa2);
2991  return 0;
2992 }
2993 
2994 
3011 l_ok
3013  PIX *pix2,
3014  l_int32 factor,
3015  BOX **pbox1,
3016  BOX **pbox2)
3017 {
3018 l_float32 cx1, cy1, cx2, cy2;
3019 l_int32 w1, h1, w2, h2, icx1, icy1, icx2, icy2;
3020 l_int32 xm, xm1, xm2, xp, xp1, xp2, ym, ym1, ym2, yp, yp1, yp2;
3021 PIX *pix3, *pix4;
3022 
3023  PROCNAME("pixCropAlignedToCentroid");
3024 
3025  if (pbox1) *pbox1 = NULL;
3026  if (pbox2) *pbox2 = NULL;
3027  if (!pix1 || !pix2)
3028  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
3029  if (factor < 1)
3030  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
3031  if (!pbox1 || !pbox2)
3032  return ERROR_INT("&box1 and &box2 not both defined", procName, 1);
3033 
3034  pix3 = pixConvertTo8(pix1, FALSE);
3035  pix4 = pixConvertTo8(pix2, FALSE);
3036  pixCentroid8(pix3, factor, &cx1, &cy1);
3037  pixCentroid8(pix4, factor, &cx2, &cy2);
3038  pixGetDimensions(pix3, &w1, &h1, NULL);
3039  pixGetDimensions(pix4, &w2, &h2, NULL);
3040  pixDestroy(&pix3);
3041  pixDestroy(&pix4);
3042 
3043  icx1 = (l_int32)(cx1 + 0.5);
3044  icy1 = (l_int32)(cy1 + 0.5);
3045  icx2 = (l_int32)(cx2 + 0.5);
3046  icy2 = (l_int32)(cy2 + 0.5);
3047  xm = L_MIN(icx1, icx2);
3048  xm1 = icx1 - xm;
3049  xm2 = icx2 - xm;
3050  xp = L_MIN(w1 - icx1, w2 - icx2); /* one pixel beyond to the right */
3051  xp1 = icx1 + xp;
3052  xp2 = icx2 + xp;
3053  ym = L_MIN(icy1, icy2);
3054  ym1 = icy1 - ym;
3055  ym2 = icy2 - ym;
3056  yp = L_MIN(h1 - icy1, h2 - icy2); /* one pixel below the bottom */
3057  yp1 = icy1 + yp;
3058  yp2 = icy2 + yp;
3059  *pbox1 = boxCreate(xm1, ym1, xp1 - xm1, yp1 - ym1);
3060  *pbox2 = boxCreate(xm2, ym2, xp2 - xm2, yp2 - ym2);
3061  return 0;
3062 }
3063 
3064 
3086 l_uint8 *
3088  l_int32 w,
3089  l_int32 h,
3090  size_t *psize)
3091 {
3092 l_uint8 *bytea;
3093 l_int32 i, j, n, nn, ival;
3094 l_float32 maxval;
3095 NUMA *na1, *na2;
3096 
3097  PROCNAME("l_compressGrayHistograms");
3098 
3099  if (!psize)
3100  return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL);
3101  *psize = 0;
3102  if (!naa)
3103  return (l_uint8 *)ERROR_PTR("naa not defined", procName, NULL);
3104  n = numaaGetCount(naa);
3105  for (i = 0; i < n; i++) {
3106  nn = numaaGetNumaCount(naa, i);
3107  if (nn != 256) {
3108  L_ERROR("%d numbers in numa[%d]\n", procName, nn, i);
3109  return NULL;
3110  }
3111  }
3112 
3113  if ((bytea = (l_uint8 *)LEPT_CALLOC(8 + 256 * n, sizeof(l_uint8))) == NULL)
3114  return (l_uint8 *)ERROR_PTR("bytea not made", procName, NULL);
3115  *psize = 8 + 256 * n;
3116  l_setDataFourBytes(bytea, 0, w);
3117  l_setDataFourBytes(bytea, 1, h);
3118  for (i = 0; i < n; i++) {
3119  na1 = numaaGetNuma(naa, i, L_COPY);
3120  numaGetMax(na1, &maxval, NULL);
3121  na2 = numaTransform(na1, 0, 255.0 / maxval);
3122  for (j = 0; j < 256; j++) {
3123  numaGetIValue(na2, j, &ival);
3124  bytea[8 + 256 * i + j] = ival;
3125  }
3126  numaDestroy(&na1);
3127  numaDestroy(&na2);
3128  }
3129 
3130  return bytea;
3131 }
3132 
3133 
3154 NUMAA *
3156  size_t size,
3157  l_int32 *pw,
3158  l_int32 *ph)
3159 {
3160 l_int32 i, j, n;
3161 NUMA *na;
3162 NUMAA *naa;
3163 
3164  PROCNAME("l_uncompressGrayHistograms");
3165 
3166  if (pw) *pw = 0;
3167  if (ph) *ph = 0;
3168  if (!pw || !ph)
3169  return (NUMAA *)ERROR_PTR("&w and &h not both defined", procName, NULL);
3170  if (!bytea)
3171  return (NUMAA *)ERROR_PTR("bytea not defined", procName, NULL);
3172  n = (size - 8) / 256;
3173  if ((size - 8) % 256 != 0)
3174  return (NUMAA *)ERROR_PTR("bytea size is invalid", procName, NULL);
3175 
3176  *pw = l_getDataFourBytes(bytea, 0);
3177  *ph = l_getDataFourBytes(bytea, 1);
3178  naa = numaaCreate(n);
3179  for (i = 0; i < n; i++) {
3180  na = numaCreate(256);
3181  for (j = 0; j < 256; j++)
3182  numaAddNumber(na, bytea[8 + 256 * i + j]);
3183  numaaAddNuma(naa, na, L_INSERT);
3184  }
3185 
3186  return naa;
3187 }
3188 
3189 
3190 /*------------------------------------------------------------------*
3191  * Translated images at the same resolution *
3192  *------------------------------------------------------------------*/
3223 l_ok
3225  PIX *pix2,
3226  l_int32 thresh,
3227  l_int32 *pdelx,
3228  l_int32 *pdely,
3229  l_float32 *pscore,
3230  l_int32 debugflag)
3231 {
3232 l_uint8 *subtab;
3233 l_int32 i, level, area1, area2, delx, dely;
3234 l_int32 etransx, etransy, maxshift, dbint;
3235 l_int32 *stab, *ctab;
3236 l_float32 cx1, cx2, cy1, cy2, score;
3237 PIX *pixb1, *pixb2, *pixt1, *pixt2, *pixt3, *pixt4;
3238 PIXA *pixa1, *pixa2, *pixadb;
3239 
3240  PROCNAME("pixCompareWithTranslation");
3241 
3242  if (pdelx) *pdelx = 0;
3243  if (pdely) *pdely = 0;
3244  if (pscore) *pscore = 0.0;
3245  if (!pdelx || !pdely)
3246  return ERROR_INT("&delx and &dely not defined", procName, 1);
3247  if (!pscore)
3248  return ERROR_INT("&score not defined", procName, 1);
3249  if (!pix1)
3250  return ERROR_INT("pix1 not defined", procName, 1);
3251  if (!pix2)
3252  return ERROR_INT("pix2 not defined", procName, 1);
3253 
3254  /* Make tables */
3255  subtab = makeSubsampleTab2x();
3256  stab = makePixelSumTab8();
3257  ctab = makePixelCentroidTab8();
3258 
3259  /* Binarize each image */
3260  pixb1 = pixConvertTo1(pix1, thresh);
3261  pixb2 = pixConvertTo1(pix2, thresh);
3262 
3263  /* Make a cascade of 2x reduced images for each, thresholding
3264  * with level 2 (neutral), down to 8x reduction */
3265  pixa1 = pixaCreate(4);
3266  pixa2 = pixaCreate(4);
3267  if (debugflag)
3268  pixadb = pixaCreate(4);
3269  pixaAddPix(pixa1, pixb1, L_INSERT);
3270  pixaAddPix(pixa2, pixb2, L_INSERT);
3271  for (i = 0; i < 3; i++) {
3272  pixt1 = pixReduceRankBinary2(pixb1, 2, subtab);
3273  pixt2 = pixReduceRankBinary2(pixb2, 2, subtab);
3274  pixaAddPix(pixa1, pixt1, L_INSERT);
3275  pixaAddPix(pixa2, pixt2, L_INSERT);
3276  pixb1 = pixt1;
3277  pixb2 = pixt2;
3278  }
3279 
3280  /* At the lowest level, use the centroids with a maxshift of 6
3281  * to search for the best alignment. Then at higher levels,
3282  * use the result from the level below as the initial approximation
3283  * for the alignment, and search with a maxshift of 2. */
3284  for (level = 3; level >= 0; level--) {
3285  pixt1 = pixaGetPix(pixa1, level, L_CLONE);
3286  pixt2 = pixaGetPix(pixa2, level, L_CLONE);
3287  pixCountPixels(pixt1, &area1, stab);
3288  pixCountPixels(pixt2, &area2, stab);
3289  if (level == 3) {
3290  pixCentroid(pixt1, ctab, stab, &cx1, &cy1);
3291  pixCentroid(pixt2, ctab, stab, &cx2, &cy2);
3292  etransx = lept_roundftoi(cx1 - cx2);
3293  etransy = lept_roundftoi(cy1 - cy2);
3294  maxshift = 6;
3295  } else {
3296  etransx = 2 * delx;
3297  etransy = 2 * dely;
3298  maxshift = 2;
3299  }
3300  dbint = (debugflag) ? level + 1 : 0;
3301  pixBestCorrelation(pixt1, pixt2, area1, area2, etransx, etransy,
3302  maxshift, stab, &delx, &dely, &score, dbint);
3303  if (debugflag) {
3304  fprintf(stderr, "Level %d: delx = %d, dely = %d, score = %7.4f\n",
3305  level, delx, dely, score);
3306  pixRasteropIP(pixt2, delx, dely, L_BRING_IN_WHITE);
3307  pixt3 = pixDisplayDiffBinary(pixt1, pixt2);
3308  pixt4 = pixExpandReplicate(pixt3, 8 / (1 << (3 - level)));
3309  pixaAddPix(pixadb, pixt4, L_INSERT);
3310  pixDestroy(&pixt3);
3311  }
3312  pixDestroy(&pixt1);
3313  pixDestroy(&pixt2);
3314  }
3315 
3316  if (debugflag) {
3317  pixaConvertToPdf(pixadb, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
3318  "/tmp/lept/comp/compare.pdf");
3319  convertFilesToPdf("/tmp/lept/comp", "correl_", 30, 1.0, L_FLATE_ENCODE,
3320  0, "Correlation scores at levels 1 through 5",
3321  "/tmp/lept/comp/correl.pdf");
3322  pixaDestroy(&pixadb);
3323  }
3324 
3325  *pdelx = delx;
3326  *pdely = dely;
3327  *pscore = score;
3328  pixaDestroy(&pixa1);
3329  pixaDestroy(&pixa2);
3330  LEPT_FREE(subtab);
3331  LEPT_FREE(stab);
3332  LEPT_FREE(ctab);
3333  return 0;
3334 }
3335 
3336 
3377 l_ok
3379  PIX *pix2,
3380  l_int32 area1,
3381  l_int32 area2,
3382  l_int32 etransx,
3383  l_int32 etransy,
3384  l_int32 maxshift,
3385  l_int32 *tab8,
3386  l_int32 *pdelx,
3387  l_int32 *pdely,
3388  l_float32 *pscore,
3389  l_int32 debugflag)
3390 {
3391 l_int32 shiftx, shifty, delx, dely;
3392 l_int32 *tab;
3393 l_float32 maxscore, score;
3394 FPIX *fpix;
3395 PIX *pix3, *pix4;
3396 
3397  PROCNAME("pixBestCorrelation");
3398 
3399  if (pdelx) *pdelx = 0;
3400  if (pdely) *pdely = 0;
3401  if (pscore) *pscore = 0.0;
3402  if (!pix1 || pixGetDepth(pix1) != 1)
3403  return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1);
3404  if (!pix2 || pixGetDepth(pix2) != 1)
3405  return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1);
3406  if (!area1 || !area2)
3407  return ERROR_INT("areas must be > 0", procName, 1);
3408 
3409  if (debugflag > 0)
3410  fpix = fpixCreate(2 * maxshift + 1, 2 * maxshift + 1);
3411 
3412  if (!tab8)
3413  tab = makePixelSumTab8();
3414  else
3415  tab = tab8;
3416 
3417  /* Search over a set of {shiftx, shifty} for the max */
3418  maxscore = 0;
3419  delx = etransx;
3420  dely = etransy;
3421  for (shifty = -maxshift; shifty <= maxshift; shifty++) {
3422  for (shiftx = -maxshift; shiftx <= maxshift; shiftx++) {
3423  pixCorrelationScoreShifted(pix1, pix2, area1, area2,
3424  etransx + shiftx,
3425  etransy + shifty, tab, &score);
3426  if (debugflag > 0) {
3427  fpixSetPixel(fpix, maxshift + shiftx, maxshift + shifty,
3428  1000.0 * score);
3429 /* fprintf(stderr, "(sx, sy) = (%d, %d): score = %6.4f\n",
3430  shiftx, shifty, score); */
3431  }
3432  if (score > maxscore) {
3433  maxscore = score;
3434  delx = etransx + shiftx;
3435  dely = etransy + shifty;
3436  }
3437  }
3438  }
3439 
3440  if (debugflag > 0) {
3441  lept_mkdir("lept/comp");
3442  char buf[128];
3443  pix3 = fpixDisplayMaxDynamicRange(fpix);
3444  pix4 = pixExpandReplicate(pix3, 20);
3445  snprintf(buf, sizeof(buf), "/tmp/lept/comp/correl_%d.png",
3446  debugflag);
3447  pixWrite(buf, pix4, IFF_PNG);
3448  pixDestroy(&pix3);
3449  pixDestroy(&pix4);
3450  fpixDestroy(&fpix);
3451  }
3452 
3453  if (pdelx) *pdelx = delx;
3454  if (pdely) *pdely = dely;
3455  if (pscore) *pscore = maxscore;
3456  if (!tab8) LEPT_FREE(tab);
3457  return 0;
3458 }
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:197
l_ok pixCompareGrayByHisto(PIX *pix1, PIX *pix2, BOX *box1, BOX *box2, l_float32 minratio, l_int32 maxgray, l_int32 factor, l_int32 nx, l_int32 ny, l_float32 *pscore, l_int32 debugflag)
pixCompareGrayByHisto()
Definition: compare.c:2759
NUMA * pixGetGrayHistogram(PIX *pixs, l_int32 factor)
pixGetGrayHistogram()
Definition: pix4.c:109
void bmfDestroy(L_BMF **pbmf)
bmfDestroy()
Definition: bmf.c:166
PIX * pixConvertTo1(PIX *pixs, l_int32 threshold)
pixConvertTo1()
Definition: pixconv.c:2933
l_ok numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:692
l_ok pixCorrelationBinary(PIX *pix1, PIX *pix2, l_float32 *pval)
pixCorrelationBinary()
Definition: compare.c:598
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:322
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:1944
l_ok pixGenPhotoHistos(PIX *pixs, BOX *box, l_int32 factor, l_float32 thresh, l_int32 nx, l_int32 ny, NUMAA **pnaa, l_int32 *pw, l_int32 *ph, l_int32 debugindex)
pixGenPhotoHistos()
Definition: compare.c:2215
Definition: pix.h:717
l_ok gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plottitle)
gplotAddPlot()
Definition: gplot.c:263
l_ok pixCentroid(PIX *pix, l_int32 *centtab, l_int32 *sumtab, l_float32 *pxave, l_float32 *pyave)
pixCentroid()
Definition: morphapp.c:1528
PIX * pixDilateGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateGray()
Definition: graymorph.c:274
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3233
l_int32 lept_roundftoi(l_float32 fval)
lept_roundftoi()
Definition: utils1.c:547
l_int32 * makePixelCentroidTab8(void)
makePixelCentroidTab8()
Definition: pix3.c:2337
PIX * pixConvertLossless(PIX *pixs, l_int32 d)
pixConvertLossless()
Definition: pixconv.c:3741
l_ok pixUsesCmapColor(PIX *pixs, l_int32 *pcolor)
pixUsesCmapColor()
Definition: compare.c:532
PIXA * pixaReadFiles(const char *dirname, const char *substr)
pixaReadFiles()
Definition: readfile.c:124
l_uint8 * l_compressGrayHistograms(NUMAA *naa, l_int32 w, l_int32 h, size_t *psize)
l_compressGrayHistograms()
Definition: compare.c:3087
l_ok numaWriteDebug(const char *filename, NUMA *na)
numaWriteDebug()
Definition: numabasic.c:1193
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:473
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:163
l_ok gplotMakeOutput(GPLOT *gplot)
gplotMakeOutput()
Definition: gplot.c:379
GPLOT * gplotCreate(const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel)
gplotCreate()
Definition: gplot.c:138
l_ok numaGetNonzeroRange(NUMA *na, l_float32 eps, l_int32 *pfirst, l_int32 *plast)
numaGetNonzeroRange()
Definition: numafunc1.c:1006
l_ok pixRasteropIP(PIX *pixd, l_int32 hshift, l_int32 vshift, l_int32 incolor)
pixRasteropIP()
Definition: rop.c:461
Definition: pix.h:716
l_ok numaEarthMoverDistance(NUMA *na1, NUMA *na2, l_float32 *pdist)
numaEarthMoverDistance()
Definition: numafunc2.c:2178
l_ok cmapEqual(PIXCMAP *cmap1, PIXCMAP *cmap2, l_int32 ncomps, l_int32 *psame)
cmapEqual()
Definition: compare.c:474
l_ok pixDecideIfText(PIX *pixs, BOX *box, l_int32 *pistext, PIXA *pixadb)
pixDecideIfText()
Definition: pageseg.c:1351
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:193
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3041
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:781
PIX * pixGetAverageTiled(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 type)
pixGetAverageTiled()
Definition: pix4.c:1653
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
l_ok pixSetMaskedGeneral(PIX *pixd, PIX *pixm, l_uint32 val, l_int32 x, l_int32 y)
pixSetMaskedGeneral()
Definition: pix3.c:294
PIXA * pixaSplitPix(PIX *pixs, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor)
pixaSplitPix()
Definition: pixabasic.c:346
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:741
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1395
l_ok numaWriteStream(FILE *fp, NUMA *na)
numaWriteStream()
Definition: numabasic.c:1246
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:187
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1624
l_ok numaSetCount(NUMA *na, l_int32 newcount)
numaSetCount()
Definition: numabasic.c:658
l_ok pixCompareBinary(PIX *pix1, PIX *pix2, l_int32 comptype, l_float32 *pfract, PIX **ppixdiff)
pixCompareBinary()
Definition: compare.c:712
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:443
NUMA * numaClipToInterval(NUMA *nas, l_int32 first, l_int32 last)
numaClipToInterval()
Definition: numafunc1.c:1105
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:239
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:968
PIX * pixPadToCenterCentroid(PIX *pixs, l_int32 factor)
pixPadToCenterCentroid()
Definition: compare.c:2327
NUMAA * numaaCreate(l_int32 n)
numaaCreate()
Definition: numabasic.c:1340
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1020
PIX * pixaDisplayTiledInColumns(PIXA *pixas, l_int32 nx, l_float32 scalefactor, l_int32 spacing, l_int32 border)
pixaDisplayTiledInColumns()
Definition: pixafunc2.c:1020
l_ok pixaccMultConst(PIXACC *pixacc, l_float32 factor)
pixaccMultConst()
Definition: pixacc.c:297
l_ok numaSetValue(NUMA *na, l_int32 index, l_float32 val)
numaSetValue()
Definition: numabasic.c:759
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1573
PIX * pixDisplayDiffBinary(PIX *pix1, PIX *pix2)
pixDisplayDiffBinary()
Definition: compare.c:656
l_ok pixCentroid8(PIX *pixs, l_int32 factor, l_float32 *pcx, l_float32 *pcy)
pixCentroid8()
Definition: compare.c:2381
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:3224
void numaaDestroy(NUMAA **pnaa)
numaaDestroy()
Definition: numabasic.c:1444
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2404
Definition: bmf.h:45
l_ok fpixSetPixel(FPIX *fpix, l_int32 x, l_int32 y, l_float32 val)
fpixSetPixel()
Definition: fpix1.c:683
l_ok pixPaintThroughMask(PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_uint32 val)
pixPaintThroughMask()
Definition: pix3.c:618
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:111
PIX * fpixDisplayMaxDynamicRange(FPIX *fpixs)
fpixDisplayMaxDynamicRange()
Definition: fpix2.c:424
void pixaccDestroy(PIXACC **ppixacc)
pixaccDestroy()
Definition: pixacc.c:160
NUMA * numaaGetNuma(NUMAA *naa, l_int32 index, l_int32 accessflag)
numaaGetNuma()
Definition: numabasic.c:1659
PIX * pixSubtractGray(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtractGray()
Definition: pixarith.c:353
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:727
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:859
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:503
Definition: array.h:59
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:1391
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:1457
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1574
PIX * pixAbsDifference(PIX *pixs1, PIX *pixs2)
pixAbsDifference()
Definition: pixarith.c:872
l_ok pixaComparePhotoRegionsByHisto(PIXA *pixa, l_float32 minratio, l_float32 textthresh, l_int32 factor, l_int32 nx, l_int32 ny, l_float32 simthresh, NUMA **pnai, l_float32 **pscores, PIX **ppixd, l_int32 debug)
pixaComparePhotoRegionsByHisto()
Definition: compare.c:1893
PIX * pixAnd(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixAnd()
Definition: pix3.c:1510
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:836
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:751
l_ok pixComparePhotoRegionsByHisto(PIX *pix1, PIX *pix2, BOX *box1, BOX *box2, l_float32 minratio, l_int32 factor, l_int32 nx, l_int32 ny, l_float32 *pscore, l_int32 debugflag)
pixComparePhotoRegionsByHisto()
Definition: compare.c:2105
l_ok pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1823
l_ok gplotSimple2(NUMA *na1, NUMA *na2, l_int32 outformat, const char *outroot, const char *title)
gplotSimple2()
Definition: gplot.c:604
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2297
l_ok pixEqualWithAlpha(PIX *pix1, PIX *pix2, l_int32 use_alpha, l_int32 *psame)
pixEqualWithAlpha()
Definition: compare.c:176
Definition: pix.h:546
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:887
static l_int32 pixCompareTilesByHisto(PIX *pix1, PIX *pix2, l_int32 maxgray, l_int32 factor, l_int32 nx, l_int32 ny, l_float32 *pscore, PIXA *pixadebug)
pixCompareTilesByHisto()
Definition: compare.c:2885
NUMA * pixCompareRankDifference(PIX *pix1, PIX *pix2, l_int32 factor)
pixCompareRankDifference()
Definition: compare.c:1218
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
Definition: gplot.h:75
PIX * pixCreateRGBImage(PIX *pixr, PIX *pixg, PIX *pixb)
pixCreateRGBImage()
Definition: pix2.c:2348
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:515
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1639
PIX * pixScaleToSize(PIX *pixs, l_int32 wd, l_int32 hd)
pixScaleToSize()
Definition: scale1.c:317
Definition: array.h:71
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
PIX * pixScaleByIntSampling(PIX *pixs, l_int32 factor)
pixScaleByIntSampling()
Definition: scale1.c:1445
l_ok pixcmapHasColor(PIXCMAP *cmap, l_int32 *pcolor)
pixcmapHasColor()
Definition: colormap.c:1002
l_ok pixCropAlignedToCentroid(PIX *pix1, PIX *pix2, l_int32 factor, BOX **pbox1, BOX **pbox2)
pixCropAlignedToCentroid()
Definition: compare.c:3012
PIX * pixaccFinal(PIXACC *pixacc, l_int32 outdepth)
pixaccFinal()
Definition: pixacc.c:192
Definition: pix.h:454
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:360
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:180
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
PIX * pixReduceRankBinary2(PIX *pixs, l_int32 level, l_uint8 *intab)
pixReduceRankBinary2()
Definition: binreduce.c:223
NUMA * numaTransform(NUMA *nas, l_float32 shift, l_float32 scale)
numaTransform()
Definition: numafunc2.c:409
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:2591
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:190
NUMA * pixGetDifferenceHistogram(PIX *pix1, PIX *pix2, l_int32 factor)
pixGetDifferenceHistogram()
Definition: compare.c:1484
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1459
NUMA * numaWindowedMean(NUMA *nas, l_int32 wc)
numaWindowedMean()
Definition: numafunc2.c:582
l_int32 l_getDataFourBytes(void *line, l_int32 n)
l_getDataFourBytes()
Definition: arrayaccess.c:343
PIX * pixExpandReplicate(PIX *pixs, l_int32 factor)
pixExpandReplicate()
Definition: scale2.c:867
FPIX * fpixCreate(l_int32 width, l_int32 height)
fpixCreate()
Definition: fpix1.c:153
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:865
l_ok pixCompareTiled(PIX *pix1, PIX *pix2, l_int32 sx, l_int32 sy, l_int32 type, PIX **ppixdiff)
pixCompareTiled()
Definition: compare.c:1124
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:752
l_int32 numaaGetCount(NUMAA *naa)
numaaGetCount()
Definition: numabasic.c:1550
PIXACC * pixaccCreate(l_int32 w, l_int32 h, l_int32 negflag)
pixaccCreate()
Definition: pixacc.c:90
PIXA * pixaScale(PIXA *pixas, l_float32 scalex, l_float32 scaley)
pixaScale()
Definition: pixafunc1.c:1970
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:672
l_int32 numaaGetNumaCount(NUMAA *naa, l_int32 index)
numaaGetNumaCount()
Definition: numabasic.c:1568
Definition: pix.h:718
l_ok pixRenderBoxArb(PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval)
pixRenderBoxArb()
Definition: graphics.c:1642
l_int32 pixcmapGetCount(PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:635
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:3378
Definition: pix.h:134
void l_setDataFourBytes(void *line, l_int32 n, l_int32 val)
l_setDataFourBytes()
Definition: arrayaccess.c:359
Definition: pix.h:719
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:1303
l_ok pixZero(PIX *pix, l_int32 *pempty)
pixZero()
Definition: pix3.c:1701
NUMAA * l_uncompressGrayHistograms(l_uint8 *bytea, size_t size, l_int32 *pw, l_int32 *ph)
l_uncompressGrayHistograms()
Definition: compare.c:3155
#define PIX_SRC
Definition: pix.h:327
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:788
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:628
l_ok pixAlphaIsOpaque(PIX *pix, l_int32 *popaque)
pixAlphaIsOpaque()
Definition: pix2.c:3235
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:278
l_ok pixEqual(PIX *pix1, PIX *pix2, l_int32 *psame)
pixEqual()
Definition: compare.c:150
Definition: pix.h:201
NUMA * numaNormalizeHistogram(NUMA *nas, l_float32 tsum)
numaNormalizeHistogram()
Definition: numafunc2.c:1172
PIX * pixAddTextlines(PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location)
pixAddTextlines()
Definition: textops.c:270
PIX * pixMinOrMax(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 type)
pixMinOrMax()
Definition: pixarith.c:1054
l_ok pixSetResolution(PIX *pix, l_int32 xres, l_int32 yres)
pixSetResolution()
Definition: pix1.c:1339
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:1382
l_ok pixDecideIfPhotoImage(PIX *pix, l_int32 factor, l_int32 nx, l_int32 ny, l_float32 thresh, NUMAA **pnaa, PIXA *pixadebug)
pixDecideIfPhotoImage()
Definition: compare.c:2459
l_ok numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:486
l_ok pixcmapGetRGBA(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *paval)
pixcmapGetRGBA()
Definition: colormap.c:819
l_ok pixaccAdd(PIXACC *pixacc, PIX *pix)
pixaccAdd()
Definition: pixacc.c:253
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:1615
l_int32 lept_rmdir(const char *subdir)
lept_rmdir()
Definition: utils2.c:2021
l_int32 pixSizesEqual(const PIX *pix1, const PIX *pix2)
pixSizesEqual()
Definition: pix1.c:781
l_ok pixEqualWithCmap(PIX *pix1, PIX *pix2, l_int32 *psame)
pixEqualWithCmap()
Definition: compare.c:378
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:341
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:373
l_ok boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:310
Definition: pix.h:480
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:408
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2737
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:165
l_ok grayInterHistogramStats(NUMAA *naa, l_int32 wc, NUMA **pnam, NUMA **pnams, NUMA **pnav, NUMA **pnarv)
grayInterHistogramStats()
Definition: numafunc2.c:2269
l_uint8 * makeSubsampleTab2x(void)
Permutation table for 2x rank binary reduction.
Definition: binreduce.c:384
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:631
l_ok numaGetSumOnInterval(NUMA *na, l_int32 first, l_int32 last, l_float32 *psum)
numaGetSumOnInterval()
Definition: numafunc1.c:599
l_ok gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:575
l_ok numaaAddNuma(NUMAA *naa, NUMA *na, l_int32 copyflag)
numaaAddNuma()
Definition: numabasic.c:1482
Definition: pix.h:582
PIX * pixColorMorph(PIX *pixs, l_int32 type, l_int32 hsize, l_int32 vsize)
pixColorMorph()
Definition: colormorph.c:66
l_ok pixGetPSNR(PIX *pix1, PIX *pix2, l_int32 factor, l_float32 *ppsnr)
pixGetPSNR()
Definition: compare.c:1778
L_BMF * bmfCreate(const char *dir, l_int32 fontsize)
bmfCreate()
Definition: bmf.c:114