From f175b23559f3ba759d222538fd28922c1cbe2196 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Thu, 14 Dec 2006 03:49:31 -0800 Subject: pdiff: Rename everything to .c and fix an last littele C++ isms. The only things we had missed were a few new/delete pairs, and some obvious header file fixups, (like not doing ). --- test/pdiff/CompareArgs.cpp | 114 ------------- test/pdiff/Makefile.am | 6 +- test/pdiff/PerceptualDiff.cpp | 101 ------------ test/pdiff/args.c | 119 ++++++++++++++ test/pdiff/args.cpp | 118 -------------- test/pdiff/args.h | 2 +- test/pdiff/lpyramid.c | 113 +++++++++++++ test/pdiff/lpyramid.cpp | 113 ------------- test/pdiff/pdiff.c | 361 ++++++++++++++++++++++++++++++++++++++++++ test/pdiff/pdiff.cpp | 347 ---------------------------------------- test/pdiff/pdiff.h | 16 +- test/pdiff/perceptualdiff.c | 101 ++++++++++++ 12 files changed, 706 insertions(+), 805 deletions(-) delete mode 100644 test/pdiff/CompareArgs.cpp delete mode 100644 test/pdiff/PerceptualDiff.cpp create mode 100644 test/pdiff/args.c delete mode 100644 test/pdiff/args.cpp create mode 100644 test/pdiff/lpyramid.c delete mode 100644 test/pdiff/lpyramid.cpp create mode 100644 test/pdiff/pdiff.c delete mode 100644 test/pdiff/pdiff.cpp create mode 100644 test/pdiff/perceptualdiff.c (limited to 'test/pdiff') diff --git a/test/pdiff/CompareArgs.cpp b/test/pdiff/CompareArgs.cpp deleted file mode 100644 index 3b58ff7d..00000000 --- a/test/pdiff/CompareArgs.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - Comapre Args - Copyright (C) 2006 Yangli Hector Yee - - This program is free software; you can redistribute it and/or modify it under the terms of the - GNU General Public License as published by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "CompareArgs.h" -#include - -static const char* copyright = -"PerceptualDiff version 1.0, Copyright (C) 2006 Yangli Hector Yee\n\ -PerceptualDiff comes with ABSOLUTELY NO WARRANTY;\n\ -This is free software, and you are welcome\n\ -to redistribute it under certain conditions;\n\ -See the GPL page for details: http://www.gnu.org/copyleft/gpl.html\n\n"; - -static const char *usage = -"PeceptualDiff image1.tif image2.tif\n\n\ - Compares image1.tif and image2.tif using a perceptually based image metric\n\ - Options:\n\ -\t-verbose : Turns on verbose mode\n\ -\t-fov deg : Field of view in degrees (0.1 to 89.9)\n\ -\t-threshold p : #pixels p below which differences are ignored\n\ -\t-gamma g : Value to convert rgb into linear space (default 2.2)\n\ -\t-luminance l : White luminance (default 100.0 cdm^-2)\n\ -\n\ -\n Note: Input files can also be in the PNG format\ -\n"; - -CompareArgs::CompareArgs() -{ - surface_a = NULL; - surface_b = NULL; - Verbose = false; - FieldOfView = 45.0f; - Gamma = 2.2f; - ThresholdPixels = 100; - Luminance = 100.0f; -} - -CompareArgs::~CompareArgs() -{ - cairo_surface_destroy (surface_a); - cairo_surface_destroy (surface_b); -} - -bool CompareArgs::Parse_Args(int argc, char **argv) -{ - int i; - - if (argc < 3) { - fprintf (stderr, "%s", copyright); - fprintf (stderr, "%s", usage); - return false; - } - for (i = 0; i < argc; i++) { - if (i == 1) { - surface_a = cairo_image_surface_create_from_png (argv[1]); - if (cairo_surface_status (surface_a)) - { - fprintf (stderr, "FAIL: Cannot open %s: %s\n", - argv[1], cairo_status_to_string (cairo_surface_status (surface_a))); - return false; - } - } else if (i == 2) { - surface_b = cairo_image_surface_create_from_png (argv[2]); - if (cairo_surface_status (surface_b)) - { - fprintf (stderr, "FAIL: Cannot open %s: %s\n", - argv[2], cairo_status_to_string (cairo_surface_status (surface_b))); - return false; - } - } else { - if (strstr(argv[i], "-fov")) { - if (i + 1 < argc) { - FieldOfView = (float) atof(argv[i + 1]); - } - } else if (strstr(argv[i], "-verbose")) { - Verbose = true; - } else if (strstr(argv[i], "-threshold")) { - if (i + 1 < argc) { - ThresholdPixels = atoi(argv[i + 1]); - } - } else if (strstr(argv[i], "-gamma")) { - if (i + 1 < argc) { - Gamma = (float) atof(argv[i + 1]); - } - }else if (strstr(argv[i], "-luminance")) { - if (i + 1 < argc) { - Luminance = (float) atof(argv[i + 1]); - } - } - } - } /* i */ - return true; -} - -void CompareArgs::Print_Args() -{ - printf("Field of view is %f degrees\n", FieldOfView); - printf("Threshold pixels is %d pixels\n", ThresholdPixels); - printf("The Gamma is %f\n", Gamma); - printf("The Display's luminance is %f candela per meter squared\n", Luminance); -} diff --git a/test/pdiff/Makefile.am b/test/pdiff/Makefile.am index f53c8d2d..d72b2732 100644 --- a/test/pdiff/Makefile.am +++ b/test/pdiff/Makefile.am @@ -5,12 +5,12 @@ libpdiff_la_SOURCES = \ pdiff.h \ lpyramid.c \ lpyramid.h \ - pdiff.cpp + pdiff.c perceptualdiff_SOURCES = \ - args.cpp \ + args.c \ args.h \ - PerceptualDiff.cpp + perceptualdiff.c INCLUDES = -I$(top_srcdir)/src LDADD = libpdiff.la $(top_builddir)/src/libcairo.la diff --git a/test/pdiff/PerceptualDiff.cpp b/test/pdiff/PerceptualDiff.cpp deleted file mode 100644 index 1df3614a..00000000 --- a/test/pdiff/PerceptualDiff.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - PerceptualDiff - a program that compares two images using a perceptual metric - based on the paper : - A perceptual metric for production testing. Journal of graphics tools, 9(4):33-40, 2004, Hector Yee - Copyright (C) 2006 Yangli Hector Yee - - This program is free software; you can redistribute it and/or modify it under the terms of the - GNU General Public License as published by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include -#include "lpyramid.h" -#include "args.h" -#include "pdiff.h" - -static bool Yee_Compare(args_t *args) -{ - int width_a, height_a, stride_a; - unsigned char *data_a, *row_a; - uint32_t *pixel_a; - int width_b, height_b, stride_b; - unsigned char *data_b, *row_b; - uint32_t *pixel_b; - unsigned int x, y, dim, pixels_failed; - bool identical = true; - - width_a = cairo_image_surface_get_width (args->surface_a); - height_a = cairo_image_surface_get_height (args->surface_a); - stride_a = cairo_image_surface_get_stride (args->surface_a); - data_a = cairo_image_surface_get_data (args->surface_a); - - width_b = cairo_image_surface_get_width (args->surface_b); - height_b = cairo_image_surface_get_height (args->surface_b); - stride_b = cairo_image_surface_get_stride (args->surface_b); - data_b = cairo_image_surface_get_data (args->surface_b); - - if ((width_a != width_b) || (height_a != height_b)) { - printf ("FAIL: Image dimensions do not match\n"); - return false; - } - - identical = true; - - for (y = 0; y < height_a; y++) { - row_a = data_a + y * stride_a; - row_b = data_b + y * stride_b; - pixel_a = (uint32_t *) row_a; - pixel_b = (uint32_t *) row_b; - for (x = 0; x < width_a; x++) { - if (*pixel_a != *pixel_b) { - identical = false; - } - pixel_a++; - pixel_b++; - } - } - if (identical) { - printf ("PASS: Images are binary identical\n"); - return true; - } - - pixels_failed = pdiff_compare (args->surface_a, args->surface_b, - args->Gamma, args->Luminance, - args->FieldOfView); - - if (pixels_failed < args->ThresholdPixels) { - printf ("PASS: Images are perceptually indistinguishable\n"); - return true; - } - - printf("FAIL: Images are visibly different\n" - "%d pixels are different\n", pixels_failed); - - return false; -} - -int main(int argc, char **argv) -{ - args_t args; - - args_init (&args); - - if (!args_parse (&args, argc, argv)) { - return -1; - } else { - if (args.Verbose) - args_print (&args); - } - return ! Yee_Compare(&args); -} diff --git a/test/pdiff/args.c b/test/pdiff/args.c new file mode 100644 index 00000000..0dfbea54 --- /dev/null +++ b/test/pdiff/args.c @@ -0,0 +1,119 @@ +/* + Comapre Args + Copyright (C) 2006 Yangli Hector Yee + + This program is free software; you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "args.h" +#include +#include +#include + +static const char* copyright = +"PerceptualDiff version 1.0, Copyright (C) 2006 Yangli Hector Yee\n\ +PerceptualDiff comes with ABSOLUTELY NO WARRANTY;\n\ +This is free software, and you are welcome\n\ +to redistribute it under certain conditions;\n\ +See the GPL page for details: http://www.gnu.org/copyleft/gpl.html\n\n"; + +static const char *usage = +"PeceptualDiff image1.tif image2.tif\n\n\ + Compares image1.tif and image2.tif using a perceptually based image metric\n\ + Options:\n\ +\t-verbose : Turns on verbose mode\n\ +\t-fov deg : Field of view in degrees (0.1 to 89.9)\n\ +\t-threshold p : #pixels p below which differences are ignored\n\ +\t-gamma g : Value to convert rgb into linear space (default 2.2)\n\ +\t-luminance l : White luminance (default 100.0 cdm^-2)\n\ +\n\ +\n Note: Input files can also be in the PNG format\ +\n"; + +void +args_init (args_t *args) +{ + args->surface_a = NULL; + args->surface_b = NULL; + args->Verbose = false; + args->FieldOfView = 45.0f; + args->Gamma = 2.2f; + args->ThresholdPixels = 100; + args->Luminance = 100.0f; +} + +void +args_fini (args_t *args) +{ + cairo_surface_destroy (args->surface_a); + cairo_surface_destroy (args->surface_b); +} + +bool +args_parse (args_t *args, int argc, char **argv) +{ + int i; + if (argc < 3) { + fprintf (stderr, "%s", copyright); + fprintf (stderr, "%s", usage); + return false; + } + for (i = 0; i < argc; i++) { + if (i == 1) { + args->surface_a = cairo_image_surface_create_from_png (argv[1]); + if (cairo_surface_status (args->surface_a)) + { + fprintf (stderr, "FAIL: Cannot open %s: %s\n", + argv[1], cairo_status_to_string (cairo_surface_status (args->surface_a))); + return false; + } + } else if (i == 2) { + args->surface_b = cairo_image_surface_create_from_png (argv[2]); + if (cairo_surface_status (args->surface_b)) + { + fprintf (stderr, "FAIL: Cannot open %s: %s\n", + argv[2], cairo_status_to_string (cairo_surface_status (args->surface_b))); + return false; + } + } else { + if (strstr(argv[i], "-fov")) { + if (i + 1 < argc) { + args->FieldOfView = (float) atof(argv[i + 1]); + } + } else if (strstr(argv[i], "-verbose")) { + args->Verbose = true; + } else if (strstr(argv[i], "-threshold")) { + if (i + 1 < argc) { + args->ThresholdPixels = atoi(argv[i + 1]); + } + } else if (strstr(argv[i], "-gamma")) { + if (i + 1 < argc) { + args->Gamma = (float) atof(argv[i + 1]); + } + }else if (strstr(argv[i], "-luminance")) { + if (i + 1 < argc) { + args->Luminance = (float) atof(argv[i + 1]); + } + } + } + } /* i */ + return true; +} + +void +args_print (args_t *args) +{ + printf("Field of view is %f degrees\n", args->FieldOfView); + printf("Threshold pixels is %d pixels\n", args->ThresholdPixels); + printf("The Gamma is %f\n", args->Gamma); + printf("The Display's luminance is %f candela per meter squared\n", args->Luminance); +} diff --git a/test/pdiff/args.cpp b/test/pdiff/args.cpp deleted file mode 100644 index 9fbe23ed..00000000 --- a/test/pdiff/args.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - Comapre Args - Copyright (C) 2006 Yangli Hector Yee - - This program is free software; you can redistribute it and/or modify it under the terms of the - GNU General Public License as published by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "args.h" -#include -#include -#include - -static const char* copyright = -"PerceptualDiff version 1.0, Copyright (C) 2006 Yangli Hector Yee\n\ -PerceptualDiff comes with ABSOLUTELY NO WARRANTY;\n\ -This is free software, and you are welcome\n\ -to redistribute it under certain conditions;\n\ -See the GPL page for details: http://www.gnu.org/copyleft/gpl.html\n\n"; - -static const char *usage = -"PeceptualDiff image1.tif image2.tif\n\n\ - Compares image1.tif and image2.tif using a perceptually based image metric\n\ - Options:\n\ -\t-verbose : Turns on verbose mode\n\ -\t-fov deg : Field of view in degrees (0.1 to 89.9)\n\ -\t-threshold p : #pixels p below which differences are ignored\n\ -\t-gamma g : Value to convert rgb into linear space (default 2.2)\n\ -\t-luminance l : White luminance (default 100.0 cdm^-2)\n\ -\n\ -\n Note: Input files can also be in the PNG format\ -\n"; - -void -args_init (args_t *args) -{ - args->surface_a = NULL; - args->surface_b = NULL; - args->Verbose = false; - args->FieldOfView = 45.0f; - args->Gamma = 2.2f; - args->ThresholdPixels = 100; - args->Luminance = 100.0f; -} - -void -args_fini (args_t *args) -{ - cairo_surface_destroy (args->surface_a); - cairo_surface_destroy (args->surface_b); -} - -bool -args_parse (args_t *args, int argc, char **argv) -{ - if (argc < 3) { - fprintf (stderr, "%s", copyright); - fprintf (stderr, "%s", usage); - return false; - } - for (int i = 0; i < argc; i++) { - if (i == 1) { - args->surface_a = cairo_image_surface_create_from_png (argv[1]); - if (cairo_surface_status (args->surface_a)) - { - fprintf (stderr, "FAIL: Cannot open %s: %s\n", - argv[1], cairo_status_to_string (cairo_surface_status (args->surface_a))); - return false; - } - } else if (i == 2) { - args->surface_b = cairo_image_surface_create_from_png (argv[2]); - if (cairo_surface_status (args->surface_b)) - { - fprintf (stderr, "FAIL: Cannot open %s: %s\n", - argv[2], cairo_status_to_string (cairo_surface_status (args->surface_b))); - return false; - } - } else { - if (strstr(argv[i], "-fov")) { - if (i + 1 < argc) { - args->FieldOfView = (float) atof(argv[i + 1]); - } - } else if (strstr(argv[i], "-verbose")) { - args->Verbose = true; - } else if (strstr(argv[i], "-threshold")) { - if (i + 1 < argc) { - args->ThresholdPixels = atoi(argv[i + 1]); - } - } else if (strstr(argv[i], "-gamma")) { - if (i + 1 < argc) { - args->Gamma = (float) atof(argv[i + 1]); - } - }else if (strstr(argv[i], "-luminance")) { - if (i + 1 < argc) { - args->Luminance = (float) atof(argv[i + 1]); - } - } - } - } /* i */ - return true; -} - -void -args_print (args_t *args) -{ - printf("Field of view is %f degrees\n", args->FieldOfView); - printf("Threshold pixels is %d pixels\n", args->ThresholdPixels); - printf("The Gamma is %f\n", args->Gamma); - printf("The Display's luminance is %f candela per meter squared\n", args->Luminance); -} diff --git a/test/pdiff/args.h b/test/pdiff/args.h index 0197c902..aa6cf299 100644 --- a/test/pdiff/args.h +++ b/test/pdiff/args.h @@ -17,7 +17,7 @@ #ifndef _ARGS_H #define _ARGS_H -#include +#include "pdiff.h" /* Args to pass into the comparison function */ typedef struct _args diff --git a/test/pdiff/lpyramid.c b/test/pdiff/lpyramid.c new file mode 100644 index 00000000..92915ab0 --- /dev/null +++ b/test/pdiff/lpyramid.c @@ -0,0 +1,113 @@ +/* + Laplacian Pyramid + Copyright (C) 2006 Yangli Hector Yee + + This program is free software; you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "lpyramid.h" +#include +#include +#include + +struct _lpyramid { + /* Succesively blurred versions of the original image */ + float *levels[MAX_PYR_LEVELS]; + + int width; + int height; +}; + +static void +convolve (lpyramid_t *pyramid, float *a, float *b) +/* convolves image b with the filter kernel and stores it in a */ +{ + int y,x,i,j,nx,ny; + const float Kernel[] = {0.05f, 0.25f, 0.4f, 0.25f, 0.05f}; + int width = pyramid->width; + int height = pyramid->height; + + for (y=0; y=width) nx=2*(width-1)-nx; + if (ny>=height) ny=2*(height-1)-ny; + a[index] += Kernel[i+2] * Kernel[j+2] * b[ny * width + nx]; + } + } + } + } +} + +/* + * Construction/Destruction + */ + +lpyramid_t * +lpyramid_create (float *image, int width, int height) +{ + lpyramid_t *pyramid; + int i; + + /* XXX: Remove stupid cast after finishing port to C */ + pyramid = (lpyramid_t *) malloc (sizeof (lpyramid_t)); + if (pyramid == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } + pyramid->width = width; + pyramid->height = height; + + /* Make the Laplacian pyramid by successively + * copying the earlier levels and blurring them */ + for (i=0; ilevels[i] = (float *) malloc (width * height * sizeof (float)); + if (pyramid->levels[i] == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } + if (i == 0) { + memcpy (pyramid->levels[i], image, width * height * sizeof (float)); + } else { + convolve(pyramid, pyramid->levels[i], pyramid->levels[i - 1]); + } + } + + return pyramid; +} + +void +lpyramid_destroy (lpyramid_t *pyramid) +{ + int i; + + for (i=0; ilevels[i]); +} + +float +lpyramid_get_value (lpyramid_t *pyramid, int x, int y, int level) +{ + int index = x + y * pyramid->width; + int l = level; + if (l > MAX_PYR_LEVELS) + l = MAX_PYR_LEVELS; + return pyramid->levels[level][index]; +} diff --git a/test/pdiff/lpyramid.cpp b/test/pdiff/lpyramid.cpp deleted file mode 100644 index 92915ab0..00000000 --- a/test/pdiff/lpyramid.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - Laplacian Pyramid - Copyright (C) 2006 Yangli Hector Yee - - This program is free software; you can redistribute it and/or modify it under the terms of the - GNU General Public License as published by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "lpyramid.h" -#include -#include -#include - -struct _lpyramid { - /* Succesively blurred versions of the original image */ - float *levels[MAX_PYR_LEVELS]; - - int width; - int height; -}; - -static void -convolve (lpyramid_t *pyramid, float *a, float *b) -/* convolves image b with the filter kernel and stores it in a */ -{ - int y,x,i,j,nx,ny; - const float Kernel[] = {0.05f, 0.25f, 0.4f, 0.25f, 0.05f}; - int width = pyramid->width; - int height = pyramid->height; - - for (y=0; y=width) nx=2*(width-1)-nx; - if (ny>=height) ny=2*(height-1)-ny; - a[index] += Kernel[i+2] * Kernel[j+2] * b[ny * width + nx]; - } - } - } - } -} - -/* - * Construction/Destruction - */ - -lpyramid_t * -lpyramid_create (float *image, int width, int height) -{ - lpyramid_t *pyramid; - int i; - - /* XXX: Remove stupid cast after finishing port to C */ - pyramid = (lpyramid_t *) malloc (sizeof (lpyramid_t)); - if (pyramid == NULL) { - fprintf (stderr, "Out of memory.\n"); - exit (1); - } - pyramid->width = width; - pyramid->height = height; - - /* Make the Laplacian pyramid by successively - * copying the earlier levels and blurring them */ - for (i=0; ilevels[i] = (float *) malloc (width * height * sizeof (float)); - if (pyramid->levels[i] == NULL) { - fprintf (stderr, "Out of memory.\n"); - exit (1); - } - if (i == 0) { - memcpy (pyramid->levels[i], image, width * height * sizeof (float)); - } else { - convolve(pyramid, pyramid->levels[i], pyramid->levels[i - 1]); - } - } - - return pyramid; -} - -void -lpyramid_destroy (lpyramid_t *pyramid) -{ - int i; - - for (i=0; ilevels[i]); -} - -float -lpyramid_get_value (lpyramid_t *pyramid, int x, int y, int level) -{ - int index = x + y * pyramid->width; - int l = level; - if (l > MAX_PYR_LEVELS) - l = MAX_PYR_LEVELS; - return pyramid->levels[level][index]; -} diff --git a/test/pdiff/pdiff.c b/test/pdiff/pdiff.c new file mode 100644 index 00000000..1b73e796 --- /dev/null +++ b/test/pdiff/pdiff.c @@ -0,0 +1,361 @@ +/* + Metric + Copyright (C) 2006 Yangli Hector Yee + + This program is free software; you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "lpyramid.h" +#include +#include +#include +#include +#include "pdiff.h" + +#ifndef M_PI +#define M_PI 3.14159265f +#endif + +/* + * Given the adaptation luminance, this function returns the + * threshold of visibility in cd per m^2 + * TVI means Threshold vs Intensity function + * This version comes from Ward Larson Siggraph 1997 + */ +static float +tvi (float adaptation_luminance) +{ + /* returns the threshold luminance given the adaptation luminance + units are candelas per meter squared + */ + float log_a, r, result; + log_a = log10f(adaptation_luminance); + + if (log_a < -3.94f) { + r = -2.86f; + } else if (log_a < -1.44f) { + r = powf(0.405f * log_a + 1.6f , 2.18f) - 2.86f; + } else if (log_a < -0.0184f) { + r = log_a - 0.395f; + } else if (log_a < 1.9f) { + r = powf(0.249f * log_a + 0.65f, 2.7f) - 0.72f; + } else { + r = log_a - 1.255f; + } + + result = powf(10.0f , r); + + return result; +} + +/* computes the contrast sensitivity function (Barten SPIE 1989) + * given the cycles per degree (cpd) and luminance (lum) + */ +static float +csf (float cpd, float lum) +{ + float a, b, result; + + a = 440.0f * powf((1.0f + 0.7f / lum), -0.2f); + b = 0.3f * powf((1.0f + 100.0f / lum), 0.15f); + + result = a * cpd * expf(-b * cpd) * sqrtf(1.0f + 0.06f * expf(b * cpd)); + + return result; +} + +/* + * Visual Masking Function + * from Daly 1993 + */ +static float +mask (float contrast) +{ + float a, b, result; + a = powf(392.498f * contrast, 0.7f); + b = powf(0.0153f * a, 4.0f); + result = powf(1.0f + b, 0.25f); + + return result; +} + +/* convert Adobe RGB (1998) with reference white D65 to XYZ */ +static void +AdobeRGBToXYZ (float r, float g, float b, float *x, float *y, float *z) +{ + /* matrix is from http://www.brucelindbloom.com/ */ + *x = r * 0.576700f + g * 0.185556f + b * 0.188212f; + *y = r * 0.297361f + g * 0.627355f + b * 0.0752847f; + *z = r * 0.0270328f + g * 0.0706879f + b * 0.991248f; +} + +static void +XYZToLAB (float x, float y, float z, float *L, float *A, float *B) +{ + static float xw = -1; + static float yw; + static float zw; + const float epsilon = 216.0f / 24389.0f; + const float kappa = 24389.0f / 27.0f; + float f[3]; + float r[3]; + int i; + + /* reference white */ + if (xw < 0) { + AdobeRGBToXYZ(1, 1, 1, &xw, &yw, &zw); + } + r[0] = x / xw; + r[1] = y / yw; + r[2] = z / zw; + for (i = 0; i < 3; i++) { + if (r[i] > epsilon) { + f[i] = powf(r[i], 1.0f / 3.0f); + } else { + f[i] = (kappa * r[i] + 16.0f) / 116.0f; + } + } + *L = 116.0f * f[1] - 16.0f; + *A = 500.0f * (f[0] - f[1]); + *B = 200.0f * (f[1] - f[2]); +} + +static uint32_t +_get_pixel (cairo_surface_t *surface, int i) +{ + uint32_t *data; + + data = (uint32_t *) cairo_image_surface_get_data (surface); + return data[i]; +} + +static unsigned char +_get_red (cairo_surface_t *surface, int i) +{ + uint32_t pixel; + uint8_t alpha; + + pixel = _get_pixel (surface, i); + + alpha = (pixel & 0xff000000) >> 24; + + if (alpha == 0) + return 0; + else + return (((pixel & 0x00ff0000) >> 16) * 255 + alpha / 2) / alpha; +} + +static unsigned char +_get_green (cairo_surface_t *surface, int i) +{ + uint32_t pixel; + uint8_t alpha; + + pixel = _get_pixel (surface, i); + + alpha = (pixel & 0xff000000) >> 24; + + if (alpha == 0) + return 0; + else + return (((pixel & 0x0000ff00) >> 8) * 255 + alpha / 2) / alpha; +} + +static unsigned char +_get_blue (cairo_surface_t *surface, int i) +{ + uint32_t pixel; + uint8_t alpha; + + pixel = _get_pixel (surface, i); + + alpha = (pixel & 0xff000000) >> 24; + + if (alpha == 0) + return 0; + else + return (((pixel & 0x000000ff) >> 0) * 255 + alpha / 2) / alpha; +} + +static void * +xmalloc (size_t size) +{ + void *buf; + + buf = malloc (size); + if (buf == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } +} + +int +pdiff_compare (cairo_surface_t *surface_a, + cairo_surface_t *surface_b, + double gamma, + double luminance, + double field_of_view) +{ + unsigned int dim = (cairo_image_surface_get_width (surface_a) + * cairo_image_surface_get_height (surface_a)); + unsigned int i; + + /* assuming colorspaces are in Adobe RGB (1998) convert to XYZ */ + float *aX = xmalloc (dim * sizeof (float)); + float *aY = xmalloc (dim * sizeof (float)); + float *aZ = xmalloc (dim * sizeof (float)); + float *bX = xmalloc (dim * sizeof (float)); + float *bY = xmalloc (dim * sizeof (float)); + float *bZ = xmalloc (dim * sizeof (float)); + float *aLum = xmalloc (dim * sizeof (float)); + float *bLum = xmalloc (dim * sizeof (float)); + + float *aA = xmalloc (dim * sizeof (float)); + float *bA = xmalloc (dim * sizeof (float)); + float *aB = xmalloc (dim * sizeof (float)); + float *bB = xmalloc (dim * sizeof (float)); + + unsigned int x, y, w, h; + + lpyramid_t *la, *lb; + + float num_one_degree_pixels, pixels_per_degree, num_pixels; + unsigned int adaptation_level; + + float cpd[MAX_PYR_LEVELS]; + float F_freq[MAX_PYR_LEVELS - 2]; + float csf_max; + + unsigned int pixels_failed; + + w = cairo_image_surface_get_width (surface_a); + h = cairo_image_surface_get_height (surface_a); + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + float r, g, b, l; + i = x + y * w; + r = powf(_get_red (surface_a, i) / 255.0f, gamma); + g = powf(_get_green (surface_a, i) / 255.0f, gamma); + b = powf(_get_blue (surface_a, i) / 255.0f, gamma); + + AdobeRGBToXYZ(r,g,b,&aX[i],&aY[i],&aZ[i]); + XYZToLAB(aX[i], aY[i], aZ[i], &l, &aA[i], &aB[i]); + r = powf(_get_red (surface_b, i) / 255.0f, gamma); + g = powf(_get_green (surface_b, i) / 255.0f, gamma); + b = powf(_get_blue (surface_b, i) / 255.0f, gamma); + + AdobeRGBToXYZ(r,g,b,&bX[i],&bY[i],&bZ[i]); + XYZToLAB(bX[i], bY[i], bZ[i], &l, &bA[i], &bB[i]); + aLum[i] = aY[i] * luminance; + bLum[i] = bY[i] * luminance; + } + } + + la = lpyramid_create (aLum, w, h); + lb = lpyramid_create (bLum, w, h); + + num_one_degree_pixels = (float) (2 * tan(field_of_view * 0.5 * M_PI / 180) * 180 / M_PI); + pixels_per_degree = w / num_one_degree_pixels; + + num_pixels = 1; + adaptation_level = 0; + for (i = 0; i < MAX_PYR_LEVELS; i++) { + adaptation_level = i; + if (num_pixels > num_one_degree_pixels) break; + num_pixels *= 2; + } + + cpd[0] = 0.5f * pixels_per_degree; + for (i = 1; i < MAX_PYR_LEVELS; i++) cpd[i] = 0.5f * cpd[i - 1]; + csf_max = csf(3.248f, 100.0f); + + for (i = 0; i < MAX_PYR_LEVELS - 2; i++) F_freq[i] = csf_max / csf( cpd[i], 100.0f); + + pixels_failed = 0; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + int index = x + y * w; + float contrast[MAX_PYR_LEVELS - 2]; + float F_mask[MAX_PYR_LEVELS - 2]; + float factor; + float delta; + bool pass; + float sum_contrast = 0; + for (i = 0; i < MAX_PYR_LEVELS - 2; i++) { + float n1 = fabsf(lpyramid_get_value (la,x,y,i) - lpyramid_get_value (la,x,y,i + 1)); + float n2 = fabsf(lpyramid_get_value (lb,x,y,i) - lpyramid_get_value (lb,x,y,i + 1)); + float numerator = (n1 > n2) ? n1 : n2; + float d1 = fabsf(lpyramid_get_value(la,x,y,i+2)); + float d2 = fabsf(lpyramid_get_value(lb,x,y,i+2)); + float denominator = (d1 > d2) ? d1 : d2; + if (denominator < 1e-5f) denominator = 1e-5f; + contrast[i] = numerator / denominator; + sum_contrast += contrast[i]; + } + if (sum_contrast < 1e-5) sum_contrast = 1e-5f; + float adapt = lpyramid_get_value(la,x,y,adaptation_level) + lpyramid_get_value(lb,x,y,adaptation_level); + adapt *= 0.5f; + if (adapt < 1e-5) adapt = 1e-5f; + for (i = 0; i < MAX_PYR_LEVELS - 2; i++) { + F_mask[i] = mask(contrast[i] * csf(cpd[i], adapt)); + } + factor = 0; + for (i = 0; i < MAX_PYR_LEVELS - 2; i++) { + factor += contrast[i] * F_freq[i] * F_mask[i] / sum_contrast; + } + if (factor < 1) factor = 1; + if (factor > 10) factor = 10; + delta = fabsf(lpyramid_get_value(la,x,y,0) - lpyramid_get_value(lb,x,y,0)); + pass = true; + /* pure luminance test */ + if (delta > factor * tvi(adapt)) { + pass = false; + } else { + /* CIE delta E test with modifications */ + float color_scale = 1.0f; + float da = aA[index] - bA[index]; + float db = aB[index] - bB[index]; + float delta_e; + /* ramp down the color test in scotopic regions */ + if (adapt < 10.0f) { + color_scale = 1.0f - (10.0f - color_scale) / 10.0f; + color_scale = color_scale * color_scale; + } + da = da * da; + db = db * db; + delta_e = (da + db) * color_scale; + if (delta_e > factor) { + pass = false; + } + } + if (!pass) + pixels_failed++; + } + } + + free (aX); + free (aY); + free (aZ); + free (bX); + free (bY); + free (bZ); + free (aLum); + free (bLum); + lpyramid_destroy (la); + lpyramid_destroy (lb); + free (aA); + free (bA); + free (aB); + free (bB); + + return pixels_failed; +} diff --git a/test/pdiff/pdiff.cpp b/test/pdiff/pdiff.cpp deleted file mode 100644 index 1609cdad..00000000 --- a/test/pdiff/pdiff.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* - Metric - Copyright (C) 2006 Yangli Hector Yee - - This program is free software; you can redistribute it and/or modify it under the terms of the - GNU General Public License as published by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "lpyramid.h" -#include -#include -#include "pdiff.h" - -#ifndef M_PI -#define M_PI 3.14159265f -#endif - -/* - * Given the adaptation luminance, this function returns the - * threshold of visibility in cd per m^2 - * TVI means Threshold vs Intensity function - * This version comes from Ward Larson Siggraph 1997 - */ -static float -tvi (float adaptation_luminance) -{ - /* returns the threshold luminance given the adaptation luminance - units are candelas per meter squared - */ - float log_a, r, result; - log_a = log10f(adaptation_luminance); - - if (log_a < -3.94f) { - r = -2.86f; - } else if (log_a < -1.44f) { - r = powf(0.405f * log_a + 1.6f , 2.18f) - 2.86f; - } else if (log_a < -0.0184f) { - r = log_a - 0.395f; - } else if (log_a < 1.9f) { - r = powf(0.249f * log_a + 0.65f, 2.7f) - 0.72f; - } else { - r = log_a - 1.255f; - } - - result = powf(10.0f , r); - - return result; -} - -/* computes the contrast sensitivity function (Barten SPIE 1989) - * given the cycles per degree (cpd) and luminance (lum) - */ -static float -csf (float cpd, float lum) -{ - float a, b, result; - - a = 440.0f * powf((1.0f + 0.7f / lum), -0.2f); - b = 0.3f * powf((1.0f + 100.0f / lum), 0.15f); - - result = a * cpd * expf(-b * cpd) * sqrtf(1.0f + 0.06f * expf(b * cpd)); - - return result; -} - -/* - * Visual Masking Function - * from Daly 1993 - */ -static float -mask (float contrast) -{ - float a, b, result; - a = powf(392.498f * contrast, 0.7f); - b = powf(0.0153f * a, 4.0f); - result = powf(1.0f + b, 0.25f); - - return result; -} - -/* convert Adobe RGB (1998) with reference white D65 to XYZ */ -static void -AdobeRGBToXYZ (float r, float g, float b, float *x, float *y, float *z) -{ - /* matrix is from http://www.brucelindbloom.com/ */ - *x = r * 0.576700f + g * 0.185556f + b * 0.188212f; - *y = r * 0.297361f + g * 0.627355f + b * 0.0752847f; - *z = r * 0.0270328f + g * 0.0706879f + b * 0.991248f; -} - -static void -XYZToLAB (float x, float y, float z, float *L, float *A, float *B) -{ - static float xw = -1; - static float yw; - static float zw; - const float epsilon = 216.0f / 24389.0f; - const float kappa = 24389.0f / 27.0f; - float f[3]; - float r[3]; - int i; - - /* reference white */ - if (xw < 0) { - AdobeRGBToXYZ(1, 1, 1, &xw, &yw, &zw); - } - r[0] = x / xw; - r[1] = y / yw; - r[2] = z / zw; - for (i = 0; i < 3; i++) { - if (r[i] > epsilon) { - f[i] = powf(r[i], 1.0f / 3.0f); - } else { - f[i] = (kappa * r[i] + 16.0f) / 116.0f; - } - } - *L = 116.0f * f[1] - 16.0f; - *A = 500.0f * (f[0] - f[1]); - *B = 200.0f * (f[1] - f[2]); -} - -static uint32_t -_get_pixel (cairo_surface_t *surface, int i) -{ - uint32_t *data; - - data = (uint32_t *) cairo_image_surface_get_data (surface); - return data[i]; -} - -static unsigned char -_get_red (cairo_surface_t *surface, int i) -{ - uint32_t pixel; - uint8_t alpha; - - pixel = _get_pixel (surface, i); - - alpha = (pixel & 0xff000000) >> 24; - - if (alpha == 0) - return 0; - else - return (((pixel & 0x00ff0000) >> 16) * 255 + alpha / 2) / alpha; -} - -static unsigned char -_get_green (cairo_surface_t *surface, int i) -{ - uint32_t pixel; - uint8_t alpha; - - pixel = _get_pixel (surface, i); - - alpha = (pixel & 0xff000000) >> 24; - - if (alpha == 0) - return 0; - else - return (((pixel & 0x0000ff00) >> 8) * 255 + alpha / 2) / alpha; -} - -static unsigned char -_get_blue (cairo_surface_t *surface, int i) -{ - uint32_t pixel; - uint8_t alpha; - - pixel = _get_pixel (surface, i); - - alpha = (pixel & 0xff000000) >> 24; - - if (alpha == 0) - return 0; - else - return (((pixel & 0x000000ff) >> 0) * 255 + alpha / 2) / alpha; -} - -int -pdiff_compare (cairo_surface_t *surface_a, - cairo_surface_t *surface_b, - double gamma, - double luminance, - double field_of_view) -{ - unsigned int dim = (cairo_image_surface_get_width (surface_a) - * cairo_image_surface_get_height (surface_a)); - unsigned int i; - - /* assuming colorspaces are in Adobe RGB (1998) convert to XYZ */ - float *aX = new float[dim]; - float *aY = new float[dim]; - float *aZ = new float[dim]; - float *bX = new float[dim]; - float *bY = new float[dim]; - float *bZ = new float[dim]; - float *aLum = new float[dim]; - float *bLum = new float[dim]; - - float *aA = new float[dim]; - float *bA = new float[dim]; - float *aB = new float[dim]; - float *bB = new float[dim]; - - unsigned int x, y, w, h; - - lpyramid_t *la, *lb; - - float num_one_degree_pixels, pixels_per_degree, num_pixels; - unsigned int adaptation_level; - - float cpd[MAX_PYR_LEVELS]; - float F_freq[MAX_PYR_LEVELS - 2]; - float csf_max; - - unsigned int pixels_failed; - - w = cairo_image_surface_get_width (surface_a); - h = cairo_image_surface_get_height (surface_a); - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - float r, g, b, l; - i = x + y * w; - r = powf(_get_red (surface_a, i) / 255.0f, gamma); - g = powf(_get_green (surface_a, i) / 255.0f, gamma); - b = powf(_get_blue (surface_a, i) / 255.0f, gamma); - - AdobeRGBToXYZ(r,g,b,&aX[i],&aY[i],&aZ[i]); - XYZToLAB(aX[i], aY[i], aZ[i], &l, &aA[i], &aB[i]); - r = powf(_get_red (surface_b, i) / 255.0f, gamma); - g = powf(_get_green (surface_b, i) / 255.0f, gamma); - b = powf(_get_blue (surface_b, i) / 255.0f, gamma); - - AdobeRGBToXYZ(r,g,b,&bX[i],&bY[i],&bZ[i]); - XYZToLAB(bX[i], bY[i], bZ[i], &l, &bA[i], &bB[i]); - aLum[i] = aY[i] * luminance; - bLum[i] = bY[i] * luminance; - } - } - - la = lpyramid_create (aLum, w, h); - lb = lpyramid_create (bLum, w, h); - - num_one_degree_pixels = (float) (2 * tan(field_of_view * 0.5 * M_PI / 180) * 180 / M_PI); - pixels_per_degree = w / num_one_degree_pixels; - - num_pixels = 1; - adaptation_level = 0; - for (i = 0; i < MAX_PYR_LEVELS; i++) { - adaptation_level = i; - if (num_pixels > num_one_degree_pixels) break; - num_pixels *= 2; - } - - cpd[0] = 0.5f * pixels_per_degree; - for (i = 1; i < MAX_PYR_LEVELS; i++) cpd[i] = 0.5f * cpd[i - 1]; - csf_max = csf(3.248f, 100.0f); - - for (i = 0; i < MAX_PYR_LEVELS - 2; i++) F_freq[i] = csf_max / csf( cpd[i], 100.0f); - - pixels_failed = 0; - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - int index = x + y * w; - float contrast[MAX_PYR_LEVELS - 2]; - float F_mask[MAX_PYR_LEVELS - 2]; - float factor; - float delta; - bool pass; - float sum_contrast = 0; - for (i = 0; i < MAX_PYR_LEVELS - 2; i++) { - float n1 = fabsf(lpyramid_get_value (la,x,y,i) - lpyramid_get_value (la,x,y,i + 1)); - float n2 = fabsf(lpyramid_get_value (lb,x,y,i) - lpyramid_get_value (lb,x,y,i + 1)); - float numerator = (n1 > n2) ? n1 : n2; - float d1 = fabsf(lpyramid_get_value(la,x,y,i+2)); - float d2 = fabsf(lpyramid_get_value(lb,x,y,i+2)); - float denominator = (d1 > d2) ? d1 : d2; - if (denominator < 1e-5f) denominator = 1e-5f; - contrast[i] = numerator / denominator; - sum_contrast += contrast[i]; - } - if (sum_contrast < 1e-5) sum_contrast = 1e-5f; - float adapt = lpyramid_get_value(la,x,y,adaptation_level) + lpyramid_get_value(lb,x,y,adaptation_level); - adapt *= 0.5f; - if (adapt < 1e-5) adapt = 1e-5f; - for (i = 0; i < MAX_PYR_LEVELS - 2; i++) { - F_mask[i] = mask(contrast[i] * csf(cpd[i], adapt)); - } - factor = 0; - for (i = 0; i < MAX_PYR_LEVELS - 2; i++) { - factor += contrast[i] * F_freq[i] * F_mask[i] / sum_contrast; - } - if (factor < 1) factor = 1; - if (factor > 10) factor = 10; - delta = fabsf(lpyramid_get_value(la,x,y,0) - lpyramid_get_value(lb,x,y,0)); - pass = true; - /* pure luminance test */ - if (delta > factor * tvi(adapt)) { - pass = false; - } else { - /* CIE delta E test with modifications */ - float color_scale = 1.0f; - float da = aA[index] - bA[index]; - float db = aB[index] - bB[index]; - float delta_e; - /* ramp down the color test in scotopic regions */ - if (adapt < 10.0f) { - color_scale = 1.0f - (10.0f - color_scale) / 10.0f; - color_scale = color_scale * color_scale; - } - da = da * da; - db = db * db; - delta_e = (da + db) * color_scale; - if (delta_e > factor) { - pass = false; - } - } - if (!pass) - pixels_failed++; - } - } - - if (aX) delete[] aX; - if (aY) delete[] aY; - if (aZ) delete[] aZ; - if (bX) delete[] bX; - if (bY) delete[] bY; - if (bZ) delete[] bZ; - if (aLum) delete[] aLum; - if (bLum) delete[] bLum; - lpyramid_destroy (la); - lpyramid_destroy (lb); - if (aA) delete aA; - if (bA) delete bA; - if (aB) delete aB; - if (bB) delete bB; - - return pixels_failed; -} diff --git a/test/pdiff/pdiff.h b/test/pdiff/pdiff.h index d20ae86d..b398cc6f 100644 --- a/test/pdiff/pdiff.h +++ b/test/pdiff/pdiff.h @@ -17,12 +17,16 @@ #ifndef _PDIFF_H #define _PDIFF_H -#ifdef __cplusplus -extern "C" { -#endif - #include +typedef int bool; +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif + /* Image comparison metric using Yee's method (and a cairo interface) * References: A Perceptual Metric for Production Testing, Hector Yee, Journal of Graphics Tools 2004 */ @@ -33,8 +37,4 @@ pdiff_compare (cairo_surface_t *surface_a, double luminance, double field_of_view); -#ifdef __cplusplus -} -#endif - #endif diff --git a/test/pdiff/perceptualdiff.c b/test/pdiff/perceptualdiff.c new file mode 100644 index 00000000..953558e0 --- /dev/null +++ b/test/pdiff/perceptualdiff.c @@ -0,0 +1,101 @@ +/* + PerceptualDiff - a program that compares two images using a perceptual metric + based on the paper : + A perceptual metric for production testing. Journal of graphics tools, 9(4):33-40, 2004, Hector Yee + Copyright (C) 2006 Yangli Hector Yee + + This program is free software; you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include "lpyramid.h" +#include "args.h" +#include "pdiff.h" + +static bool Yee_Compare(args_t *args) +{ + int width_a, height_a, stride_a; + unsigned char *data_a, *row_a; + uint32_t *pixel_a; + int width_b, height_b, stride_b; + unsigned char *data_b, *row_b; + uint32_t *pixel_b; + unsigned int x, y, dim, pixels_failed; + bool identical = true; + + width_a = cairo_image_surface_get_width (args->surface_a); + height_a = cairo_image_surface_get_height (args->surface_a); + stride_a = cairo_image_surface_get_stride (args->surface_a); + data_a = cairo_image_surface_get_data (args->surface_a); + + width_b = cairo_image_surface_get_width (args->surface_b); + height_b = cairo_image_surface_get_height (args->surface_b); + stride_b = cairo_image_surface_get_stride (args->surface_b); + data_b = cairo_image_surface_get_data (args->surface_b); + + if ((width_a != width_b) || (height_a != height_b)) { + printf ("FAIL: Image dimensions do not match\n"); + return false; + } + + identical = true; + + for (y = 0; y < height_a; y++) { + row_a = data_a + y * stride_a; + row_b = data_b + y * stride_b; + pixel_a = (uint32_t *) row_a; + pixel_b = (uint32_t *) row_b; + for (x = 0; x < width_a; x++) { + if (*pixel_a != *pixel_b) { + identical = false; + } + pixel_a++; + pixel_b++; + } + } + if (identical) { + printf ("PASS: Images are binary identical\n"); + return true; + } + + pixels_failed = pdiff_compare (args->surface_a, args->surface_b, + args->Gamma, args->Luminance, + args->FieldOfView); + + if (pixels_failed < args->ThresholdPixels) { + printf ("PASS: Images are perceptually indistinguishable\n"); + return true; + } + + printf("FAIL: Images are visibly different\n" + "%d pixels are different\n", pixels_failed); + + return false; +} + +int main(int argc, char **argv) +{ + args_t args; + + args_init (&args); + + if (!args_parse (&args, argc, argv)) { + return -1; + } else { + if (args.Verbose) + args_print (&args); + } + return ! Yee_Compare(&args); +} -- cgit v1.2.3