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