| /*====================================================================* |
| - Copyright (C) 2001 Leptonica. All rights reserved. |
| - This software is distributed in the hope that it will be |
| - useful, but with NO WARRANTY OF ANY KIND. |
| - No author or distributor accepts responsibility to anyone for the |
| - consequences of using this software, or for whether it serves any |
| - particular purpose or works at all, unless he or she says so in |
| - writing. Everyone is granted permission to copy, modify and |
| - redistribute this source code, for commercial or non-commercial |
| - purposes, with the following restrictions: (1) the origin of this |
| - source code must not be misrepresented; (2) modified versions must |
| - be plainly marked as such; and (3) this notice may not be removed |
| - or altered from any source or modified source distribution. |
| *====================================================================*/ |
| |
| /* |
| * compare.c |
| * |
| * Test for pix equality |
| * l_int32 pixEqual() |
| * l_int32 pixEqualWithCmap() |
| * l_int32 pixUsesCmapColor() |
| * |
| * Binary correlation |
| * l_int32 pixCorrelationBinary() |
| * |
| * Difference of two images of same size |
| * l_int32 pixCompareBinary() |
| * l_int32 pixCompareGrayOrRGB() |
| * l_int32 pixCompareGray() |
| * l_int32 pixCompareRGB() |
| * l_int32 pixCompareTiled() |
| * |
| * Difference of two images as rank array |
| * NUMA *pixCompareRankDifference |
| */ |
| |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "allheaders.h" |
| |
| /* Small enough to consider equal to 0.0, for plot output */ |
| static const l_float32 TINY = 0.00001; |
| |
| |
| /*------------------------------------------------------------------* |
| * Test for pix equality * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixEqual() |
| * |
| * Input: pix1 |
| * pix2 |
| * &same (<return> 1 if same; 0 if different) |
| * Return: 0 if OK; 1 on error |
| * |
| * Notes: |
| * (1) Equality is defined as having the same pixel values for |
| * each respective image pixel. |
| * (2) This works on two pix of any depth. If one or both pix |
| * have a colormap, the depths can be different and the |
| * two pix can still be equal. |
| * (3) If both pix have colormaps and the depths are equal, |
| * use the pixEqualWithCmap() function, which does a fast |
| * comparison if the colormaps are identical and a relatively |
| * slow comparison otherwise. |
| * (4) In all other cases, any existing colormaps must first be |
| * removed before doing pixel comparison. After the colormaps |
| * are removed, the resulting two images must have the same depth. |
| * The "lowest common denominator" is RGB, but this is only |
| * chosen when necessary, or when both have colormaps but |
| * different depths. |
| * (5) For 32 bpp, ignore the bits in the 4th byte (the 'A' byte |
| * of the RGBA pixel) |
| * (6) For images without colormaps that are not 32 bpp, all bits |
| * in the image part of the data array must be identical. |
| */ |
| l_int32 |
| pixEqual(PIX *pix1, |
| PIX *pix2, |
| l_int32 *psame) |
| { |
| l_int32 w1, h1, d1, w2, h2, d2, wpl1, wpl2, i, j, color; |
| l_int32 fullwords, linebits, endbits; |
| l_uint32 endmask; |
| l_uint32 *data1, *data2, *line1, *line2; |
| PIX *pixs1, *pixs2, *pixt1, *pixt2; |
| PIXCMAP *cmap1, *cmap2; |
| |
| PROCNAME("pixEqual"); |
| |
| if (!psame) |
| return ERROR_INT("psamel not defined", procName, 1); |
| *psame = 0; /* pix are different unless we exit after checking all data */ |
| |
| if (!pix1) |
| return ERROR_INT("pix1 not defined", procName, 1); |
| if (!pix2) |
| return ERROR_INT("pix2 not defined", procName, 1); |
| |
| pixGetDimensions(pix1, &w1, &h1, &d1); |
| pixGetDimensions(pix2, &w2, &h2, &d2); |
| if (w1 != w2 || h1 != h2) { |
| L_INFO("pix sizes differ", procName); |
| return 0; |
| } |
| |
| cmap1 = pixGetColormap(pix1); |
| cmap2 = pixGetColormap(pix2); |
| if (!cmap1 && !cmap2 && (d1 != d2) && (d1 == 32 || d2 == 32)) { |
| L_INFO("no colormaps, pix depths unequal, and one of them is RGB", |
| procName); |
| return 0; |
| } |
| |
| if (cmap1 && cmap2 && (d1 == d2)) /* use special function */ |
| return pixEqualWithCmap(pix1, pix2, psame); |
| |
| /* Must remove colormaps if they exist, and in the process |
| * end up with the resulting images having the same depth. */ |
| if (cmap1 && !cmap2) { |
| pixUsesCmapColor(pix1, &color); |
| if (color && d2 <= 8) /* can't be equal */ |
| return 0; |
| if (d2 < 8) |
| pixs2 = pixConvertTo8(pix2, FALSE); |
| else |
| pixs2 = pixClone(pix2); |
| if (d2 <= 8) |
| pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_GRAYSCALE); |
| else |
| pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR); |
| } |
| else if (!cmap1 && cmap2) { |
| pixUsesCmapColor(pix2, &color); |
| if (color && d1 <= 8) /* can't be equal */ |
| return 0; |
| if (d1 < 8) |
| pixs1 = pixConvertTo8(pix1, FALSE); |
| else |
| pixs1 = pixClone(pix1); |
| if (d1 <= 8) |
| pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_GRAYSCALE); |
| else |
| pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_FULL_COLOR); |
| } |
| else if (cmap1 && cmap2) { /* depths not equal; use rgb */ |
| pixs1 = pixRemoveColormap(pix1, REMOVE_CMAP_TO_FULL_COLOR); |
| pixs2 = pixRemoveColormap(pix2, REMOVE_CMAP_TO_FULL_COLOR); |
| } |
| else { /* no colormaps */ |
| pixs1 = pixClone(pix1); |
| pixs2 = pixClone(pix2); |
| } |
| |
| /* OK, we have no colormaps, but the depths may still be different */ |
| d1 = pixGetDepth(pixs1); |
| d2 = pixGetDepth(pixs2); |
| if (d1 != d2) { |
| if (d1 == 16 || d2 == 16) { |
| L_INFO("one pix is 16 bpp", procName); |
| pixDestroy(&pixs1); |
| pixDestroy(&pixs2); |
| return 0; |
| } |
| pixt1 = pixConvertLossless(pixs1, 8); |
| pixt2 = pixConvertLossless(pixs2, 8); |
| if (!pixt1 || !pixt2) { |
| L_INFO("failure to convert to 8 bpp", procName); |
| pixDestroy(&pixs1); |
| pixDestroy(&pixs2); |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| return 0; |
| } |
| } |
| else { |
| pixt1 = pixClone(pixs1); |
| pixt2 = pixClone(pixs2); |
| } |
| pixDestroy(&pixs1); |
| pixDestroy(&pixs2); |
| |
| /* No colormaps, equal depths; do pixel comparisons */ |
| d1 = pixGetDepth(pixt1); |
| d2 = pixGetDepth(pixt2); |
| wpl1 = pixGetWpl(pixt1); |
| wpl2 = pixGetWpl(pixt2); |
| data1 = pixGetData(pixt1); |
| data2 = pixGetData(pixt2); |
| |
| if (d1 == 32) { /* assume RGBA, with A = don't-care */ |
| for (i = 0; i < h1; i++) { |
| line1 = data1 + wpl1 * i; |
| line2 = data2 + wpl2 * i; |
| for (j = 0; j < wpl1; j++) { |
| if ((*line1 ^ *line2) & 0xffffff00) { |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| return 0; |
| } |
| line1++; |
| line2++; |
| } |
| } |
| } |
| else { /* all bits count */ |
| linebits = d1 * w1; |
| fullwords = linebits / 32; |
| endbits = linebits & 31; |
| endmask = 0xffffffff << (32 - endbits); |
| for (i = 0; i < h1; i++) { |
| line1 = data1 + wpl1 * i; |
| line2 = data2 + wpl2 * i; |
| for (j = 0; j < fullwords; j++) { |
| if (*line1 ^ *line2) { |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| return 0; |
| } |
| line1++; |
| line2++; |
| } |
| if (endbits) { |
| if ((*line1 ^ *line2) & endmask) { |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| return 0; |
| } |
| } |
| } |
| } |
| |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| *psame = 1; |
| return 0; |
| } |
| |
| |
| /*! |
| * pixEqualWithCmap() |
| * |
| * Input: pix1 |
| * pix2 |
| * &same |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This returns same = TRUE if the images have identical content. |
| * (2) Both pix must have a colormap, and be of equal size and depth. |
| * If these conditions are not satisfied, it is not an error; |
| * the returned result is same = FALSE. |
| * (3) We then check whether the colormaps are the same; if so, |
| * the comparison proceeds 32 bits at a time. |
| * (4) If the colormaps are different, the comparison is done by |
| * slow brute force. |
| */ |
| l_int32 |
| pixEqualWithCmap(PIX *pix1, |
| PIX *pix2, |
| l_int32 *psame) |
| { |
| l_int32 d, w, h, wpl1, wpl2, i, j, linebits, fullwords, endbits; |
| l_int32 nc1, nc2, samecmaps; |
| l_int32 rval1, rval2, gval1, gval2, bval1, bval2; |
| l_uint32 endmask, val1, val2; |
| l_uint32 *data1, *data2, *line1, *line2; |
| PIXCMAP *cmap1, *cmap2; |
| |
| PROCNAME("pixEqualWithCmap"); |
| |
| if (!psame) |
| return ERROR_INT("&same not defined", procName, 1); |
| *psame = 0; |
| if (!pix1) |
| return ERROR_INT("pix1 not defined", procName, 1); |
| if (!pix2) |
| return ERROR_INT("pix2 not defined", procName, 1); |
| |
| if (pixSizesEqual(pix1, pix2) == 0) |
| return 0; |
| |
| cmap1 = pixGetColormap(pix1); |
| cmap2 = pixGetColormap(pix2); |
| if (!cmap1 || !cmap2) { |
| L_INFO("both images don't have colormap", procName); |
| return 0; |
| } |
| d = pixGetDepth(pix1); |
| if (d != 1 && d != 2 && d != 4 && d != 8) { |
| L_INFO("pix depth not in {1, 2, 4, 8}", procName); |
| return 0; |
| } |
| |
| nc1 = pixcmapGetCount(cmap1); |
| nc2 = pixcmapGetCount(cmap2); |
| samecmaps = TRUE; |
| if (nc1 != nc2) { |
| L_INFO("colormap sizes are different", procName); |
| samecmaps = FALSE; |
| } |
| |
| /* Check if colormaps are identical */ |
| if (samecmaps == TRUE) { |
| for (i = 0; i < nc1; i++) { |
| pixcmapGetColor(cmap1, i, &rval1, &gval1, &bval1); |
| pixcmapGetColor(cmap2, i, &rval2, &gval2, &bval2); |
| if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2) { |
| samecmaps = FALSE; |
| break; |
| } |
| } |
| } |
| |
| h = pixGetHeight(pix1); |
| w = pixGetWidth(pix1); |
| if (samecmaps == TRUE) { /* colormaps are identical; compare by words */ |
| linebits = d * w; |
| wpl1 = pixGetWpl(pix1); |
| wpl2 = pixGetWpl(pix2); |
| data1 = pixGetData(pix1); |
| data2 = pixGetData(pix2); |
| fullwords = linebits / 32; |
| endbits = linebits & 31; |
| endmask = 0xffffffff << (32 - endbits); |
| for (i = 0; i < h; i++) { |
| line1 = data1 + wpl1 * i; |
| line2 = data2 + wpl2 * i; |
| for (j = 0; j < fullwords; j++) { |
| if (*line1 ^ *line2) |
| return 0; |
| line1++; |
| line2++; |
| } |
| if (endbits) { |
| if ((*line1 ^ *line2) & endmask) |
| return 0; |
| } |
| } |
| *psame = 1; |
| return 0; |
| } |
| |
| /* Colormaps aren't identical; compare pixel by pixel */ |
| for (i = 0; i < h; i++) { |
| for (j = 0; j < w; j++) { |
| pixGetPixel(pix1, j, i, &val1); |
| pixGetPixel(pix2, j, i, &val2); |
| pixcmapGetColor(cmap1, val1, &rval1, &gval1, &bval1); |
| pixcmapGetColor(cmap2, val2, &rval2, &gval2, &bval2); |
| if (rval1 != rval2 || gval1 != gval2 || bval1 != bval2) |
| return 0; |
| } |
| } |
| |
| *psame = 1; |
| return 0; |
| } |
| |
| |
| /*! |
| * pixUsesCmapColor() |
| * |
| * Input: pixs |
| * &color (<return>) |
| * Return: 0 if OK, 1 on error |
| * |
| * Notes: |
| * (1) This returns color = TRUE if three things are obtained: |
| * (a) the pix has a colormap |
| * (b) the colormap has at least one color entry |
| * (c) a color entry is actually used |
| * (2) It is used in pixEqual() for comparing two images, in a |
| * situation where it is required to know if the colormap |
| * has color entries that are actually used in the image. |
| */ |
| l_int32 |
| pixUsesCmapColor(PIX *pixs, |
| l_int32 *pcolor) |
| { |
| l_int32 n, i, rval, gval, bval, numpix; |
| NUMA *na; |
| PIXCMAP *cmap; |
| |
| PROCNAME("pixUsesCmapColor"); |
| |
| if (!pcolor) |
| return ERROR_INT("&color not defined", procName, 1); |
| *pcolor = 0; |
| if (!pixs) |
| return ERROR_INT("pixs not defined", procName, 1); |
| |
| if ((cmap = pixGetColormap(pixs)) == NULL) |
| return 0; |
| |
| pixcmapHasColor(cmap, pcolor); |
| if (*pcolor == 0) /* no color */ |
| return 0; |
| |
| /* The cmap has color entries. Are they used? */ |
| na = pixGetGrayHistogram(pixs, 1); |
| n = pixcmapGetCount(cmap); |
| for (i = 0; i < n; i++) { |
| pixcmapGetColor(cmap, i, &rval, &gval, &bval); |
| numaGetIValue(na, i, &numpix); |
| if ((rval != gval || rval != bval) && numpix) { /* color found! */ |
| *pcolor = 1; |
| break; |
| } |
| } |
| numaDestroy(&na); |
| |
| return 0; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Binary correlation * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixCorrelationBinary() |
| * |
| * Input: pix1 (1 bpp) |
| * pix2 (1 bpp) |
| * &val (<return> correlation) |
| * Return: 0 if OK; 1 on error |
| * |
| * Notes: |
| * (1) The correlation is a number between 0.0 and 1.0, |
| * based on foreground similarity: |
| * (|1 AND 2|)**2 |
| * correlation = -------------- |
| * |1| * |2| |
| * where |x| is the count of foreground pixels in image x. |
| * If the images are identical, this is 1.0. |
| * If they have no fg pixels in common, this is 0.0. |
| * If one or both images have no fg pixels, the correlation is 0.0. |
| * (2) Typically the two images are of equal size, but this |
| * is not enforced. Instead, the UL corners are be aligned. |
| */ |
| l_int32 |
| pixCorrelationBinary(PIX *pix1, |
| PIX *pix2, |
| l_float32 *pval) |
| { |
| l_int32 count1, count2, countn; |
| l_int32 *tab8; |
| PIX *pixn; |
| |
| PROCNAME("pixCorrelationBinary"); |
| |
| if (!pval) |
| return ERROR_INT("&pval not defined", procName, 1); |
| *pval = 0.0; |
| if (!pix1) |
| return ERROR_INT("pix1 not defined", procName, 1); |
| if (!pix2) |
| return ERROR_INT("pix2 not defined", procName, 1); |
| |
| tab8 = makePixelSumTab8(); |
| pixCountPixels(pix1, &count1, tab8); |
| pixCountPixels(pix2, &count2, tab8); |
| pixn = pixAnd(NULL, pix1, pix2); |
| pixCountPixels(pixn, &countn, tab8); |
| *pval = (l_float32)(countn * countn) / (l_float32)(count1 * count2); |
| FREE(tab8); |
| return 0; |
| } |
| |
| |
| /*------------------------------------------------------------------* |
| * Difference of two images * |
| *------------------------------------------------------------------*/ |
| /*! |
| * pixCompareBinary() |
| * |
| * Input: pix1 (1 bpp) |
| * pix2 (1 bpp) |
| * comptype (L_COMPARE_XOR, L_COMPARE_SUBTRACT) |
| * &fract (<return> fraction of pixels that are different) |
| * &pixdiff (<optional return> pix of difference) |
| * Return: 0 if OK; 1 on error |
| * |
| * Notes: |
| * (1) The two images are aligned at the UL corner, and do not |
| * need to be the same size. |
| * (2) If using L_COMPARE_SUBTRACT, pix2 is subtracted from pix1. |
| * (3) The total number of pixels is determined by pix1. |
| */ |
| l_int32 |
| pixCompareBinary(PIX *pix1, |
| PIX *pix2, |
| l_int32 comptype, |
| l_float32 *pfract, |
| PIX **ppixdiff) |
| { |
| l_int32 w, h, count; |
| PIX *pixt; |
| |
| PROCNAME("pixCompareBinary"); |
| |
| if (ppixdiff) *ppixdiff = NULL; |
| if (!pfract) |
| return ERROR_INT("&pfract not defined", procName, 1); |
| *pfract = 0.0; |
| if (!pix1 || pixGetDepth(pix1) != 1) |
| return ERROR_INT("pix1 not defined or not 1 bpp", procName, 1); |
| if (!pix2 || pixGetDepth(pix2) != 1) |
| return ERROR_INT("pix2 not defined or not 1 bpp", procName, 1); |
| if (comptype != L_COMPARE_XOR && comptype != L_COMPARE_SUBTRACT) |
| return ERROR_INT("invalid comptype", procName, 1); |
| |
| if (comptype == L_COMPARE_XOR) |
| pixt = pixXor(NULL, pix1, pix2); |
| else /* comptype == L_COMPARE_SUBTRACT) */ |
| pixt = pixSubtract(NULL, pix1, pix2); |
| pixCountPixels(pixt, &count, NULL); |
| pixGetDimensions(pix1, &w, &h, NULL); |
| *pfract = (l_float32)(count) / (l_float32)(w * h); |
| |
| if (ppixdiff) |
| *ppixdiff = pixt; |
| else |
| pixDestroy(&pixt); |
| return 0; |
| } |
| |
| |
| /*! |
| * pixCompareGrayOrRGB() |
| * |
| * Input: pix1 (8 or 16 bpp gray, 32 bpp rgb, or colormapped) |
| * pix2 (8 or 16 bpp gray, 32 bpp rgb, or colormapped) |
| * comptype (L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF) |
| * plottype (gplot plot output type, or 0 for no plot) |
| * &same (<optional return> 1 if pixel values are identical) |
| * &diff (<optional return> average difference) |
| * &rmsdiff (<optional return> rms of difference) |
| * &pixdiff (<optional return> pix of difference) |
| * Return: 0 if OK; 1 on error |
| * |
| * Notes: |
| * (1) The two images are aligned at the UL corner, and do not |
| * need to be the same size. If they are not the same size, |
| * the comparison will be made over overlapping pixels. |
| * (2) If there is a colormap, it is removed and the result |
| * is either gray or RGB depending on the colormap. |
| * (3) If RGB, each component is compared separately. |
| * (4) If type is L_COMPARE_ABS_DIFF, pix2 is subtracted from pix1 |
| * and the absolute value is taken. |
| * (5) If type is L_COMPARE_SUBTRACT, pix2 is subtracted from pix1 |
| * and the result is clipped to 0. |
| * (6) The plot output types are specified in gplot.h. |
| * Use 0 if no difference plot is to be made. |
| * (7) If the images are pixelwise identical, no difference |
| * plot is made, even if requested. The result (TRUE or FALSE) |
| * is optionally returned in the parameter 'same'. |
| * (8) The average difference (either subtracting or absolute value) |
| * is optionally returned in the parameter 'diff'. |
| * (9) The RMS difference is optionally returned in the |
| * parameter 'rmsdiff'. For RGB, we return the average of |
| * the RMS differences for each of the components. |
| */ |
| l_int32 |
| pixCompareGrayOrRGB(PIX *pix1, |
| PIX *pix2, |
| l_int32 comptype, |
| l_int32 plottype, |
| l_int32 *psame, |
| l_float32 *pdiff, |
| l_float32 *prmsdiff, |
| PIX **ppixdiff) |
| { |
| l_int32 retval, d; |
| PIX *pixt1, *pixt2; |
| |
| PROCNAME("pixCompareGrayOrRGB"); |
| |
| if (ppixdiff) *ppixdiff = NULL; |
| if (!pix1) |
| return ERROR_INT("pix1 not defined", procName, 1); |
| if (!pix2) |
| return ERROR_INT("pix2 not defined", procName, 1); |
| if (pixGetDepth(pix1) < 8 && !pixGetColormap(pix1)) |
| return ERROR_INT("pix1 depth < 8 bpp and not cmapped", procName, 1); |
| if (pixGetDepth(pix2) < 8 && !pixGetColormap(pix2)) |
| return ERROR_INT("pix2 depth < 8 bpp and not cmapped", procName, 1); |
| if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) |
| return ERROR_INT("invalid comptype", procName, 1); |
| if (plottype > NUM_GPLOT_OUTPUTS) |
| return ERROR_INT("invalid plottype", procName, 1); |
| |
| pixt1 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); |
| pixt2 = pixRemoveColormap(pix2, REMOVE_CMAP_BASED_ON_SRC); |
| d = pixGetDepth(pixt1); |
| if (d != pixGetDepth(pixt2)) { |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| return ERROR_INT("intrinsic depths are not equal", procName, 1); |
| } |
| |
| if (d == 8 || d == 16) |
| retval = pixCompareGray(pixt1, pixt2, comptype, plottype, psame, |
| pdiff, prmsdiff, ppixdiff); |
| else /* d == 32 */ |
| retval = pixCompareRGB(pixt1, pixt2, comptype, plottype, psame, |
| pdiff, prmsdiff, ppixdiff); |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| return retval; |
| } |
| |
| |
| /*! |
| * pixCompareGray() |
| * |
| * Input: pix1 (8 or 16 bpp, not cmapped) |
| * pix2 (8 or 16 bpp, not cmapped) |
| * comptype (L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF) |
| * plottype (gplot plot output type, or 0 for no plot) |
| * &same (<optional return> 1 if pixel values are identical) |
| * &diff (<optional return> average difference) |
| * &rmsdiff (<optional return> rms of difference) |
| * &pixdiff (<optional return> pix of difference) |
| * Return: 0 if OK; 1 on error |
| * |
| * Notes: |
| * (1) See pixCompareGrayOrRGB() for details. |
| * (2) Use pixCompareGrayOrRGB() if the input pix are colormapped. |
| */ |
| l_int32 |
| pixCompareGray(PIX *pix1, |
| PIX *pix2, |
| l_int32 comptype, |
| l_int32 plottype, |
| l_int32 *psame, |
| l_float32 *pdiff, |
| l_float32 *prmsdiff, |
| PIX **ppixdiff) |
| { |
| l_int32 d1, d2, first, last; |
| GPLOT *gplot; |
| NUMA *na, *nac; |
| PIX *pixt; |
| |
| PROCNAME("pixCompareGray"); |
| |
| if (psame) *psame = 0; |
| if (pdiff) *pdiff = 0.0; |
| if (prmsdiff) *prmsdiff = 0.0; |
| if (ppixdiff) *ppixdiff = NULL; |
| if (!pix1) |
| return ERROR_INT("pix1 not defined", procName, 1); |
| if (!pix2) |
| return ERROR_INT("pix2 not defined", procName, 1); |
| d1 = pixGetDepth(pix1); |
| d2 = pixGetDepth(pix2); |
| if ((d1 != d2) || (d1 != 8 && d1 != 16)) |
| return ERROR_INT("depths unequal or not 8 or 16 bpp", procName, 1); |
| if (pixGetColormap(pix1) || pixGetColormap(pix2)) |
| return ERROR_INT("pix1 and/or pix2 are colormapped", procName, 1); |
| if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) |
| return ERROR_INT("invalid comptype", procName, 1); |
| if (plottype > NUM_GPLOT_OUTPUTS) |
| return ERROR_INT("invalid plottype", procName, 1); |
| |
| if (comptype == L_COMPARE_SUBTRACT) |
| pixt = pixSubtractGray(NULL, pix1, pix2); |
| else /* comptype == L_COMPARE_ABS_DIFF) */ |
| pixt = pixAbsDifference(pix1, pix2); |
| |
| if (psame) |
| pixZero(pixt, psame); |
| |
| if (pdiff) |
| pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_MEAN_ABSVAL, pdiff); |
| |
| if (plottype) { |
| na = pixGetGrayHistogram(pixt, 1); |
| numaGetNonzeroRange(na, TINY, &first, &last); |
| nac = numaClipToInterval(na, 0, last); |
| gplot = gplotCreate("/tmp/junkgrayroot", plottype, |
| "Pixel Difference Histogram", "diff val", |
| "number of pixels"); |
| gplotAddPlot(gplot, NULL, nac, GPLOT_LINES, "gray"); |
| gplotMakeOutput(gplot); |
| gplotDestroy(&gplot); |
| numaDestroy(&na); |
| numaDestroy(&nac); |
| } |
| |
| if (ppixdiff) |
| *ppixdiff = pixCopy(NULL, pixt); |
| |
| if (prmsdiff) { |
| if (comptype == L_COMPARE_SUBTRACT) { /* wrong type for rms diff */ |
| pixDestroy(&pixt); |
| pixt = pixAbsDifference(pix1, pix2); |
| } |
| pixGetAverageMasked(pixt, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, prmsdiff); |
| } |
| |
| pixDestroy(&pixt); |
| return 0; |
| } |
| |
| |
| /*! |
| * pixCompareRGB() |
| * |
| * Input: pix1 (32 bpp rgb) |
| * pix2 (32 bpp rgb) |
| * comptype (L_COMPARE_SUBTRACT, L_COMPARE_ABS_DIFF) |
| * plottype (gplot plot output type, or 0 for no plot) |
| * &same (<optional return> 1 if pixel values are identical) |
| * &diff (<optional return> average difference) |
| * &rmsdiff (<optional return> rms of difference) |
| * &pixdiff (<optional return> pix of difference) |
| * Return: 0 if OK; 1 on error |
| * |
| * Notes: |
| * (1) See pixCompareGrayOrRGB() for details. |
| */ |
| l_int32 |
| pixCompareRGB(PIX *pix1, |
| PIX *pix2, |
| l_int32 comptype, |
| l_int32 plottype, |
| l_int32 *psame, |
| l_float32 *pdiff, |
| l_float32 *prmsdiff, |
| PIX **ppixdiff) |
| { |
| l_int32 rsame, gsame, bsame, first, rlast, glast, blast, last; |
| l_float32 rdiff, gdiff, bdiff; |
| GPLOT *gplot; |
| NUMA *nar, *nag, *nab, *narc, *nagc, *nabc; |
| PIX *pixr1, *pixr2, *pixg1, *pixg2, *pixb1, *pixb2, *pixr, *pixg, *pixb; |
| |
| PROCNAME("pixCompareRGB"); |
| |
| if (ppixdiff) *ppixdiff = NULL; |
| if (!pix1 || pixGetDepth(pix1) != 32) |
| return ERROR_INT("pix1 not defined or not 32 bpp", procName, 1); |
| if (!pix2 || pixGetDepth(pix2) != 32) |
| return ERROR_INT("pix2 not defined or not ew bpp", procName, 1); |
| if (comptype != L_COMPARE_SUBTRACT && comptype != L_COMPARE_ABS_DIFF) |
| return ERROR_INT("invalid comptype", procName, 1); |
| if (plottype > NUM_GPLOT_OUTPUTS) |
| return ERROR_INT("invalid plottype", procName, 1); |
| |
| pixr1 = pixGetRGBComponent(pix1, COLOR_RED); |
| pixr2 = pixGetRGBComponent(pix2, COLOR_RED); |
| pixg1 = pixGetRGBComponent(pix1, COLOR_GREEN); |
| pixg2 = pixGetRGBComponent(pix2, COLOR_GREEN); |
| pixb1 = pixGetRGBComponent(pix1, COLOR_BLUE); |
| pixb2 = pixGetRGBComponent(pix2, COLOR_BLUE); |
| if (comptype == L_COMPARE_SUBTRACT) { |
| pixr = pixSubtractGray(NULL, pixr1, pixr2); |
| pixg = pixSubtractGray(NULL, pixg1, pixg2); |
| pixb = pixSubtractGray(NULL, pixb1, pixb2); |
| } |
| else { /* comptype == L_COMPARE_ABS_DIFF) */ |
| pixr = pixAbsDifference(pixr1, pixr2); |
| pixg = pixAbsDifference(pixg1, pixg2); |
| pixb = pixAbsDifference(pixb1, pixb2); |
| } |
| |
| if (psame) { |
| pixZero(pixr, &rsame); |
| pixZero(pixg, &gsame); |
| pixZero(pixb, &bsame); |
| if (!rsame || !gsame || !bsame) |
| *psame = 0; |
| else |
| *psame = 1; |
| } |
| |
| if (pdiff) { |
| pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_MEAN_ABSVAL, &rdiff); |
| pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_MEAN_ABSVAL, &gdiff); |
| pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_MEAN_ABSVAL, &bdiff); |
| *pdiff = (rdiff + gdiff + bdiff) / 3.0; |
| } |
| |
| if (plottype) { |
| nar = pixGetGrayHistogram(pixr, 1); |
| nag = pixGetGrayHistogram(pixg, 1); |
| nab = pixGetGrayHistogram(pixb, 1); |
| numaGetNonzeroRange(nar, TINY, &first, &rlast); |
| numaGetNonzeroRange(nag, TINY, &first, &glast); |
| numaGetNonzeroRange(nab, TINY, &first, &blast); |
| last = L_MAX(rlast, glast); |
| last = L_MAX(last, blast); |
| narc = numaClipToInterval(nar, 0, last); |
| nagc = numaClipToInterval(nag, 0, last); |
| nabc = numaClipToInterval(nab, 0, last); |
| gplot = gplotCreate("/tmp/junkrgbroot", plottype, |
| "Pixel Difference Histogram", "diff val", |
| "number of pixels"); |
| gplotAddPlot(gplot, NULL, narc, GPLOT_LINES, "red"); |
| gplotAddPlot(gplot, NULL, nagc, GPLOT_LINES, "green"); |
| gplotAddPlot(gplot, NULL, nabc, GPLOT_LINES, "blue"); |
| gplotMakeOutput(gplot); |
| gplotDestroy(&gplot); |
| numaDestroy(&nar); |
| numaDestroy(&nag); |
| numaDestroy(&nab); |
| numaDestroy(&narc); |
| numaDestroy(&nagc); |
| numaDestroy(&nabc); |
| } |
| |
| if (ppixdiff) |
| *ppixdiff = pixCreateRGBImage(pixr, pixg, pixb); |
| |
| if (prmsdiff) { |
| if (comptype == L_COMPARE_SUBTRACT) { |
| pixDestroy(&pixr); |
| pixDestroy(&pixg); |
| pixDestroy(&pixb); |
| pixr = pixAbsDifference(pixr1, pixr2); |
| pixg = pixAbsDifference(pixg1, pixg2); |
| pixb = pixAbsDifference(pixb1, pixb2); |
| } |
| pixGetAverageMasked(pixr, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &rdiff); |
| pixGetAverageMasked(pixg, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &gdiff); |
| pixGetAverageMasked(pixb, NULL, 0, 0, 1, L_ROOT_MEAN_SQUARE, &bdiff); |
| *prmsdiff = (rdiff + gdiff + bdiff) / 3.0; |
| } |
| |
| pixDestroy(&pixr1); |
| pixDestroy(&pixr2); |
| pixDestroy(&pixg1); |
| pixDestroy(&pixg2); |
| pixDestroy(&pixb1); |
| pixDestroy(&pixb2); |
| pixDestroy(&pixr); |
| pixDestroy(&pixg); |
| pixDestroy(&pixb); |
| return 0; |
| } |
| |
| |
| /*! |
| * pixCompareTiled() |
| * |
| * Input: pix1 (8 bpp or 32 bpp rgb) |
| * pix2 (8 bpp 32 bpp rgb) |
| * sx, sy (tile size; must be > 1) |
| * type (L_MEAN_ABSVAL or L_ROOT_MEAN_SQUARE) |
| * &pixdiff (<return> pix of difference) |
| * Return: 0 if OK; 1 on error |
| * |
| * Notes: |
| * (1) With L_MEAN_ABSVAL, we compute for each tile the |
| * average abs value of the pixel component difference between |
| * the two (aligned) images. With L_ROOT_MEAN_SQUARE, we |
| * compute instead the rms difference over all components. |
| * (2) The two input pix must be the same depth. Comparison is made |
| * using UL corner alignment. |
| * (3) For 32 bpp, the distance between corresponding tiles |
| * is found by averaging the measured difference over all three |
| * components of each pixel in the tile. |
| * (4) The result, pixdiff, contains one pixel for each source tile. |
| */ |
| l_int32 |
| pixCompareTiled(PIX *pix1, |
| PIX *pix2, |
| l_int32 sx, |
| l_int32 sy, |
| l_int32 type, |
| PIX **ppixdiff) |
| { |
| l_int32 d1, d2, w, h; |
| PIX *pixt, *pixr, *pixg, *pixb; |
| PIX *pixrdiff, *pixgdiff, *pixbdiff; |
| PIXACC *pixacc; |
| |
| PROCNAME("pixCompareTiled"); |
| |
| if (!ppixdiff) |
| return ERROR_INT("&pixdiff not defined", procName, 1); |
| *ppixdiff = NULL; |
| if (!pix1) |
| return ERROR_INT("pix1 not defined", procName, 1); |
| if (!pix2) |
| return ERROR_INT("pix2 not defined", procName, 1); |
| d1 = pixGetDepth(pix1); |
| d2 = pixGetDepth(pix2); |
| if (d1 != d2) |
| return ERROR_INT("depths not equal", procName, 1); |
| if (d1 != 8 && d1 != 32) |
| return ERROR_INT("pix1 not 8 or 32 bpp", procName, 1); |
| if (d2 != 8 && d2 != 32) |
| return ERROR_INT("pix2 not 8 or 32 bpp", procName, 1); |
| if (sx < 2 || sy < 2) |
| return ERROR_INT("sx and sy not both > 1", procName, 1); |
| if (type != L_MEAN_ABSVAL && type != L_ROOT_MEAN_SQUARE) |
| return ERROR_INT("invalid type", procName, 1); |
| |
| pixt = pixAbsDifference(pix1, pix2); |
| if (d1 == 8) |
| *ppixdiff = pixGetAverageTiled(pixt, sx, sy, type); |
| else { /* d1 == 32 */ |
| pixr = pixGetRGBComponent(pixt, COLOR_RED); |
| pixg = pixGetRGBComponent(pixt, COLOR_GREEN); |
| pixb = pixGetRGBComponent(pixt, COLOR_BLUE); |
| pixrdiff = pixGetAverageTiled(pixr, sx, sy, type); |
| pixgdiff = pixGetAverageTiled(pixg, sx, sy, type); |
| pixbdiff = pixGetAverageTiled(pixb, sx, sy, type); |
| pixGetDimensions(pixrdiff, &w, &h, NULL); |
| pixacc = pixaccCreate(w, h, 0); |
| pixaccAdd(pixacc, pixrdiff); |
| pixaccAdd(pixacc, pixgdiff); |
| pixaccAdd(pixacc, pixbdiff); |
| pixaccMultConst(pixacc, 1. / 3.); |
| *ppixdiff = pixaccFinal(pixacc, 8); |
| pixDestroy(&pixr); |
| pixDestroy(&pixg); |
| pixDestroy(&pixb); |
| pixDestroy(&pixrdiff); |
| pixDestroy(&pixgdiff); |
| pixDestroy(&pixbdiff); |
| pixaccDestroy(&pixacc); |
| } |
| pixDestroy(&pixt); |
| return 0; |
| } |
| |
| |
| /*! |
| * pixCompareRankDifference() |
| * |
| * Input: pix1 (8 bpp gray or 32 bpp rgb, or colormapped) |
| * pix2 (8 bpp gray or 32 bpp rgb, or colormapped) |
| * Return: narank (numa of rank difference), or null on error |
| * |
| * Notes: |
| * (1) This answers the question: if the pixel values in each |
| * component are compared by absolute difference, for |
| * any value of difference, what is the fraction of |
| * pixel pairs that have a difference of this magnitude |
| * or greater. For a difference of 0, the fraction is 1.0. |
| * In this sense, it is a mapping from pixel difference to |
| * rank order of difference. |
| * (2) The two images are aligned at the UL corner, and do not |
| * need to be the same size. If they are not the same size, |
| * the comparison will be made over overlapping pixels. |
| * (3) If there is a colormap, it is removed and the result |
| * is either gray or RGB depending on the colormap. |
| * (4) If RGB, pixel differences for each component are aggregated |
| * into a single histogram. |
| */ |
| NUMA * |
| pixCompareRankDifference(PIX *pix1, |
| PIX *pix2) |
| { |
| l_int32 w1, h1, d1, w2, h2, d2, w, h, wpl1, wpl2; |
| l_int32 i, j, val, val1, val2; |
| l_uint32 pixel1, pixel2; |
| l_uint32 *data1, *data2, *line1, *line2; |
| l_float32 *array1, *array2; |
| NUMA *nah, *nan, *nad; |
| PIX *pixt1, *pixt2; |
| |
| PROCNAME("pixCompareRankDifference"); |
| |
| if (!pix1) |
| return (NUMA *)ERROR_PTR("pix1 not defined", procName, NULL); |
| if (!pix2) |
| return (NUMA *)ERROR_PTR("pix2 not defined", procName, NULL); |
| d1 = pixGetDepth(pix1); |
| d2 = pixGetDepth(pix2); |
| if (d1 == 16 || d2 == 16) |
| return (NUMA *)ERROR_PTR("d == 16 not supported", procName, NULL); |
| if (d1 < 8 && !pixGetColormap(pix1)) |
| return (NUMA *)ERROR_PTR("pix1 depth < 8 bpp and not cmapped", |
| procName, NULL); |
| if (d2 < 8 && !pixGetColormap(pix2)) |
| return (NUMA *)ERROR_PTR("pix2 depth < 8 bpp and not cmapped", |
| procName, NULL); |
| pixt1 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); |
| pixt2 = pixRemoveColormap(pix2, REMOVE_CMAP_BASED_ON_SRC); |
| pixGetDimensions(pixt1, &w1, &h1, &d1); |
| pixGetDimensions(pixt2, &w2, &h2, &d2); |
| if (d1 != d2) { |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| return (NUMA *)ERROR_PTR("pix depths not equal", procName, NULL); |
| } |
| |
| nah = numaCreate(256); |
| numaSetCount(nah, 256); /* all initialized to 0.0 */ |
| array1 = numaGetFArray(nah, L_NOCOPY); |
| w = L_MIN(w1, w2); |
| h = L_MIN(h1, h2); |
| data1 = pixGetData(pixt1); |
| data2 = pixGetData(pixt2); |
| wpl1 = pixGetWpl(pixt1); |
| wpl2 = pixGetWpl(pixt2); |
| if (d1 == 8) { |
| for (i = 0; i < h; i++) { |
| line1 = data1 + i * wpl1; |
| line2 = data2 + i * wpl2; |
| for (j = 0; j < w; j++) { |
| val1 = GET_DATA_BYTE(line1, j); |
| val2 = GET_DATA_BYTE(line2, j); |
| val = L_ABS(val1 - val2); |
| array1[val]++; |
| } |
| } |
| } |
| else { /* d1 == 32 */ |
| for (i = 0; i < h; i++) { |
| line1 = data1 + i * wpl1; |
| line2 = data2 + i * wpl2; |
| for (j = 0; j < w; j++) { |
| pixel1 = line1[j]; |
| pixel2 = line2[j]; |
| val1 = pixel1 >> 24; |
| val2 = pixel2 >> 24; |
| val = L_ABS(val1 - val2); |
| array1[val]++; |
| val1 = (pixel1 >> 16) & 0xff; |
| val2 = (pixel2 >> 16) & 0xff; |
| val = L_ABS(val1 - val2); |
| array1[val]++; |
| val1 = (pixel1 >> 8) & 0xff; |
| val2 = (pixel2 >> 8) & 0xff; |
| val = L_ABS(val1 - val2); |
| array1[val]++; |
| } |
| } |
| } |
| |
| nan = numaNormalizeHistogram(nah, 1.0); |
| array1 = numaGetFArray(nan, L_NOCOPY); |
| |
| nad = numaCreate(256); |
| numaSetCount(nad, 256); /* all initialized to 0.0 */ |
| array2 = numaGetFArray(nad, L_NOCOPY); |
| |
| /* Finally, do rank accumulation on normalized histo of diffs */ |
| array2[0] = 1.0; |
| for (i = 1; i < 256; i++) |
| array2[i] = array2[i - 1] - array1[i - 1]; |
| |
| pixDestroy(&pixt1); |
| pixDestroy(&pixt2); |
| numaDestroy(&nah); |
| numaDestroy(&nan); |
| return nad; |
| } |
| |
| |