Leptonica  1.73
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_int32
150 pixEqual(PIX *pix1,
151  PIX *pix2,
152  l_int32 *psame)
153 {
154  return pixEqualWithAlpha(pix1, pix2, 0, psame);
155 }
156 
157 
175 l_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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_int32
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  *------------------------------------------------------------------*/
1891 l_int32
1893  l_float32 minratio,
1894  l_float32 textthresh,
1895  l_int32 factor,
1896  l_int32 nx,
1897  l_int32 ny,
1898  l_float32 simthresh,
1899  NUMA **pnai,
1900  l_float32 **pscores,
1901  PIX **ppixd)
1902 {
1903 char *text;
1904 l_int32 i, j, n, w, h, w1, h1, w2, h2, ival, index;
1905 l_float32 score;
1906 l_float32 *scores;
1907 NUMA *nai, *naw, *nah;
1908 NUMAA *naa;
1909 NUMAA **n3a; /* array of naa */
1910 PIX *pix;
1911 
1912  PROCNAME("pixaComparePhotoRegionsByHisto");
1913 
1914  if (pscores) *pscores = NULL;
1915  if (ppixd) *ppixd = NULL;
1916  if (!pnai)
1917  return ERROR_INT("&na not defined", procName, 1);
1918  *pnai = NULL;
1919  if (!pixa)
1920  return ERROR_INT("pixa not defined", procName, 1);
1921  if (minratio < 0.0 || minratio > 1.0)
1922  return ERROR_INT("minratio not in [0.0 ... 1.0]", procName, 1);
1923  if (textthresh <= 0.0) textthresh = 1.3;
1924  if (factor < 1)
1925  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
1926  if (nx < 1 || ny < 1)
1927  return ERROR_INT("nx and ny must both be > 0", procName, 1);
1928  if (simthresh <= 0.0) simthresh = 0.25;
1929  if (simthresh > 1.0)
1930  return ERROR_INT("simthresh invalid; should be near 0.25", procName, 1);
1931 
1932  /* Prepare the histograms */
1933  n = pixaGetCount(pixa);
1934  if ((n3a = (NUMAA **)LEPT_CALLOC(n, sizeof(NUMAA *))) == NULL)
1935  return ERROR_INT("calloc fail for n3a", procName, 1);
1936  naw = numaCreate(0);
1937  nah = numaCreate(0);
1938  for (i = 0; i < n; i++) {
1939  pix = pixaGetPix(pixa, i, L_CLONE);
1940  text = pixGetText(pix);
1941  pixSetResolution(pix, 150, 150);
1942  pixGenPhotoHistos(pix, NULL, factor, textthresh, nx, ny,
1943  &naa, &w, &h, FALSE);
1944  n3a[i] = naa;
1945  numaAddNumber(naw, w);
1946  numaAddNumber(nah, h);
1947  if (naa)
1948  fprintf(stderr, "Image %s is photo\n", text);
1949  else
1950  fprintf(stderr, "Image %s is NOT photo\n", text);
1951  pixDestroy(&pix);
1952  }
1953 
1954  /* Do the comparisons. We are making a set of classes, where
1955  * all similar images are placed in the same class. There are
1956  * 'n' input images. The classes are labeled by 'index' (all
1957  * similar images get the same 'index' value), and 'nai' maps
1958  * the index of the image in the input array to the index
1959  * of the similarity class. */
1960  if ((scores = (l_float32 *)LEPT_CALLOC(n * n, sizeof(l_float32))) == NULL) {
1961  L_ERROR("calloc fail for scores\n", procName);
1962  goto cleanup;
1963  }
1964  nai = numaMakeConstant(-1, n); /* index */
1965  for (i = 0, index = 0; i < n; i++) {
1966  scores[n * i + i] = 1.0;
1967  numaGetIValue(nai, i, &ival);
1968  if (ival != -1) /* already set */
1969  continue;
1970  numaSetValue(nai, i, index);
1971  if (n3a[i] == NULL) { /* not a photo */
1972  index++;
1973  continue;
1974  }
1975  numaGetIValue(naw, i, &w1);
1976  numaGetIValue(nah, i, &h1);
1977  for (j = i + 1; j < n; j++) {
1978  numaGetIValue(nai, j, &ival);
1979  if (ival != -1) /* already set */
1980  continue;
1981  if (n3a[j] == NULL) /* not a photo */
1982  continue;
1983  numaGetIValue(naw, j, &w2);
1984  numaGetIValue(nah, j, &h2);
1985  compareTilesByHisto(n3a[i], n3a[j], minratio, w1, h1, w2, h2,
1986  &score, NULL);
1987  scores[n * i + j] = score;
1988  scores[n * j + i] = score; /* the score array is symmetric */
1989 /* fprintf(stderr, "score = %5.3f\n", score); */
1990  if (score > simthresh) {
1991  numaSetValue(nai, j, index);
1992  fprintf(stderr, "Setting %d similar to %d, in class %d\n",
1993  j, i, index);
1994  }
1995  }
1996  index++;
1997  }
1998  *pnai = nai;
1999 
2000  /* Debug: optionally save and display the score array.
2001  * All images that are photos are represented by a point on
2002  * the diagonal. Other images in the same similarity class
2003  * are on the same horizontal raster line to the right.
2004  * The array has been symmetrized, so images in the same
2005  * same similarity class also appear on the same column below. */
2006  if (pscores) {
2007  l_int32 wpl, fact;
2008  l_uint32 *line, *data;
2009  PIX *pix2, *pix3;
2010  pix2 = pixCreate(n, n, 8);
2011  data = pixGetData(pix2);
2012  wpl = pixGetWpl(pix2);
2013  for (i = 0; i < n; i++) {
2014  line = data + i * wpl;
2015  for (j = 0; j < n; j++) {
2016  SET_DATA_BYTE(line, j,
2017  L_MIN(255, 4.0 * 255 * scores[n * i + j]));
2018  }
2019  }
2020  fact = L_MAX(2, 1000 / n);
2021  pix3 = pixExpandReplicate(pix2, fact);
2022  fprintf(stderr, "Writing to /tmp/lept/comp/scorearray.png\n");
2023  lept_mkdir("lept/comp");
2024  pixWrite("/tmp/lept/comp/scorearray.png", pix3, IFF_PNG);
2025  pixDestroy(&pix2);
2026  pixDestroy(&pix3);
2027  *pscores = scores;
2028  } else {
2029  LEPT_FREE(scores);
2030  }
2031 
2032  /* Debug: optionally display and save the image comparisons.
2033  * Image similarity classes are displayed by column; similar
2034  * images are displayed in the same column. */
2035  if (ppixd)
2036  *ppixd = pixaDisplayTiledByIndex(pixa, nai, 200, 20, 2, 6, 0x0000ff00);
2037 
2038 cleanup:
2039  numaDestroy(&naw);
2040  numaDestroy(&nah);
2041  for (i = 0; i < n; i++)
2042  numaaDestroy(&n3a[i]);
2043  LEPT_FREE(n3a);
2044  return 0;
2045 }
2046 
2047 
2099 l_int32
2101  PIX *pix2,
2102  BOX *box1,
2103  BOX *box2,
2104  l_float32 minratio,
2105  l_int32 factor,
2106  l_int32 nx,
2107  l_int32 ny,
2108  l_float32 *pscore,
2109  l_int32 debugflag)
2110 {
2111 l_int32 w1, h1, w2, h2, w1c, h1c, w2c, h2c;
2112 l_float32 wratio, hratio;
2113 NUMAA *naa1, *naa2;
2114 PIX *pix3, *pix4;
2115 PIXA *pixa;
2116 
2117  PROCNAME("pixComparePhotoRegionsByHisto");
2118 
2119  if (!pscore)
2120  return ERROR_INT("&score not defined", procName, 1);
2121  *pscore = 0.0;
2122  if (!pix1 || !pix2)
2123  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
2124  if (minratio < 0.5 || minratio > 1.0)
2125  return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1);
2126  if (factor < 1)
2127  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2128  if (nx < 1 || ny < 1)
2129  return ERROR_INT("nx and ny must both be > 0", procName, 1);
2130 
2131  if (debugflag)
2132  lept_mkdir("lept/comp");
2133 
2134  /* Initial filter by size */
2135  if (box1)
2136  boxGetGeometry(box1, NULL, NULL, &w1, &h1);
2137  else
2138  pixGetDimensions(pix1, &w1, &h1, NULL);
2139  if (box2)
2140  boxGetGeometry(box2, NULL, NULL, &w2, &h2);
2141  else
2142  pixGetDimensions(pix1, &w2, &h2, NULL);
2143  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2144  (l_float32)w2 / (l_float32)w1;
2145  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2146  (l_float32)h2 / (l_float32)h1;
2147  if (wratio < minratio || hratio < minratio)
2148  return 0;
2149 
2150  /* Initial crop, if necessary, and make histos */
2151  if (box1)
2152  pix3 = pixClipRectangle(pix1, box1, NULL);
2153  else
2154  pix3 = pixClone(pix1);
2155  pixGenPhotoHistos(pix3, NULL, factor, 0, nx, ny,
2156  &naa1, &w1c, &h1c, debugflag);
2157  pixDestroy(&pix3);
2158  if (!naa1) return 0;
2159  if (box2)
2160  pix4 = pixClipRectangle(pix2, box2, NULL);
2161  else
2162  pix4 = pixClone(pix2);
2163  pixGenPhotoHistos(pix4, NULL, factor, 0, nx, ny,
2164  &naa2, &w2c, &h2c, debugflag);
2165  pixDestroy(&pix4);
2166  if (!naa2) return 0;
2167 
2168  /* Compare histograms */
2169  pixa = (debugflag) ? pixaCreate(0) : NULL;
2170  compareTilesByHisto(naa1, naa2, minratio, w1c, h1c, w2c, h2c, pscore, pixa);
2171  pixaDestroy(&pixa);
2172  return 0;
2173 }
2174 
2175 
2206 l_int32
2208  BOX *box,
2209  l_int32 factor,
2210  l_float32 thresh,
2211  l_int32 nx,
2212  l_int32 ny,
2213  NUMAA **pnaa,
2214  l_int32 *pw,
2215  l_int32 *ph,
2216  l_int32 debugflag)
2217 {
2218 NUMAA *naa;
2219 PIX *pix1, *pix2, *pix3, *pixm;
2220 PIXA *pixa;
2221 
2222  PROCNAME("pixGenPhotoHistos");
2223 
2224  if (pnaa) *pnaa = NULL;
2225  if (pw) *pw = 0;
2226  if (ph) *ph = 0;
2227  if (!pnaa)
2228  return ERROR_INT("&naa not defined", procName, 1);
2229  if (!pw || !ph)
2230  return ERROR_INT("&w and &h not both defined", procName, 1);
2231  if (!pixs || pixGetDepth(pixs) == 1)
2232  return ERROR_INT("pixs not defined or 1 bpp", procName, 1);
2233  if (factor < 1)
2234  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2235  if (nx < 1 || ny < 1)
2236  return ERROR_INT("nx and ny must both be > 0", procName, 1);
2237  if (thresh <= 0.0) thresh = 1.3; /* default */
2238 
2239  pixa = NULL;
2240  if (debugflag) {
2241  pixa = pixaCreate(0);
2242  lept_mkdir("lept/comp");
2243  }
2244 
2245  /* Initial crop, if necessary */
2246  if (box)
2247  pix1 = pixClipRectangle(pixs, box, NULL);
2248  else
2249  pix1 = pixClone(pixs);
2250 
2251  /* Convert to 8 bpp and pad to center the centroid */
2252  pix2 = pixConvertTo8(pix1, FALSE);
2253  pix3 = pixPadToCenterCentroid(pix2, factor);
2254 
2255  /* Set to 255 all pixels above 230. Do this so that light gray
2256  * pixels do not enter into the comparison. */
2257  pixm = pixThresholdToBinary(pix3, 230);
2258  pixInvert(pixm, pixm);
2259  pixSetMaskedGeneral(pix3, pixm, 255, 0, 0);
2260  pixDestroy(&pixm);
2261 
2262  if (debugflag) {
2263  PIX *pix4, *pix5, *pix6, *pix7, *pix8;
2264  PIXA *pixa2;
2265  pix4 = pixConvertTo32(pix2);
2266  pix5 = pixConvertTo32(pix3);
2267  pix6 = pixScaleToSize(pix4, 400, 0);
2268  pix7 = pixScaleToSize(pix5, 400, 0);
2269  pixa2 = pixaCreate(2);
2270  pixaAddPix(pixa2, pix6, L_INSERT);
2271  pixaAddPix(pixa2, pix7, L_INSERT);
2272  pix8 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 3);
2273  pixaAddPix(pixa, pix8, L_INSERT);
2274  pixDestroy(&pix4);
2275  pixDestroy(&pix5);
2276  pixaDestroy(&pixa2);
2277  }
2278  pixDestroy(&pix1);
2279  pixDestroy(&pix2);
2280 
2281  /* Test if this is a photoimage */
2282  pixDecideIfPhotoImage(pix3, factor, nx, ny, thresh, &naa, pixa);
2283  if (naa) {
2284  *pnaa = naa;
2285  *pw = pixGetWidth(pix3);
2286  *ph = pixGetHeight(pix3);
2287  }
2288 
2289  if (pixa) {
2290  fprintf(stderr, "Writing to /tmp/lept/comp/tiledhistos.pdf\n");
2291  pixaConvertToPdf(pixa, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
2292  "/tmp/lept/comp/tiledhistos.pdf");
2293  pixaDestroy(&pixa);
2294  }
2295 
2296  pixDestroy(&pix3);
2297  return 0;
2298 }
2299 
2300 
2316 PIX *
2318  l_int32 factor)
2319 
2320 {
2321 l_float32 cx, cy;
2322 l_int32 xs, ys, delx, dely, icx, icy, ws, hs, wd, hd;
2323 PIX *pix1, *pixd;
2324 
2325  PROCNAME("pixPadToCenterCentroid");
2326 
2327  if (!pixs)
2328  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2329  if (factor < 1)
2330  return (PIX *)ERROR_PTR("invalid sampling factor", procName, NULL);
2331 
2332  pix1 = pixConvertTo8(pixs, FALSE);
2333  pixCentroid8(pix1, factor, &cx, &cy);
2334  icx = (l_int32)(cx + 0.5);
2335  icy = (l_int32)(cy + 0.5);
2336  pixGetDimensions(pix1, &ws, &hs, NULL);
2337  delx = ws - 2 * icx;
2338  dely = hs - 2 * icy;
2339  xs = L_MAX(0, delx);
2340  ys = L_MAX(0, dely);
2341  wd = 2 * L_MAX(icx, ws - icx);
2342  hd = 2 * L_MAX(icy, hs - icy);
2343  pixd = pixCreate(wd, hd, 8);
2344  pixSetAll(pixd); /* to white */
2345  pixCopyResolution(pixd, pixs);
2346  pixRasterop(pixd, xs, ys, ws, hs, PIX_SRC, pix1, 0, 0);
2347  pixDestroy(&pix1);
2348  return pixd;
2349 }
2350 
2351 
2370 l_int32
2372  l_int32 factor,
2373  l_float32 *pcx,
2374  l_float32 *pcy)
2375 {
2376 l_int32 i, j, w, h, wpl, val;
2377 l_float32 sumx, sumy, sumv;
2378 l_uint32 *data, *line;
2379 PIX *pix1;
2380 
2381  PROCNAME("pixCentroid8");
2382 
2383  if (pcx) *pcx = 0.0;
2384  if (pcy) *pcy = 0.0;
2385  if (!pixs || pixGetDepth(pixs) != 8)
2386  return ERROR_INT("pixs undefined or not 8 bpp", procName, 1);
2387  if (factor < 1)
2388  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2389  if (!pcx || !pcy)
2390  return ERROR_INT("&cx and &cy not both defined", procName, 1);
2391 
2392  pix1 = pixInvert(NULL, pixs);
2393  pixGetDimensions(pix1, &w, &h, NULL);
2394  data = pixGetData(pix1);
2395  wpl = pixGetWpl(pix1);
2396  sumx = sumy = sumv = 0.0;
2397  for (i = 0; i < h; i++) {
2398  line = data + i * wpl;
2399  for (j = 0; j < w; j++) {
2400  val = GET_DATA_BYTE(line, j);
2401  sumx += val * j;
2402  sumy += val * i;
2403  sumv += val;
2404  }
2405  }
2406  pixDestroy(&pix1);
2407 
2408  if (sumv == 0) {
2409  L_INFO("input image is white\n", procName);
2410  *pcx = (l_float32)(w) / 2;
2411  *pcy = (l_float32)(h) / 2;
2412  } else {
2413  *pcx = sumx / sumv;
2414  *pcy = sumy / sumv;
2415  }
2416 
2417  return 0;
2418 }
2419 
2420 
2448 l_int32
2450  l_int32 factor,
2451  l_int32 nx,
2452  l_int32 ny,
2453  l_float32 thresh,
2454  NUMAA **pnaa,
2455  PIXA *pixadebug)
2456 {
2457 char buf[64];
2458 l_int32 i, n, istext, isphoto;
2459 l_float32 maxval, sum1, sum2, ratio;
2460 L_BMF *bmf;
2461 NUMA *na1, *na2, *na3, *narv;
2462 NUMAA *naa;
2463 PIX *pix1;
2464 PIXA *pixa, *pixa2;
2465 
2466  PROCNAME("pixDecideIfPhotoImage");
2467 
2468  if (!pnaa)
2469  return ERROR_INT("&naa not defined", procName, 1);
2470  *pnaa = NULL;
2471  if (!pix || pixGetDepth(pix) != 8 || pixGetColormap(pix))
2472  return ERROR_INT("pix undefined or invalid", procName, 1);
2473  if (thresh <= 0.0) thresh = 1.3; /* default */
2474 
2475  /* Look for text lines */
2476  pixDecideIfText(pix, NULL, &istext, pixadebug);
2477  if (istext) {
2478  L_INFO("Image is text\n", procName);
2479  return 0;
2480  }
2481 
2482  /* Evaluate histograms in each tile */
2483  pixa = pixaSplitPix(pix, nx, ny, 0, 0);
2484  n = nx * ny;
2485  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
2486  naa = numaaCreate(n);
2487  for (i = 0; i < n; i++) {
2488  pix1 = pixaGetPix(pixa, i, L_CLONE);
2489 
2490  /* Get histograms, set white count to 0, normalize max to 255 */
2491  na1 = pixGetGrayHistogram(pix1, factor);
2492  numaSetValue(na1, 255, 0);
2493  na2 = numaWindowedMean(na1, 5); /* do some smoothing */
2494  numaGetMax(na2, &maxval, NULL);
2495  na3 = numaTransform(na2, 0, 255.0 / maxval);
2496  if (pixadebug) {
2497  snprintf(buf, sizeof(buf), "/tmp/lept/comp/plot.%d", i);
2498  gplotSimple1(na3, GPLOT_PNG, buf, "Histos");
2499  }
2500 
2501  numaaAddNuma(naa, na3, L_INSERT);
2502  numaDestroy(&na1);
2503  numaDestroy(&na2);
2504  pixDestroy(&pix1);
2505  }
2506  if (pixadebug) {
2507  pixa2 = pixaReadFiles("/tmp/lept/comp", ".png");
2508  pixaJoin(pixa, pixa2, 0, -1);
2509  pixaDestroy(&pixa2);
2510  }
2511 
2512  /* Compute the standard deviation between these histos to decide
2513  * if the image is photo or something more like line art,
2514  * which does not support good comparison by tiled histograms. */
2515  grayInterHistogramStats(naa, 5, NULL, NULL, NULL, &narv);
2516 
2517  /* For photos, the root variance has a larger weight of
2518  * values in the range [50 ... 150] compared to [200 ... 230],
2519  * than text or line art. For the latter, most of the variance
2520  * between tiles is in the lightest parts of the image, well
2521  * above 150. */
2522  numaGetSumOnInterval(narv, 50, 150, &sum1);
2523  numaGetSumOnInterval(narv, 200, 230, &sum2);
2524  if (sum2 == 0.0) /* shouldn't happen */
2525  isphoto = 0; /* be conservative */
2526  else {
2527  ratio = sum1 / sum2;
2528  isphoto = (ratio > thresh) ? 1 : 0;
2529  if (pixadebug) {
2530  if (isphoto)
2531  L_INFO("ratio %f > %f; isphoto is true\n",
2532  procName, ratio, thresh);
2533  else
2534  L_INFO("ratio %f < %f; isphoto is false\n",
2535  procName, ratio, thresh);
2536  }
2537  }
2538  if (isphoto)
2539  *pnaa = naa;
2540  else
2541  numaaDestroy(&naa);
2542  bmfDestroy(&bmf);
2543  numaDestroy(&narv);
2544  pixaDestroy(&pixa);
2545  return 0;
2546 }
2547 
2548 
2571 l_int32
2573  NUMAA *naa2,
2574  l_float32 minratio,
2575  l_int32 w1,
2576  l_int32 h1,
2577  l_int32 w2,
2578  l_int32 h2,
2579  l_float32 *pscore,
2580  PIXA *pixadebug)
2581 {
2582 char buf1[128], buf2[128];
2583 l_int32 i, n;
2584 l_float32 wratio, hratio, score, minscore, dist;
2585 L_BMF *bmf;
2586 NUMA *na1, *na2, *nadist, *nascore;
2587 
2588  PROCNAME("compareTilesByHisto");
2589 
2590  if (!pscore)
2591  return ERROR_INT("&score not defined", procName, 1);
2592  *pscore = 0.0;
2593  if (!naa1 || !naa2)
2594  return ERROR_INT("naa1 and naa2 not both defined", procName, 1);
2595 
2596  /* Filter for different sizes */
2597  n = numaaGetCount(naa1);
2598  if (n != numaaGetCount(naa2))
2599  return ERROR_INT("naa1 and naa2 are different size", procName, 1);
2600 
2601  if (pixadebug) {
2602  lept_rmdir("lept/comptile");
2603  lept_mkdir("lept/comptile");
2604  }
2605 
2606  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2607  (l_float32)w2 / (l_float32)w1;
2608  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2609  (l_float32)h2 / (l_float32)h1;
2610  if (wratio < minratio || hratio < minratio) {
2611  if (pixadebug)
2612  L_INFO("Sizes differ: wratio = %f, hratio = %f\n",
2613  procName, wratio, hratio);
2614  return 0;
2615  }
2616 
2617  /* Evaluate histograms in each tile. Remove white before
2618  * computing EMD, because there are may be a lot of white
2619  * pixels due to padding, and we don't want to include them.
2620  * This also makes the debug histo plots more informative. */
2621  minscore = 1.0;
2622  nadist = numaCreate(n);
2623  nascore = numaCreate(n);
2624  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
2625  for (i = 0; i < n; i++) {
2626  na1 = numaaGetNuma(naa1, i, L_CLONE);
2627  na2 = numaaGetNuma(naa2, i, L_CLONE);
2628  numaSetValue(na1, 255, 0.0);
2629  numaSetValue(na2, 255, 0.0);
2630 
2631  /* To compare histograms, use the normalized earthmover distance.
2632  * Further normalize to get the EM distance as a fraction of the
2633  * maximum distance in the histogram (255). Finally, scale this
2634  * up by 10.0, and subtract from 1.0 to get a similarity score. */
2635  numaEarthMoverDistance(na1, na2, &dist);
2636  score = L_MAX(0.0, 1.0 - 10.0 * (dist / 255.));
2637  numaAddNumber(nadist, dist);
2638  numaAddNumber(nascore, score);
2639  minscore = L_MIN(minscore, score);
2640  if (pixadebug) {
2641  snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d", i);
2642  gplotSimple2(na1, na2, GPLOT_PNG, buf1, "Histos");
2643  }
2644  numaDestroy(&na1);
2645  numaDestroy(&na2);
2646  }
2647  *pscore = minscore;
2648 
2649  if (pixadebug) {
2650  for (i = 0; i < n; i++) {
2651  PIX *pix1, *pix2;
2652  snprintf(buf1, sizeof(buf1), "/tmp/lept/comptile/plot.%d.png", i);
2653  pix1 = pixRead(buf1);
2654  numaGetFValue(nadist, i, &dist);
2655  numaGetFValue(nascore, i, &score);
2656  snprintf(buf2, sizeof(buf2),
2657  "Image %d\ndist = %5.3f, score = %5.3f", i, dist, score);
2658  pix2 = pixAddTextlines(pix1, bmf, buf2, 0x0000ff00, L_ADD_BELOW);
2659  pixaAddPix(pixadebug, pix2, L_INSERT);
2660  pixDestroy(&pix1);
2661  }
2662  fprintf(stderr, "Writing to /tmp/lept/comptile/comparegray.pdf\n");
2663  pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
2664  "/tmp/lept/comptile/comparegray.pdf");
2665  numaWrite("/tmp/lept/comptile/scores.na", nascore);
2666  numaWrite("/tmp/lept/comptile/dists.na", nadist);
2667  }
2668 
2669  bmfDestroy(&bmf);
2670  numaDestroy(&nadist);
2671  numaDestroy(&nascore);
2672  return 0;
2673 }
2674 
2675 
2739 l_int32
2741  PIX *pix2,
2742  BOX *box1,
2743  BOX *box2,
2744  l_float32 minratio,
2745  l_int32 maxgray,
2746  l_int32 factor,
2747  l_int32 nx,
2748  l_int32 ny,
2749  l_float32 *pscore,
2750  l_int32 debugflag)
2751 {
2752 l_int32 w1, h1, w2, h2;
2753 l_float32 wratio, hratio;
2754 BOX *box3, *box4;
2755 PIX *pix3, *pix4, *pix5, *pix6, *pix7, *pix8;
2756 PIXA *pixa;
2757 
2758  PROCNAME("pixCompareGrayByHisto");
2759 
2760  if (!pscore)
2761  return ERROR_INT("&score not defined", procName, 1);
2762  *pscore = 0.0;
2763  if (!pix1 || !pix2)
2764  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
2765  if (minratio < 0.5 || minratio > 1.0)
2766  return ERROR_INT("minratio not in [0.5 ... 1.0]", procName, 1);
2767  if (maxgray < 200)
2768  return ERROR_INT("invalid maxgray; should be >= 200", procName, 1);
2769  maxgray = L_MIN(255, maxgray);
2770  if (factor < 1)
2771  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
2772  if (nx < 1 || ny < 1)
2773  return ERROR_INT("nx and ny must both be > 0", procName, 1);
2774 
2775  if (debugflag)
2776  lept_mkdir("lept/comp");
2777 
2778  /* Initial filter by size */
2779  if (box1)
2780  boxGetGeometry(box1, NULL, NULL, &w1, &h1);
2781  else
2782  pixGetDimensions(pix1, &w1, &h1, NULL);
2783  if (box2)
2784  boxGetGeometry(box2, NULL, NULL, &w2, &h2);
2785  else
2786  pixGetDimensions(pix1, &w2, &h2, NULL);
2787  wratio = (w1 < w2) ? (l_float32)w1 / (l_float32)w2 :
2788  (l_float32)w2 / (l_float32)w1;
2789  hratio = (h1 < h2) ? (l_float32)h1 / (l_float32)h2 :
2790  (l_float32)h2 / (l_float32)h1;
2791  if (wratio < minratio || hratio < minratio)
2792  return 0;
2793 
2794  /* Initial crop, if necessary */
2795  if (box1)
2796  pix3 = pixClipRectangle(pix1, box1, NULL);
2797  else
2798  pix3 = pixClone(pix1);
2799  if (box2)
2800  pix4 = pixClipRectangle(pix2, box2, NULL);
2801  else
2802  pix4 = pixClone(pix2);
2803 
2804  /* Convert to 8 bpp, align centroids and do maximal crop */
2805  pix5 = pixConvertTo8(pix3, FALSE);
2806  pix6 = pixConvertTo8(pix4, FALSE);
2807  pixCropAlignedToCentroid(pix5, pix6, factor, &box3, &box4);
2808  pix7 = pixClipRectangle(pix5, box3, NULL);
2809  pix8 = pixClipRectangle(pix6, box4, NULL);
2810  pixa = (debugflag) ? pixaCreate(0) : NULL;
2811  if (debugflag) {
2812  PIX *pix9, *pix10, *pix11, *pix12, *pix13;
2813  PIXA *pixa2;
2814  pix9 = pixConvertTo32(pix5);
2815  pix10 = pixConvertTo32(pix6);
2816  pixRenderBoxArb(pix9, box3, 2, 255, 0, 0);
2817  pixRenderBoxArb(pix10, box4, 2, 255, 0, 0);
2818  pix11 = pixScaleToSize(pix9, 400, 0);
2819  pix12 = pixScaleToSize(pix10, 400, 0);
2820  pixa2 = pixaCreate(2);
2821  pixaAddPix(pixa2, pix11, L_INSERT);
2822  pixaAddPix(pixa2, pix12, L_INSERT);
2823  pix13 = pixaDisplayTiledInRows(pixa2, 32, 1000, 1.0, 0, 50, 0);
2824  pixaAddPix(pixa, pix13, L_INSERT);
2825  pixDestroy(&pix9);
2826  pixDestroy(&pix10);
2827  pixaDestroy(&pixa2);
2828  }
2829  pixDestroy(&pix3);
2830  pixDestroy(&pix4);
2831  pixDestroy(&pix5);
2832  pixDestroy(&pix6);
2833  boxDestroy(&box3);
2834  boxDestroy(&box4);
2835 
2836  /* Tile and compare histograms */
2837  pixCompareTilesByHisto(pix7, pix8, maxgray, factor, nx, ny, pscore, pixa);
2838  pixaDestroy(&pixa);
2839  pixDestroy(&pix7);
2840  pixDestroy(&pix8);
2841  return 0;
2842 }
2843 
2844 
2865 static l_int32
2867  PIX *pix2,
2868  l_int32 maxgray,
2869  l_int32 factor,
2870  l_int32 nx,
2871  l_int32 ny,
2872  l_float32 *pscore,
2873  PIXA *pixadebug)
2874 {
2875 char buf[64];
2876 l_int32 i, j, n;
2877 l_float32 score, minscore, maxval1, maxval2, dist;
2878 L_BMF *bmf;
2879 NUMA *na1, *na2, *na3, *na4, *na5, *na6, *na7;
2880 PIX *pix3, *pix4;
2881 PIXA *pixa1, *pixa2;
2882 
2883  PROCNAME("pixCompareTilesByHisto");
2884 
2885  if (!pscore)
2886  return ERROR_INT("&score not defined", procName, 1);
2887  *pscore = 0.0;
2888  if (!pix1 || !pix2)
2889  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
2890 
2891  /* Evaluate histograms in each tile */
2892  pixa1 = pixaSplitPix(pix1, nx, ny, 0, 0);
2893  pixa2 = pixaSplitPix(pix2, nx, ny, 0, 0);
2894  n = nx * ny;
2895  na7 = (pixadebug) ? numaCreate(n) : NULL;
2896  bmf = (pixadebug) ? bmfCreate(NULL, 6) : NULL;
2897  minscore = 1.0;
2898  for (i = 0; i < n; i++) {
2899  pix3 = pixaGetPix(pixa1, i, L_CLONE);
2900  pix4 = pixaGetPix(pixa2, i, L_CLONE);
2901 
2902  /* Get histograms, set white count to 0, normalize max to 255 */
2903  na1 = pixGetGrayHistogram(pix3, factor);
2904  na2 = pixGetGrayHistogram(pix4, factor);
2905  if (maxgray < 255) {
2906  for (j = maxgray + 1; j <= 255; j++) {
2907  numaSetValue(na1, j, 0);
2908  numaSetValue(na2, j, 0);
2909  }
2910  }
2911  na3 = numaWindowedMean(na1, 5);
2912  na4 = numaWindowedMean(na2, 5);
2913  numaGetMax(na3, &maxval1, NULL);
2914  numaGetMax(na4, &maxval2, NULL);
2915  na5 = numaTransform(na3, 0, 255.0 / maxval1);
2916  na6 = numaTransform(na4, 0, 255.0 / maxval2);
2917  if (pixadebug) {
2918  gplotSimple2(na5, na6, GPLOT_PNG, "/tmp/lept/comp/plot1", "Histos");
2919  }
2920 
2921  /* To compare histograms, use the normalized earthmover distance.
2922  * Further normalize to get the EM distance as a fraction of the
2923  * maximum distance in the histogram (255). Finally, scale this
2924  * up by 10.0, and subtract from 1.0 to get a similarity score. */
2925  numaEarthMoverDistance(na5, na6, &dist);
2926  score = L_MAX(0.0, 1.0 - 8.0 * (dist / 255.));
2927  if (pixadebug) numaAddNumber(na7, score);
2928  minscore = L_MIN(minscore, score);
2929  if (pixadebug) {
2930  PIX *pix5, *pix6, *pix7, *pix8, *pix9, *pix10;
2931  PIXA *pixa3;
2932  l_int32 w, h, wscale;
2933  pixa3 = pixaCreate(3);
2934  pixGetDimensions(pix3, &w, &h, NULL);
2935  wscale = (w > h) ? 700 : 400;
2936  pix5 = pixScaleToSize(pix3, wscale, 0);
2937  pix6 = pixScaleToSize(pix4, wscale, 0);
2938  pixaAddPix(pixa3, pix5, L_INSERT);
2939  pixaAddPix(pixa3, pix6, L_INSERT);
2940  pix7 = pixRead("/tmp/lept/comp/plot1.png");
2941  pix8 = pixScaleToSize(pix7, 700, 0);
2942  snprintf(buf, sizeof(buf), "%5.3f", score);
2943  pix9 = pixAddTextlines(pix8, bmf, buf, 0x0000ff00, L_ADD_RIGHT);
2944  pixaAddPix(pixa3, pix9, L_INSERT);
2945  pix10 = pixaDisplayTiledInRows(pixa3, 32, 1000, 1.0, 0, 50, 0);
2946  pixaAddPix(pixadebug, pix10, L_INSERT);
2947  pixDestroy(&pix7);
2948  pixDestroy(&pix8);
2949  pixaDestroy(&pixa3);
2950  }
2951  numaDestroy(&na1);
2952  numaDestroy(&na2);
2953  numaDestroy(&na3);
2954  numaDestroy(&na4);
2955  numaDestroy(&na5);
2956  numaDestroy(&na6);
2957  pixDestroy(&pix3);
2958  pixDestroy(&pix4);
2959  }
2960  *pscore = minscore;
2961 
2962  if (pixadebug) {
2963  pixaConvertToPdf(pixadebug, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
2964  "/tmp/lept/comp/comparegray.pdf");
2965  numaWrite("/tmp/lept/comp/tilescores.na", na7);
2966  }
2967 
2968  bmfDestroy(&bmf);
2969  numaDestroy(&na7);
2970  pixaDestroy(&pixa1);
2971  pixaDestroy(&pixa2);
2972  return 0;
2973 }
2974 
2975 
2992 l_int32
2994  PIX *pix2,
2995  l_int32 factor,
2996  BOX **pbox1,
2997  BOX **pbox2)
2998 {
2999 l_float32 cx1, cy1, cx2, cy2;
3000 l_int32 w1, h1, w2, h2, icx1, icy1, icx2, icy2;
3001 l_int32 xm, xm1, xm2, xp, xp1, xp2, ym, ym1, ym2, yp, yp1, yp2;
3002 PIX *pix3, *pix4;
3003 
3004  PROCNAME("pixCropAlignedToCentroid");
3005 
3006  if (pbox1) *pbox1 = NULL;
3007  if (pbox2) *pbox2 = NULL;
3008  if (!pix1 || !pix2)
3009  return ERROR_INT("pix1 and pix2 not both defined", procName, 1);
3010  if (factor < 1)
3011  return ERROR_INT("subsampling factor must be >= 1", procName, 1);
3012  if (!pbox1 || !pbox2)
3013  return ERROR_INT("&box1 and &box2 not both defined", procName, 1);
3014 
3015  pix3 = pixConvertTo8(pix1, FALSE);
3016  pix4 = pixConvertTo8(pix2, FALSE);
3017  pixCentroid8(pix3, factor, &cx1, &cy1);
3018  pixCentroid8(pix4, factor, &cx2, &cy2);
3019  pixGetDimensions(pix3, &w1, &h1, NULL);
3020  pixGetDimensions(pix4, &w2, &h2, NULL);
3021  pixDestroy(&pix3);
3022  pixDestroy(&pix4);
3023 
3024  icx1 = (l_int32)(cx1 + 0.5);
3025  icy1 = (l_int32)(cy1 + 0.5);
3026  icx2 = (l_int32)(cx2 + 0.5);
3027  icy2 = (l_int32)(cy2 + 0.5);
3028  xm = L_MIN(icx1, icx2);
3029  xm1 = icx1 - xm;
3030  xm2 = icx2 - xm;
3031  xp = L_MIN(w1 - icx1, w2 - icx2); /* one pixel beyond to the right */
3032  xp1 = icx1 + xp;
3033  xp2 = icx2 + xp;
3034  ym = L_MIN(icy1, icy2);
3035  ym1 = icy1 - ym;
3036  ym2 = icy2 - ym;
3037  yp = L_MIN(h1 - icy1, h2 - icy2); /* one pixel below the bottom */
3038  yp1 = icy1 + yp;
3039  yp2 = icy2 + yp;
3040  *pbox1 = boxCreate(xm1, ym1, xp1 - xm1, yp1 - ym1);
3041  *pbox2 = boxCreate(xm2, ym2, xp2 - xm2, yp2 - ym2);
3042  return 0;
3043 }
3044 
3045 
3067 l_uint8 *
3069  l_int32 w,
3070  l_int32 h,
3071  size_t *psize)
3072 {
3073 l_uint8 *bytea;
3074 l_int32 i, j, n, nn, ival;
3075 l_float32 maxval;
3076 NUMA *na1, *na2;
3077 
3078  PROCNAME("l_compressGrayHistograms");
3079 
3080  if (!psize)
3081  return (l_uint8 *)ERROR_PTR("&size not defined", procName, NULL);
3082  *psize = 0;
3083  if (!naa)
3084  return (l_uint8 *)ERROR_PTR("naa not defined", procName, NULL);
3085  n = numaaGetCount(naa);
3086  for (i = 0; i < n; i++) {
3087  nn = numaaGetNumaCount(naa, i);
3088  if (nn != 256) {
3089  L_ERROR("%d numbers in numa[%d]\n", procName, nn, i);
3090  return NULL;
3091  }
3092  }
3093 
3094  if ((bytea = (l_uint8 *)LEPT_CALLOC(8 + 256 * n, sizeof(l_uint8))) == NULL)
3095  return (l_uint8 *)ERROR_PTR("bytea not made", procName, NULL);
3096  *psize = 8 + 256 * n;
3097  l_setDataFourBytes(bytea, 0, w);
3098  l_setDataFourBytes(bytea, 1, h);
3099  for (i = 0; i < n; i++) {
3100  na1 = numaaGetNuma(naa, i, L_COPY);
3101  numaGetMax(na1, &maxval, NULL);
3102  na2 = numaTransform(na1, 0, 255.0 / maxval);
3103  for (j = 0; j < 256; j++) {
3104  numaGetIValue(na2, j, &ival);
3105  bytea[8 + 256 * i + j] = ival;
3106  }
3107  numaDestroy(&na1);
3108  numaDestroy(&na2);
3109  }
3110 
3111  return bytea;
3112 }
3113 
3114 
3135 NUMAA *
3137  size_t size,
3138  l_int32 *pw,
3139  l_int32 *ph)
3140 {
3141 l_int32 i, j, n;
3142 NUMA *na;
3143 NUMAA *naa;
3144 
3145  PROCNAME("l_uncompressGrayHistograms");
3146 
3147  if (pw) *pw = 0;
3148  if (ph) *ph = 0;
3149  if (!pw || !ph)
3150  return (NUMAA *)ERROR_PTR("&w and &h not both defined", procName, NULL);
3151  if (!bytea)
3152  return (NUMAA *)ERROR_PTR("bytea not defined", procName, NULL);
3153  n = (size - 8) / 256;
3154  if ((size - 8) % 256 != 0)
3155  return (NUMAA *)ERROR_PTR("bytea size is invalid", procName, NULL);
3156 
3157  *pw = l_getDataFourBytes(bytea, 0);
3158  *ph = l_getDataFourBytes(bytea, 1);
3159  naa = numaaCreate(n);
3160  for (i = 0; i < n; i++) {
3161  na = numaCreate(256);
3162  for (j = 0; j < 256; j++)
3163  numaAddNumber(na, bytea[8 + 256 * i + j]);
3164  numaaAddNuma(naa, na, L_INSERT);
3165  }
3166 
3167  return naa;
3168 }
3169 
3170 
3171 /*------------------------------------------------------------------*
3172  * Translated images at the same resolution *
3173  *------------------------------------------------------------------*/
3204 l_int32
3206  PIX *pix2,
3207  l_int32 thresh,
3208  l_int32 *pdelx,
3209  l_int32 *pdely,
3210  l_float32 *pscore,
3211  l_int32 debugflag)
3212 {
3213 l_uint8 *subtab;
3214 l_int32 i, level, area1, area2, delx, dely;
3215 l_int32 etransx, etransy, maxshift, dbint;
3216 l_int32 *stab, *ctab;
3217 l_float32 cx1, cx2, cy1, cy2, score;
3218 PIX *pixb1, *pixb2, *pixt1, *pixt2, *pixt3, *pixt4;
3219 PIXA *pixa1, *pixa2, *pixadb;
3220 
3221  PROCNAME("pixCompareWithTranslation");
3222 
3223  if (pdelx) *pdelx = 0;
3224  if (pdely) *pdely = 0;
3225  if (pscore) *pscore = 0.0;
3226  if (!pdelx || !pdely)
3227  return ERROR_INT("&delx and &dely not defined", procName, 1);
3228  if (!pscore)
3229  return ERROR_INT("&score not defined", procName, 1);
3230  if (!pix1)
3231  return ERROR_INT("pix1 not defined", procName, 1);
3232  if (!pix2)
3233  return ERROR_INT("pix2 not defined", procName, 1);
3234 
3235  /* Make tables */
3236  subtab = makeSubsampleTab2x();
3237  stab = makePixelSumTab8();
3238  ctab = makePixelCentroidTab8();
3239 
3240  /* Binarize each image */
3241  pixb1 = pixConvertTo1(pix1, thresh);
3242  pixb2 = pixConvertTo1(pix2, thresh);
3243 
3244  /* Make a cascade of 2x reduced images for each, thresholding
3245  * with level 2 (neutral), down to 8x reduction */
3246  pixa1 = pixaCreate(4);
3247  pixa2 = pixaCreate(4);
3248  if (debugflag)
3249  pixadb = pixaCreate(4);
3250  pixaAddPix(pixa1, pixb1, L_INSERT);
3251  pixaAddPix(pixa2, pixb2, L_INSERT);
3252  for (i = 0; i < 3; i++) {
3253  pixt1 = pixReduceRankBinary2(pixb1, 2, subtab);
3254  pixt2 = pixReduceRankBinary2(pixb2, 2, subtab);
3255  pixaAddPix(pixa1, pixt1, L_INSERT);
3256  pixaAddPix(pixa2, pixt2, L_INSERT);
3257  pixb1 = pixt1;
3258  pixb2 = pixt2;
3259  }
3260 
3261  /* At the lowest level, use the centroids with a maxshift of 6
3262  * to search for the best alignment. Then at higher levels,
3263  * use the result from the level below as the initial approximation
3264  * for the alignment, and search with a maxshift of 2. */
3265  for (level = 3; level >= 0; level--) {
3266  pixt1 = pixaGetPix(pixa1, level, L_CLONE);
3267  pixt2 = pixaGetPix(pixa2, level, L_CLONE);
3268  pixCountPixels(pixt1, &area1, stab);
3269  pixCountPixels(pixt2, &area2, stab);
3270  if (level == 3) {
3271  pixCentroid(pixt1, ctab, stab, &cx1, &cy1);
3272  pixCentroid(pixt2, ctab, stab, &cx2, &cy2);
3273  etransx = lept_roundftoi(cx1 - cx2);
3274  etransy = lept_roundftoi(cy1 - cy2);
3275  maxshift = 6;
3276  } else {
3277  etransx = 2 * delx;
3278  etransy = 2 * dely;
3279  maxshift = 2;
3280  }
3281  dbint = (debugflag) ? level + 1 : 0;
3282  pixBestCorrelation(pixt1, pixt2, area1, area2, etransx, etransy,
3283  maxshift, stab, &delx, &dely, &score, dbint);
3284  if (debugflag) {
3285  fprintf(stderr, "Level %d: delx = %d, dely = %d, score = %7.4f\n",
3286  level, delx, dely, score);
3287  pixRasteropIP(pixt2, delx, dely, L_BRING_IN_WHITE);
3288  pixt3 = pixDisplayDiffBinary(pixt1, pixt2);
3289  pixt4 = pixExpandReplicate(pixt3, 8 / (1 << (3 - level)));
3290  pixaAddPix(pixadb, pixt4, L_INSERT);
3291  pixDestroy(&pixt3);
3292  }
3293  pixDestroy(&pixt1);
3294  pixDestroy(&pixt2);
3295  }
3296 
3297  if (debugflag) {
3298  pixaConvertToPdf(pixadb, 300, 1.0, L_FLATE_ENCODE, 0, NULL,
3299  "/tmp/lept/comp/compare.pdf");
3300  convertFilesToPdf("/tmp/lept/comp", "correl_", 30, 1.0, L_FLATE_ENCODE,
3301  0, "Correlation scores at levels 1 through 5",
3302  "/tmp/lept/comp/correl.pdf");
3303  pixaDestroy(&pixadb);
3304  }
3305 
3306  *pdelx = delx;
3307  *pdely = dely;
3308  *pscore = score;
3309  pixaDestroy(&pixa1);
3310  pixaDestroy(&pixa2);
3311  LEPT_FREE(subtab);
3312  LEPT_FREE(stab);
3313  LEPT_FREE(ctab);
3314  return 0;
3315 }
3316 
3317 
3358 l_int32
3360  PIX *pix2,
3361  l_int32 area1,
3362  l_int32 area2,
3363  l_int32 etransx,
3364  l_int32 etransy,
3365  l_int32 maxshift,
3366  l_int32 *tab8,
3367  l_int32 *pdelx,
3368  l_int32 *pdely,
3369  l_float32 *pscore,
3370  l_int32 debugflag)
3371 {
3372 l_int32 shiftx, shifty, delx, dely;
3373 l_int32 *tab;
3374 l_float32 maxscore, score;
3375 FPIX *fpix;
3376 PIX *pix3, *pix4;
3377 
3378  PROCNAME("pixBestCorrelation");
3379 
3380  if (pdelx) *pdelx = 0;
3381  if (pdely) *pdely = 0;
3382  if (pscore) *pscore = 0.0;
3383  if (!pix1 || pixGetDepth(pix1) != 1)
3384  return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1);
3385  if (!pix2 || pixGetDepth(pix2) != 1)
3386  return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1);
3387  if (!area1 || !area2)
3388  return ERROR_INT("areas must be > 0", procName, 1);
3389 
3390  if (debugflag > 0)
3391  fpix = fpixCreate(2 * maxshift + 1, 2 * maxshift + 1);
3392 
3393  if (!tab8)
3394  tab = makePixelSumTab8();
3395  else
3396  tab = tab8;
3397 
3398  /* Search over a set of {shiftx, shifty} for the max */
3399  maxscore = 0;
3400  delx = etransx;
3401  dely = etransy;
3402  for (shifty = -maxshift; shifty <= maxshift; shifty++) {
3403  for (shiftx = -maxshift; shiftx <= maxshift; shiftx++) {
3404  pixCorrelationScoreShifted(pix1, pix2, area1, area2,
3405  etransx + shiftx,
3406  etransy + shifty, tab, &score);
3407  if (debugflag > 0) {
3408  fpixSetPixel(fpix, maxshift + shiftx, maxshift + shifty,
3409  1000.0 * score);
3410 /* fprintf(stderr, "(sx, sy) = (%d, %d): score = %6.4f\n",
3411  shiftx, shifty, score); */
3412  }
3413  if (score > maxscore) {
3414  maxscore = score;
3415  delx = etransx + shiftx;
3416  dely = etransy + shifty;
3417  }
3418  }
3419  }
3420 
3421  if (debugflag > 0) {
3422  lept_mkdir("lept/comp");
3423  char buf[128];
3424  pix3 = fpixDisplayMaxDynamicRange(fpix);
3425  pix4 = pixExpandReplicate(pix3, 20);
3426  snprintf(buf, sizeof(buf), "/tmp/lept/comp/correl_%d.png",
3427  debugflag);
3428  pixWrite(buf, pix4, IFF_PNG);
3429  pixDestroy(&pix3);
3430  pixDestroy(&pix4);
3431  fpixDestroy(&fpix);
3432  }
3433 
3434  if (pdelx) *pdelx = delx;
3435  if (pdely) *pdely = dely;
3436  if (pscore) *pscore = maxscore;
3437  if (!tab8) LEPT_FREE(tab);
3438  return 0;
3439 }
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:197
l_int32 pixDecideIfPhotoImage(PIX *pix, l_int32 factor, l_int32 nx, l_int32 ny, l_float32 thresh, NUMAA **pnaa, PIXA *pixadebug)
pixDecideIfPhotoImage()
Definition: compare.c:2449
l_int32 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
l_int32 numaGetNonzeroRange(NUMA *na, l_float32 eps, l_int32 *pfirst, l_int32 *plast)
numaGetNonzeroRange()
Definition: numafunc1.c:990
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_int32 gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plottitle)
gplotAddPlot()
Definition: gplot.c:263
l_int32 gplotSimple1(NUMA *na, l_int32 outformat, const char *outroot, const char *title)
gplotSimple1()
Definition: gplot.c:569
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:322
l_int32 numaSetCount(NUMA *na, l_int32 newcount)
numaSetCount()
Definition: numabasic.c:657
l_int32 pixSetMaskedGeneral(PIX *pixd, PIX *pixm, l_uint32 val, l_int32 x, l_int32 y)
pixSetMaskedGeneral()
Definition: pix3.c:294
l_int32 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 debugflag)
pixGenPhotoHistos()
Definition: compare.c:2207
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:1880
l_int32 pixaccAdd(PIXACC *pixacc, PIX *pix)
pixaccAdd()
Definition: pixacc.c:253
l_int32 numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:472
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 numaGetSumOnInterval(NUMA *na, l_int32 first, l_int32 last, l_float32 *psum)
numaGetSumOnInterval()
Definition: numafunc1.c:586
l_int32 lept_roundftoi(l_float32 fval)
lept_roundftoi()
Definition: utils1.c:537
l_int32 * makePixelCentroidTab8(void)
makePixelCentroidTab8()
Definition: pix3.c:2343
l_int32 pixCentroid8(PIX *pixs, l_int32 factor, l_float32 *pcx, l_float32 *pcy)
pixCentroid8()
Definition: compare.c:2371
PIX * pixConvertLossless(PIX *pixs, l_int32 d)
pixConvertLossless()
Definition: pixconv.c:3741
l_int32 grayInterHistogramStats(NUMAA *naa, l_int32 wc, NUMA **pnam, NUMA **pnams, NUMA **pnav, NUMA **pnarv)
grayInterHistogramStats()
Definition: numafunc2.c:2243
PIXA * pixaReadFiles(const char *dirname, const char *substr)
pixaReadFiles()
Definition: readfile.c:123
l_int32 pixCropAlignedToCentroid(PIX *pix1, PIX *pix2, l_int32 factor, BOX **pbox1, BOX **pbox2)
pixCropAlignedToCentroid()
Definition: compare.c:2993
l_uint8 * l_compressGrayHistograms(NUMAA *naa, l_int32 w, l_int32 h, size_t *psize)
l_compressGrayHistograms()
Definition: compare.c:3068
l_int32 pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:180
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:161
l_int32 pixZero(PIX *pix, l_int32 *pempty)
pixZero()
Definition: pix3.c:1702
GPLOT * gplotCreate(const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel)
gplotCreate()
Definition: gplot.c:138
Definition: pix.h:704
l_int32 numaGetMax(NUMA *na, l_float32 *pmaxval, l_int32 *pimaxloc)
numaGetMax()
Definition: numafunc1.c:473
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3041
l_int32 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:2740
NUMA * numaMakeConstant(l_float32 val, l_int32 size)
numaMakeConstant()
Definition: numafunc1.c:768
l_int32 numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval)
numaGetFValue()
Definition: numabasic.c:691
l_int32 numaEarthMoverDistance(NUMA *na1, NUMA *na2, l_float32 *pdist)
numaEarthMoverDistance()
Definition: numafunc2.c:2152
PIX * pixGetAverageTiled(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 type)
pixGetAverageTiled()
Definition: pix4.c:1653
l_int32 pixaccMultConst(PIXACC *pixacc, l_float32 factor)
pixaccMultConst()
Definition: pixacc.c:297
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
l_int32 pixAlphaIsOpaque(PIX *pix, l_int32 *popaque)
pixAlphaIsOpaque()
Definition: pix2.c:3220
PIXA * pixaSplitPix(PIX *pixs, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor)
pixaSplitPix()
Definition: pixabasic.c:336
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1395
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:186
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1602
l_int32 pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:709
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:1089
l_int32 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:237
PIX * pixPadToCenterCentroid(PIX *pixs, l_int32 factor)
pixPadToCenterCentroid()
Definition: compare.c:2317
NUMAA * numaaCreate(l_int32 n)
numaaCreate()
Definition: numabasic.c:1307
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1013
l_int32 gplotMakeOutput(GPLOT *gplot)
gplotMakeOutput()
Definition: gplot.c:379
PIX * pixDisplayDiffBinary(PIX *pix1, PIX *pix2)
pixDisplayDiffBinary()
Definition: compare.c:656
l_int32 pixCompareWithTranslation(PIX *pix1, PIX *pix2, l_int32 thresh, l_int32 *pdelx, l_int32 *pdely, l_float32 *pscore, l_int32 debugflag)
pixCompareWithTranslation()
Definition: compare.c:3205
void numaaDestroy(NUMAA **pnaa)
numaaDestroy()
Definition: numabasic.c:1410
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2392
Definition: bmf.h:45
l_int32 pixRenderBoxArb(PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval)
pixRenderBoxArb()
Definition: graphics.c:1641
l_int32 numaaAddNuma(NUMAA *naa, NUMA *na, l_int32 copyflag)
numaaAddNuma()
Definition: numabasic.c:1448
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:110
l_int32 pixcmapGetRGBA(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval, l_int32 *paval)
pixcmapGetRGBA()
Definition: colormap.c:777
PIX * fpixDisplayMaxDynamicRange(FPIX *fpixs)
fpixDisplayMaxDynamicRange()
Definition: fpix2.c:422
l_int32 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
void pixaccDestroy(PIXACC **ppixacc)
pixaccDestroy()
Definition: pixacc.c:160
NUMA * numaaGetNuma(NUMAA *naa, l_int32 index, l_int32 accessflag)
numaaGetNuma()
Definition: numabasic.c:1625
PIX * pixSubtractGray(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtractGray()
Definition: pixarith.c:353
Definition: array.h:59
l_int32 pixEqualWithAlpha(PIX *pix1, PIX *pix2, l_int32 use_alpha, l_int32 *psame)
pixEqualWithAlpha()
Definition: compare.c:176
static const l_int32 L_INSERT
Definition: pix.h:710
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:1374
l_int32 pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:299
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1574
l_int32 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:3359
l_int32 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:2572
PIX * pixAbsDifference(PIX *pixs1, PIX *pixs2)
pixAbsDifference()
Definition: pixarith.c:872
l_int32 pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:493
l_int32 pixEqualWithCmap(PIX *pix1, PIX *pix2, l_int32 *psame)
pixEqualWithCmap()
Definition: compare.c:378
l_int32 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 * 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:821
l_int32 pixCompareTiled(PIX *pix1, PIX *pix2, l_int32 sx, l_int32 sy, l_int32 type, PIX **ppixdiff)
pixCompareTiled()
Definition: compare.c:1124
l_int32 * makePixelSumTab8(void)
makePixelSumTab8()
Definition: pix3.c:2298
Definition: pix.h:546
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:192
l_int32 pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1556
l_int32 cmapEqual(PIXCMAP *cmap1, PIXCMAP *cmap2, l_int32 ncomps, l_int32 *psame)
cmapEqual()
Definition: compare.c:474
l_int32 boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:309
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:2866
NUMA * pixCompareRankDifference(PIX *pix1, PIX *pix2, l_int32 factor)
pixCompareRankDifference()
Definition: compare.c:1218
l_int32 pixSizesEqual(PIX *pix1, PIX *pix2)
pixSizesEqual()
Definition: pix1.c:768
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:182
Definition: gplot.h:75
PIX * pixCreateRGBImage(PIX *pixr, PIX *pixg, PIX *pixb)
pixCreateRGBImage()
Definition: pix2.c:2336
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:517
PIX * pixSubtract(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixSubtract()
Definition: pix3.c:1639
l_int32 pixCompareBinary(PIX *pix1, PIX *pix2, l_int32 comptype, l_float32 *pfract, PIX **ppixdiff)
pixCompareBinary()
Definition: compare.c:712
PIX * pixScaleToSize(PIX *pixs, l_int32 wd, l_int32 hd)
pixScaleToSize()
Definition: scale1.c:316
Definition: array.h:71
l_int32 pixCentroid(PIX *pix, l_int32 *centtab, l_int32 *sumtab, l_float32 *pxave, l_float32 *pyave)
pixCentroid()
Definition: morphapp.c:1527
l_int32 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:749
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:545
l_int32 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)
pixaComparePhotoRegionsByHisto()
Definition: compare.c:1892
PIX * pixScaleByIntSampling(PIX *pixs, l_int32 factor)
pixScaleByIntSampling()
Definition: scale1.c:1405
l_int32 pixUsesCmapColor(PIX *pixs, l_int32 *pcolor)
pixUsesCmapColor()
Definition: compare.c:532
l_int32 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
PIX * pixaccFinal(PIXACC *pixacc, l_int32 outdepth)
pixaccFinal()
Definition: pixacc.c:192
Definition: pix.h:454
l_int32 pixPaintThroughMask(PIX *pixd, PIX *pixm, l_int32 x, l_int32 y, l_uint32 val)
pixPaintThroughMask()
Definition: pix3.c:618
l_int32 pixDecideIfText(PIX *pixs, BOX *box, l_int32 *pistext, PIXA *pixadb)
pixDecideIfText()
Definition: pageseg.c:1372
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:359
l_int32 pixSetResolution(PIX *pix, l_int32 xres, l_int32 yres)
pixSetResolution()
Definition: pix1.c:1323
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:394
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:189
NUMA * pixGetDifferenceHistogram(PIX *pix1, PIX *pix2, l_int32 factor)
pixGetDifferenceHistogram()
Definition: compare.c:1484
char * pixGetText(PIX *pix)
pixGetText()
Definition: pix1.c:1442
l_int32 pixaJoin(PIXA *pixad, PIXA *pixas, l_int32 istart, l_int32 iend)
pixaJoin()
Definition: pixabasic.c:1590
NUMA * numaWindowedMean(NUMA *nas, l_int32 wc)
numaWindowedMean()
Definition: numafunc2.c:562
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
l_int32 pixEqual(PIX *pix1, PIX *pix2, l_int32 *psame)
pixEqual()
Definition: compare.c:150
FPIX * fpixCreate(l_int32 width, l_int32 height)
fpixCreate()
Definition: fpix1.c:149
l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag)
numaGetFArray()
Definition: numabasic.c:864
l_int32 numaWrite(const char *filename, NUMA *na)
numaWrite()
Definition: numabasic.c:1182
l_int32 numaaGetCount(NUMAA *naa)
numaaGetCount()
Definition: numabasic.c:1516
PIXACC * pixaccCreate(l_int32 w, l_int32 h, l_int32 negflag)
pixaccCreate()
Definition: pixacc.c:90
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:660
l_int32 numaaGetNumaCount(NUMAA *naa, l_int32 index)
numaaGetNumaCount()
Definition: numabasic.c:1534
Definition: pix.h:705
l_int32 pixcmapGetCount(PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:593
l_int32 pixGetPSNR(PIX *pix1, PIX *pix2, l_int32 factor, l_float32 *ppsnr)
pixGetPSNR()
Definition: compare.c:1778
Definition: pix.h:134
void l_setDataFourBytes(void *line, l_int32 n, l_int32 val)
l_setDataFourBytes()
Definition: arrayaccess.c:359
l_int32 numaSetValue(NUMA *na, l_int32 index, l_float32 val)
numaSetValue()
Definition: numabasic.c:758
l_int32 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:2100
Definition: pix.h:706
l_int32 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
NUMAA * l_uncompressGrayHistograms(l_uint8 *bytea, size_t size, l_int32 *pw, l_int32 *ph)
l_uncompressGrayHistograms()
Definition: compare.c:3136
#define PIX_SRC
Definition: pix.h:327
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:630
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:277
Definition: pix.h:201
l_int32 gplotSimple2(NUMA *na1, NUMA *na2, l_int32 outformat, const char *outroot, const char *title)
gplotSimple2()
Definition: gplot.c:598
NUMA * numaNormalizeHistogram(NUMA *nas, l_float32 tsum)
numaNormalizeHistogram()
Definition: numafunc2.c:1153
PIX * pixAddTextlines(PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location)
pixAddTextlines()
Definition: textops.c:270
l_int32 numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:726
PIX * pixMinOrMax(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 type)
pixMinOrMax()
Definition: pixarith.c:1054
l_int32 pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:729
l_int32 fpixSetPixel(FPIX *fpix, l_int32 x, l_int32 y, l_float32 val)
fpixSetPixel()
Definition: fpix1.c:674
l_int32 pixGetDimensions(PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1052
l_int32 lept_rmdir(const char *subdir)
lept_rmdir()
Definition: utils2.c:1951
l_int32 pixCorrelationBinary(PIX *pix1, PIX *pix2, l_float32 *pval)
pixCorrelationBinary()
Definition: compare.c:598
void fpixDestroy(FPIX **pfpix)
fpixDestroy()
Definition: fpix1.c:370
l_int32 pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1824
l_int32 pixRasteropIP(PIX *pixd, l_int32 hshift, l_int32 vshift, l_int32 incolor)
pixRasteropIP()
Definition: rop.c:461
Definition: pix.h:480
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:398
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2725
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:164
l_uint8 * makeSubsampleTab2x(void)
Permutation table for 2x rank binary reduction.
Definition: binreduce.c:384
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:620
l_int32 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_int32 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_int32 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
Definition: pix.h:582
PIX * pixColorMorph(PIX *pixs, l_int32 type, l_int32 hsize, l_int32 vsize)
pixColorMorph()
Definition: colormorph.c:66
l_int32 numaWriteStream(FILE *fp, NUMA *na)
numaWriteStream()
Definition: numabasic.c:1213
l_int32 pixcmapHasColor(PIXCMAP *cmap, l_int32 *pcolor)
pixcmapHasColor()
Definition: colormap.c:960
L_BMF * bmfCreate(const char *dir, l_int32 fontsize)
bmfCreate()
Definition: bmf.c:114