1 /*
2 % Copyright (C) 2003 - 2018 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 % Copyright 1991-1999 E. I. du Pont de Nemours and Company
5 %
6 % This program is covered by multiple licenses, which are described in
7 % Copyright.txt. You should have received a copy of Copyright.txt with this
8 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9 %
10 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11 %                                                                             %
12 %                                                                             %
13 %                                                                             %
14 %               CCCC   OOO   M   M  PPPP    AAA   RRRR    EEEEE               %
15 %              C      O   O  MM MM  P   P  A   A  R   R   E                   %
16 %              C      O   O  M M M  PPPP   AAAAA  RRRR    EEE                 %
17 %              C      O   O  M   M  P      A   A  R R     E                   %
18 %               CCCC   OOO   M   M  P      A   A  R  R    EEEEE               %
19 %                                                                             %
20 %                                                                             %
21 %                    GraphicsMagick Image Compare Methods                     %
22 %                                                                             %
23 %                                                                             %
24 %                                                                             %
25 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26 %
27 %
28 %
29 */
30 
31 /*
32   Include declarations.
33 */
34 #include "magick/studio.h"
35 #include "magick/alpha_composite.h"
36 #include "magick/color.h"
37 #include "magick/color_lookup.h"
38 #include "magick/compare.h"
39 #include "magick/enum_strings.h"
40 #include "magick/pixel_iterator.h"
41 #include "magick/utility.h"
42 
43 /*
44 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45 %                                                                             %
46 %                                                                             %
47 %                                                                             %
48 %   D i f f e r e n c e I m a g e                                             %
49 %                                                                             %
50 %                                                                             %
51 %                                                                             %
52 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53 %
54 %  DifferenceImage() returns an annotated difference image based on the
55 %  the difference between a reference image and a compare image.
56 %
57 %  The format of the DifferenceImage method is:
58 %
59 %      Image *DifferenceImage(const Image *reference_image,
60 %                             const Image *compare_image,
61 %                             const DifferenceImageOptions *difference_options,
62 %                             ExceptionInfo *exception)
63 %
64 %  A description of each parameter follows:
65 %
66 %    o reference_image: the reference image.
67 %
68 %    o compare_image: the comparison image.
69 %
70 %    o difference_options: options to use when differencing.
71 %
72 %    o channel: the channel(s) to compare.
73 %
74 %    o exception: Return any errors or warnings in this structure.
75 %
76 */
77 static MagickPassFail
DifferenceImagePixels(void * mutable_data,const void * immutable_data,const Image * restrict reference_image,const PixelPacket * restrict reference_pixels,const IndexPacket * restrict reference_indexes,const Image * restrict compare_image,const PixelPacket * restrict compare_pixels,const IndexPacket * restrict compare_indexes,Image * restrict result_image,PixelPacket * restrict result_pixels,IndexPacket * restrict result_indexes,const long npixels,ExceptionInfo * exception)78 DifferenceImagePixels(void *mutable_data,                  /* User provided mutable data */
79                       const void *immutable_data,          /* User provided immutable data */
80                       const Image * restrict reference_image,        /* Source 1 image */
81                       const PixelPacket * restrict reference_pixels, /* Pixel row in source 1 image */
82                       const IndexPacket * restrict reference_indexes,/* Pixel row indexes in source 1 image */
83                       const Image * restrict compare_image,          /* Source 2 image */
84                       const PixelPacket * restrict compare_pixels, /* Pixel row in source 2 image */
85                       const IndexPacket * restrict compare_indexes, /* Pixel row indexes in source 2 image */
86                       Image * restrict result_image,                 /* Update image */
87                       PixelPacket * restrict result_pixels, /* Pixel row in update image */
88                       IndexPacket * restrict result_indexes, /* Pixel row indexes in update image */
89                       const long npixels,                  /* Number of pixels in row */
90                       ExceptionInfo *exception             /* Exception report */
91                    )
92 {
93   const DifferenceImageOptions
94     *difference_options = (const DifferenceImageOptions *) immutable_data;
95 
96   register ChannelType
97     channels = difference_options->channel;
98 
99   register long
100     i;
101 
102   register MagickBool
103     change;
104 
105   ARG_NOT_USED(mutable_data);
106   ARG_NOT_USED(compare_image);
107   ARG_NOT_USED(result_image);
108   ARG_NOT_USED(result_indexes);
109   ARG_NOT_USED(exception);
110 
111   for (i=0; i < npixels; i++)
112     {
113       change=MagickFalse;
114 
115       if (IsCMYKColorspace(reference_image->colorspace))
116         {
117           if (MagickChannelEnabled(channels,CyanChannel) &&
118               (GetCyanSample(&reference_pixels[i]) != GetCyanSample(&compare_pixels[i])))
119             change=MagickTrue;
120           if (MagickChannelEnabled(channels,MagentaChannel) &&
121               (GetMagentaSample(&reference_pixels[i]) != GetMagentaSample(&compare_pixels[i])))
122             change=MagickTrue;
123           if (MagickChannelEnabled(channels,YellowChannel) &&
124               (GetYellowSample(&reference_pixels[i]) != GetYellowSample(&compare_pixels[i])))
125             change=MagickTrue;
126           if (MagickChannelEnabled(channels,BlackChannel) &&
127               (GetBlackSample(&reference_pixels[i]) != GetBlackSample(&compare_pixels[i])))
128             change=MagickTrue;
129           if (MagickChannelEnabled(channels,OpacityChannel) &&
130               (reference_indexes[i] != compare_indexes[i]))
131             change=MagickTrue;
132         }
133       else
134         {
135           if (MagickChannelEnabled(channels,RedChannel) &&
136               (GetRedSample(&reference_pixels[i]) != GetRedSample(&compare_pixels[i])))
137             change=MagickTrue;
138           if (MagickChannelEnabled(channels,GreenChannel) &&
139               (GetGreenSample(&reference_pixels[i]) != GetGreenSample(&compare_pixels[i])))
140             change=MagickTrue;
141           if (MagickChannelEnabled(channels,BlueChannel) &&
142               (GetBlueSample(&reference_pixels[i]) != GetBlueSample(&compare_pixels[i])))
143             change=MagickTrue;
144           if (MagickChannelEnabled(channels,OpacityChannel) &&
145               (GetOpacitySample(&reference_pixels[i]) != GetOpacitySample(&compare_pixels[i])))
146             change=MagickTrue;
147         }
148       /*
149         Modify result image to reflect change.
150       */
151       switch (difference_options->highlight_style)
152         {
153         case UndefinedHighlightStyle:
154           break;
155         case AssignHighlightStyle:
156           {
157             /*
158               Changed pixels are assigned the highlight color.
159             */
160             if (change)
161               result_pixels[i]=difference_options->highlight_color;
162             else
163               result_pixels[i]=compare_pixels[i];
164             break;
165           }
166         case ThresholdHighlightStyle:
167           {
168             /*
169               For changed pixels, compare the pixel intensity.  If the
170               pixel intensity in the compare image is higher than the
171               reference image, then set the pixel to white, otherwise
172               set it to black.
173             */
174             if (change)
175               {
176                 Quantum
177                   compare_intensity,
178                   intensity,
179                   reference_intensity;
180 
181                 compare_intensity=PixelIntensity(&compare_pixels[i]);
182                 reference_intensity=PixelIntensity(&reference_pixels[i]);
183                 if (compare_intensity > reference_intensity)
184                   intensity=MaxRGB;
185                 else
186                   intensity=0U;
187                 result_pixels[i].red = result_pixels[i].green = result_pixels[i].blue = intensity;
188                 result_pixels[i].opacity=compare_pixels[i].opacity;
189               }
190             else
191               {
192                 result_pixels[i]=compare_pixels[i];
193               }
194             break;
195           }
196         case TintHighlightStyle:
197           {
198             /*
199               Alpha composite highlight color on top of change pixels.
200             */
201             if (change)
202               AlphaCompositePixel(&result_pixels[i],&difference_options->highlight_color,0.75*MaxRGBDouble,
203                                   &compare_pixels[i],compare_pixels[i].opacity);
204             else
205               result_pixels[i]=compare_pixels[i];
206             break;
207           }
208         case XorHighlightStyle:
209           {
210             if (change)
211               {
212                 result_pixels[i].red = compare_pixels[i].red ^ difference_options->highlight_color.red;
213                 result_pixels[i].green = compare_pixels[i].green ^ difference_options->highlight_color.green;
214                 result_pixels[i].blue = compare_pixels[i].blue ^ difference_options->highlight_color.blue;
215                 result_pixels[i].opacity = compare_pixels[i].opacity ^ difference_options->highlight_color.opacity;
216               }
217             else
218               {
219                 result_pixels[i]=compare_pixels[i];
220               }
221             break;
222           }
223         }
224     }
225 
226   return MagickPass;
227 }
228 
229 MagickExport Image *
DifferenceImage(const Image * reference_image,const Image * compare_image,const DifferenceImageOptions * difference_options,ExceptionInfo * exception)230 DifferenceImage(const Image *reference_image,const Image *compare_image,
231                 const DifferenceImageOptions *difference_options,
232                 ExceptionInfo *exception)
233 {
234   Image
235     *difference_image;
236 
237   assert(reference_image != (const Image *) NULL);
238   assert(reference_image->signature == MagickSignature);
239   assert(compare_image != (const Image *) NULL);
240   assert(compare_image->signature == MagickSignature);
241   assert(difference_options != (const DifferenceImageOptions *) NULL);
242   assert(exception != (ExceptionInfo *) NULL);
243 
244   difference_image=AllocateImage((ImageInfo *) NULL);
245   if (difference_image == (Image *) NULL)
246     {
247       ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
248                            UnableToAllocateImage);
249       return ((Image *) NULL);
250     }
251   difference_image->storage_class = DirectClass;
252   difference_image->rows = reference_image->rows;
253   difference_image->columns = reference_image->columns;
254   difference_image->depth = Max(reference_image->depth, compare_image->depth);
255 
256   /*
257     Update "difference" image to mark changes.
258   */
259   (void) PixelIterateTripleModify(DifferenceImagePixels,
260                                   NULL,
261                                   "[%s]*[%s]->[%s] Difference image pixels ...",
262                                   NULL,difference_options,
263                                   reference_image->columns,reference_image->rows,
264                                   reference_image, compare_image,0, 0,
265                                   difference_image, 0, 0,
266                                   exception);
267   return difference_image;
268 }
269 
270 /*
271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
272 %                                                                             %
273 %                                                                             %
274 %                                                                             %
275 %   G e t I m a g e C h a n n e l D i f f e r e n c e                         %
276 %                                                                             %
277 %                                                                             %
278 %                                                                             %
279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280 %
281 %  GetImageChannelDifference() updates a user provided statistics structure
282 %  with per-channel, and totalized, difference statistics corresponding
283 %  to a specified comparison metric.
284 %
285 %  The format of the GetImageChannelDifference method is:
286 %
287 %      MagickPassFail GetImageChannelDifference(const Image *reference_image,
288 %                                           const Image *compare_image,
289 %                                           const MetricType metric,
290 %                                           DifferenceStatistics *statistics,
291 %                                           ExceptionInfo *exception)
292 %
293 %  A description of each parameter follows:
294 %
295 %    o reference_image: the reference image.
296 %
297 %    o compare_image: the comparison image.
298 %
299 %    o metric: metric to use when differencing.
300 %
301 %    o statistics: the statistics structure to populate.
302 %
303 %    o exception: Return any errors or warnings in this structure.
304 %
305 */
306 /*
307   Compute the total absolute value difference.
308 
309   In this case we sum the absolute value difference between channel
310   pixel quantums.
311 */
312 static MagickPassFail
ComputeAbsoluteError(void * mutable_data,const void * immutable_data,const Image * restrict first_image,const PixelPacket * restrict first_pixels,const IndexPacket * restrict first_indexes,const Image * restrict second_image,const PixelPacket * restrict second_pixels,const IndexPacket * restrict second_indexes,const long npixels,ExceptionInfo * exception)313 ComputeAbsoluteError(void *mutable_data,
314                      const void *immutable_data,
315                      const Image * restrict first_image,
316                      const PixelPacket * restrict first_pixels,
317                      const IndexPacket * restrict first_indexes,
318                      const Image * restrict second_image,
319                      const PixelPacket * restrict second_pixels,
320                      const IndexPacket * restrict second_indexes,
321                      const long npixels,
322                      ExceptionInfo *exception)
323 {
324   DifferenceStatistics
325     lstats,
326     *stats = (DifferenceStatistics *) mutable_data;
327 
328   register long
329     i;
330 
331   ARG_NOT_USED(immutable_data);
332   ARG_NOT_USED(first_image);
333   ARG_NOT_USED(first_indexes);
334   ARG_NOT_USED(second_image);
335   ARG_NOT_USED(second_indexes);
336   ARG_NOT_USED(exception);
337 
338   InitializeDifferenceStatistics(&lstats,exception);
339   for (i=0; i < npixels; i++)
340     {
341       lstats.red += fabs(first_pixels[i].red-(double) second_pixels[i].red)/MaxRGBDouble;
342       lstats.green += fabs(first_pixels[i].green-(double) second_pixels[i].green)/MaxRGBDouble;
343       lstats.blue += fabs(first_pixels[i].blue-(double) second_pixels[i].blue)/MaxRGBDouble;
344       lstats.opacity += fabs(first_pixels[i].opacity-(double) second_pixels[i].opacity)/MaxRGBDouble;
345     }
346 
347 #if defined(HAVE_OPENMP)
348 #  pragma omp critical (GM_ComputeAbsoluteError)
349 #endif
350   {
351     stats->red += lstats.red;
352     stats->green += lstats.green;
353     stats->blue += lstats.blue;
354     stats->opacity += lstats.opacity;
355   }
356 
357   return (MagickPass);
358 }
359 
360 /*
361   Compute the peak absolute difference.
362 
363   In this case we compute the simple difference between channel pixel
364   quantums, obtain the absolute value, and store the value if it is
365   greater than the current peak value.
366 */
367 static MagickPassFail
ComputePeakAbsoluteError(void * mutable_data,const void * immutable_data,const Image * restrict first_image,const PixelPacket * restrict first_pixels,const IndexPacket * restrict first_indexes,const Image * restrict second_image,const PixelPacket * restrict second_pixels,const IndexPacket * restrict second_indexes,const long npixels,ExceptionInfo * exception)368 ComputePeakAbsoluteError(void *mutable_data,
369                          const void *immutable_data,
370                          const Image * restrict first_image,
371                          const PixelPacket * restrict first_pixels,
372                          const IndexPacket * restrict first_indexes,
373                          const Image * restrict second_image,
374                          const PixelPacket * restrict second_pixels,
375                          const IndexPacket * restrict second_indexes,
376                          const long npixels,
377                          ExceptionInfo *exception)
378 {
379   DifferenceStatistics
380     lstats,
381     *stats = (DifferenceStatistics *) mutable_data;
382 
383   double
384     difference;
385 
386   register long
387     i;
388 
389   ARG_NOT_USED(immutable_data);
390   ARG_NOT_USED(first_image);
391   ARG_NOT_USED(first_indexes);
392   ARG_NOT_USED(second_image);
393   ARG_NOT_USED(second_indexes);
394 
395   InitializeDifferenceStatistics(&lstats,exception);
396   for (i=0; i < npixels; i++)
397     {
398       difference=fabs(first_pixels[i].red-(double) second_pixels[i].red)/MaxRGBDouble;
399       if (difference > lstats.red)
400         lstats.red=difference;
401 
402       difference=fabs(first_pixels[i].green-(double) second_pixels[i].green)/MaxRGBDouble;
403       if (difference > lstats.green)
404         lstats.green=difference;
405 
406       difference=fabs(first_pixels[i].blue-(double) second_pixels[i].blue)/MaxRGBDouble;
407       if (difference > lstats.blue)
408         lstats.blue=difference;
409 
410       difference=fabs(first_pixels[i].opacity-(double) second_pixels[i].opacity)/MaxRGBDouble;
411       if (difference > lstats.opacity)
412         lstats.opacity=difference;
413     }
414 
415 #if defined(HAVE_OPENMP)
416 #  pragma omp critical (GM_ComputePeakAbsoluteError)
417 #endif
418   {
419     if (lstats.red > stats->red)
420       stats->red=lstats.red;
421     if (lstats.green > stats->green)
422       stats->green=lstats.green;
423     if (lstats.blue > stats->blue)
424       stats->blue=lstats.blue;
425     if (lstats.opacity > stats->opacity)
426       stats->opacity=lstats.opacity;
427   }
428 
429   return (MagickPass);
430 }
431 
432 /*
433   Compute the squared difference.
434 
435   In this case we sum the square of the difference between channel
436   pixel quantums.
437 */
438 static MagickPassFail
ComputeSquaredError(void * mutable_data,const void * immutable_data,const Image * restrict first_image,const PixelPacket * restrict first_pixels,const IndexPacket * restrict first_indexes,const Image * restrict second_image,const PixelPacket * restrict second_pixels,const IndexPacket * restrict second_indexes,const long npixels,ExceptionInfo * exception)439 ComputeSquaredError(void *mutable_data,
440                     const void *immutable_data,
441                     const Image * restrict first_image,
442                     const PixelPacket * restrict first_pixels,
443                     const IndexPacket * restrict first_indexes,
444                     const Image * restrict second_image,
445                     const PixelPacket * restrict second_pixels,
446                     const IndexPacket * restrict second_indexes,
447                     const long npixels,
448                     ExceptionInfo *exception)
449 {
450   DifferenceStatistics
451     lstats,
452     *stats = (DifferenceStatistics *) mutable_data;
453 
454   double
455     difference;
456 
457   register long
458     i;
459 
460   ARG_NOT_USED(immutable_data);
461   ARG_NOT_USED(first_image);
462   ARG_NOT_USED(first_indexes);
463   ARG_NOT_USED(second_image);
464   ARG_NOT_USED(second_indexes);
465   ARG_NOT_USED(exception);
466 
467   InitializeDifferenceStatistics(&lstats,exception);
468   for (i=0; i < npixels; i++)
469     {
470       difference=(first_pixels[i].red-(double) second_pixels[i].red)/MaxRGBDouble;
471       lstats.red += difference*difference;
472 
473       difference=(first_pixels[i].green-(double) second_pixels[i].green)/MaxRGBDouble;
474       lstats.green += difference*difference;
475 
476       difference=(first_pixels[i].blue-(double) second_pixels[i].blue)/MaxRGBDouble;
477       lstats.blue += difference*difference;
478 
479       difference=(first_pixels[i].opacity-(double) second_pixels[i].opacity)/MaxRGBDouble;
480       lstats.opacity += difference*difference;
481     }
482 
483 #if defined(HAVE_OPENMP)
484 #  pragma omp critical (GM_ComputeSquaredError)
485 #endif
486   {
487     stats->red += lstats.red;
488     stats->green += lstats.green;
489     stats->blue += lstats.blue;
490     stats->opacity += lstats.opacity;
491   }
492 
493   return (MagickPass);
494 }
495 MagickExport MagickPassFail
GetImageChannelDifference(const Image * reference_image,const Image * compare_image,const MetricType metric,DifferenceStatistics * statistics,ExceptionInfo * exception)496 GetImageChannelDifference(const Image *reference_image,
497                           const Image *compare_image,
498                           const MetricType metric,
499                           DifferenceStatistics *statistics,
500                           ExceptionInfo *exception)
501 {
502   PixelIteratorDualReadCallback
503     call_back = (PixelIteratorDualReadCallback) NULL;
504 
505   MagickPassFail
506     status = MagickFail;
507 
508   assert(reference_image != (const Image *) NULL);
509   assert(reference_image->signature == MagickSignature);
510   assert(compare_image != (const Image *) NULL);
511   assert(compare_image->signature == MagickSignature);
512   assert(statistics != (DifferenceStatistics *) NULL);
513   assert(exception != (ExceptionInfo *) NULL);
514 
515   InitializeDifferenceStatistics(statistics,exception);
516 
517   /*
518     Select basic differencing function to use.
519   */
520   switch (metric)
521     {
522     case UndefinedMetric:
523       break;
524     case MeanAbsoluteErrorMetric:
525       call_back=ComputeAbsoluteError;
526       break;
527     case MeanSquaredErrorMetric:
528       call_back=ComputeSquaredError;
529       break;
530     case PeakAbsoluteErrorMetric:
531       call_back=ComputePeakAbsoluteError;
532       break;
533     case PeakSignalToNoiseRatioMetric:
534       call_back=ComputeSquaredError;
535       break;
536     case RootMeanSquaredErrorMetric:
537       call_back=ComputeSquaredError;
538       break;
539     }
540 
541   if (call_back != (PixelIteratorDualReadCallback) NULL)
542     {
543       double
544         number_channels,
545         number_pixels;
546 
547       char
548         description[MaxTextExtent];
549 
550       FormatString(description,"[%%s]*[%%s] Compute image difference using %s metric...",
551                    MetricTypeToString(metric));
552 
553       status=PixelIterateDualRead(call_back,
554                                   NULL,
555                                   description,
556                                   statistics, NULL,
557                                   reference_image->columns,reference_image->rows,
558                                   reference_image,0,0,
559                                   compare_image,0,0,
560                                   exception);
561       /*
562         Post-process statistics (as required)
563       */
564 
565       number_channels=3.0 + (reference_image->matte ? 1.0 : 0.0);
566       number_pixels=(double) reference_image->columns*reference_image->rows;
567 
568       if ((MeanAbsoluteErrorMetric == metric) ||
569           (MeanSquaredErrorMetric == metric) ||
570           (PeakSignalToNoiseRatioMetric == metric)||
571           (RootMeanSquaredErrorMetric == metric))
572         {
573           /*
574             Compute mean values.
575           */
576           statistics->combined=((statistics->red+statistics->green+
577                                  statistics->blue+
578                                  (reference_image->matte ? statistics->opacity : 0.0))/
579                                 (number_pixels*number_channels));
580           statistics->red /= number_pixels;
581           statistics->green /= number_pixels;
582           statistics->blue /= number_pixels;
583           statistics->opacity /= number_pixels;
584         }
585 
586       if (PeakAbsoluteErrorMetric == metric)
587         {
588           /*
589             Determine peak channel value
590           */
591           if (statistics->red > statistics->combined)
592             statistics->combined=statistics->red;
593 
594           if (statistics->green > statistics->combined)
595             statistics->combined=statistics->green;
596 
597           if (statistics->blue > statistics->combined)
598             statistics->combined=statistics->blue;
599 
600           if ((reference_image->matte) && (statistics->opacity > statistics->combined))
601             statistics->combined=statistics->opacity;
602         }
603 
604       if (PeakSignalToNoiseRatioMetric == metric)
605         {
606           /*
607             Compute PSNR.
608           */
609           statistics->red=(20.0 * log10(1.0/sqrt(statistics->red)));
610           statistics->green=(20.0 * log10(1.0/sqrt(statistics->green)));
611           statistics->blue=(20.0 * log10(1.0/sqrt(statistics->blue)));
612           statistics->opacity=(20.0 * log10(1.0/sqrt(statistics->opacity)));
613           statistics->combined=(20.0 * log10(1.0/sqrt(statistics->combined)));
614         }
615 
616       if (RootMeanSquaredErrorMetric == metric)
617         {
618           /*
619             Compute RMSE.
620           */
621           statistics->red=sqrt(statistics->red);
622           statistics->green=sqrt(statistics->green);
623           statistics->blue=sqrt(statistics->blue);
624           statistics->opacity=sqrt(statistics->opacity);
625           statistics->combined=sqrt(statistics->combined);
626         }
627     }
628 
629   return status;
630 }
631 
632 /*
633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
634 %                                                                             %
635 %                                                                             %
636 %                                                                             %
637 %   G e t I m a g e C h a n n e l D i s t o r t i o n                         %
638 %                                                                             %
639 %                                                                             %
640 %                                                                             %
641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
642 %
643 %  GetImageChannelDistortion() updates a distortion parameter with the
644 %  distortion (error) computed according to the specified comparison metric.
645 %  The value returned is only for the channel specified.
646 %
647 %  The format of the GetImageChannelDistortion method is:
648 %
649 %       MagickPassFail GetImageChannelDistortion(const Image *reference_image,
650 %                                                const Image *compare_image,
651 %                                                const ChannelType channel,
652 %                                                const MetricType metric,
653 %                                                double *distortion,
654 %                                                ExceptionInfo *exception)
655 %
656 %  A description of each parameter follows:
657 %
658 %    o reference_image: the reference image.
659 %
660 %    o compare_image: the comparison image.
661 %
662 %    o channel: the channel to obtain error data for.
663 %
664 %    o metric: metric to use when differencing.
665 %
666 %    o distortion: updated with the computed distortion.
667 %
668 %    o exception: Return any errors or warnings in this structure.
669 %
670 */
671 MagickExport MagickPassFail
GetImageChannelDistortion(const Image * reference_image,const Image * compare_image,const ChannelType channel,const MetricType metric,double * distortion,ExceptionInfo * exception)672 GetImageChannelDistortion(const Image *reference_image,
673                           const Image *compare_image,
674                           const ChannelType channel,
675                           const MetricType metric,
676                           double *distortion,
677                           ExceptionInfo *exception)
678 {
679   DifferenceStatistics
680     statistics;
681 
682   MagickPassFail
683     status;
684 
685   assert(distortion != (double *) NULL);
686 
687   *distortion=1.0;
688   status=GetImageChannelDifference(reference_image,compare_image,metric,
689                                    &statistics,exception);
690   switch (channel)
691     {
692     case RedChannel:
693     case CyanChannel:
694       *distortion=statistics.red;
695       break;
696     case GreenChannel:
697     case MagentaChannel:
698       *distortion=statistics.green;
699       break;
700     case BlueChannel:
701     case YellowChannel:
702       *distortion=statistics.blue;
703       break;
704     case BlackChannel:
705     case MatteChannel:
706     case OpacityChannel:
707       *distortion=statistics.opacity;
708       break;
709     case UndefinedChannel:
710     case AllChannels:
711     case GrayChannel:
712       *distortion=statistics.combined;
713       break;
714     }
715 
716     return status;
717 }
718 
719 /*
720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721 %                                                                             %
722 %                                                                             %
723 %                                                                             %
724 %   G e t I m a g e D i s t o r t i o n                                       %
725 %                                                                             %
726 %                                                                             %
727 %                                                                             %
728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
729 %
730 %  GetImageDistortion() updates a distortion parameter with the distortion
731 %  (error) computed according to the specified comparison metric.  The value
732 %  returned reflects all enabled channels.
733 %
734 %  The format of the GetImageDistortion method is:
735 %
736 %       MagickPassFail GetImageDistortion(const Image *reference_image,
737 %                                         const Image *compare_image,
738 %                                         const MetricType metric,
739 %                                         double *distortion,
740 %                                         ExceptionInfo *exception)
741 %
742 %  A description of each parameter follows:
743 %
744 %    o reference_image: the reference image.
745 %
746 %    o compare_image: the comparison image.
747 %
748 %    o channel: the channel to obtain error data for.
749 %
750 %    o metric: metric to use when differencing.
751 %
752 %    o distortion: updated with the computed distortion.
753 %
754 %    o exception: Return any errors or warnings in this structure.
755 %
756 */
757 MagickExport MagickPassFail
GetImageDistortion(const Image * reference_image,const Image * compare_image,const MetricType metric,double * distortion,ExceptionInfo * exception)758 GetImageDistortion(const Image *reference_image,
759                    const Image *compare_image,
760                    const MetricType metric,
761                    double *distortion,
762                    ExceptionInfo *exception)
763 {
764   return GetImageChannelDistortion(reference_image,compare_image,AllChannels,
765                                    metric,distortion,exception);
766 }
767 
768 /*
769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
770 %                                                                             %
771 %                                                                             %
772 %                                                                             %
773 %  I s I m a g e s E q u a l                                                  %
774 %                                                                             %
775 %                                                                             %
776 %                                                                             %
777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
778 %
779 %  IsImagesEqual() measures the difference between colors at each pixel
780 %  location of two images.  A value other than 0 means the colors match
781 %  exactly.  Otherwise an error measure is computed by summing over all
782 %  pixels in an image the distance squared in RGB space between each image
783 %  pixel and its corresponding pixel in the reference image.  The error
784 %  measure is assigned to these image members:
785 %
786 %    o mean_error_per_pixel:  The mean error for any single pixel in
787 %      the image.
788 %
789 %    o normalized_mean_error:  The normalized mean quantization error for
790 %      any single pixel in the image.  This distance measure is normalized to
791 %      a range between 0 and 1.  It is independent of the range of red, green,
792 %      and blue values in the image.
793 %
794 %    o normalized_maximum_error:  The normalized maximum quantization
795 %      error for any single pixel in the image.  This distance measure is
796 %      normalized to a range between 0 and 1.  It is independent of the range
797 %      of red, green, and blue values in your image.
798 %
799 %  A small normalized mean square error, accessed as
800 %  image->normalized_mean_error, suggests the images are very similiar in
801 %  spatial layout and color.
802 %
803 %  The format of the IsImagesEqual method is:
804 %
805 %      MagickBool IsImagesEqual(Image *image,const Image *reference)
806 %
807 %  A description of each parameter follows.
808 %
809 %    o image: The image.
810 %
811 %    o reference: The reference image.
812 %
813 */
814 typedef struct _ErrorStatistics {
815   double
816     maximum,
817     total;
818 } ErrorStatistics;
819 
820 static MagickPassFail
ComputePixelError(void * mutable_data,const void * immutable_data,const Image * restrict first_image,const PixelPacket * restrict first_pixels,const IndexPacket * restrict first_indexes,const Image * restrict second_image,const PixelPacket * restrict second_pixels,const IndexPacket * restrict second_indexes,const long npixels,ExceptionInfo * exception)821 ComputePixelError(void *mutable_data,
822                   const void *immutable_data,
823                   const Image * restrict first_image,
824                   const PixelPacket * restrict first_pixels,
825                   const IndexPacket * restrict first_indexes,
826                   const Image * restrict second_image,
827                   const PixelPacket * restrict second_pixels,
828                   const IndexPacket * restrict second_indexes,
829                   const long npixels,
830                   ExceptionInfo *exception)
831 {
832   ErrorStatistics
833     *stats = (ErrorStatistics *) mutable_data;
834 
835   double
836     difference,
837     distance,
838     distance_squared,
839     stats_maximum,
840     stats_total;
841 
842   register long
843     i;
844 
845   ARG_NOT_USED(immutable_data);
846   ARG_NOT_USED(first_indexes);
847   ARG_NOT_USED(second_image);
848   ARG_NOT_USED(second_indexes);
849   ARG_NOT_USED(exception);
850 
851   stats_maximum=0.0;
852   stats_total=0.0;
853 
854   for (i=0; i < npixels; i++)
855     {
856       difference=(first_pixels[i].red-(double) second_pixels[i].red)/MaxRGBDouble;
857       distance_squared=(difference*difference);
858 
859       difference=(first_pixels[i].green-(double) second_pixels[i].green)/MaxRGBDouble;
860       distance_squared+=(difference*difference);
861 
862       difference=(first_pixels[i].blue-(double) second_pixels[i].blue)/MaxRGBDouble;
863       distance_squared+=(difference*difference);
864 
865       if (first_image->matte)
866         {
867           difference=(first_pixels[i].opacity-(double) second_pixels[i].opacity)/MaxRGBDouble;
868           distance_squared+=(difference*difference);
869         }
870       distance=sqrt(distance_squared);
871 
872       stats_total+=distance;
873       if (distance > stats_maximum)
874         stats_maximum=distance;
875     }
876 
877 #if defined(HAVE_OPENMP)
878 #  pragma omp critical (GM_ComputePixelError)
879 #endif
880   {
881     stats->total+=stats_total;
882 
883     if (stats_maximum > stats->maximum)
884       stats->maximum=stats_maximum;
885   }
886   return (MagickPass);
887 }
888 
889 MagickExport MagickBool
IsImagesEqual(Image * image,const Image * reference)890 IsImagesEqual(Image *image,const Image *reference)
891 {
892   ErrorStatistics
893     stats;
894 
895   double
896     mean_error_per_pixel,
897     normalize,
898     number_pixels;
899 
900   /*
901     Initialize measurement.
902   */
903   assert(image != (const Image *) NULL);
904   assert(image->signature == MagickSignature);
905   assert(reference != (const Image *) NULL);
906   assert(reference->signature == MagickSignature);
907   (void) memset(&image->error,0,sizeof(ErrorInfo));
908   if ((image->rows != reference->rows) ||
909       (image->columns != reference->columns))
910     ThrowBinaryException3(ImageError,UnableToCompareImages,
911       ImageSizeDiffers);
912   if ((image->colorspace != reference->colorspace) &&
913       (!IsRGBColorspace(image->colorspace) || !IsRGBColorspace(reference->colorspace)))
914     ThrowBinaryException3(ImageError,UnableToCompareImages,
915       ImageColorspaceDiffers);
916   if(image->matte != reference->matte)
917     ThrowBinaryException3(ImageError,UnableToCompareImages,
918       ImageOpacityDiffers);
919 
920   /*
921     For each pixel, collect error statistics.
922   */
923   number_pixels=(double) image->columns*image->rows;
924 
925   stats.maximum=0.0;
926   stats.total=0.0;
927 
928   (void) PixelIterateDualRead(ComputePixelError,
929                               NULL,
930                               "[%s]*[%s] Compute pixel error ...",
931                               &stats, NULL,
932                               image->columns,image->rows,
933                               image,0,0,
934                               reference,0,0,
935                               &image->exception);
936 
937   /*
938     Compute final error statistics.
939   */
940 
941   if (image->matte)
942     normalize = sqrt(4.0); /* sqrt(1.0*1.0+1.0*1.0+1.0*1.0+1.0*1.0) */
943   else
944     normalize = sqrt(3.0); /* sqrt(1.0*1.0+1.0*1.0+1.0*1.0) */
945   mean_error_per_pixel=stats.total/number_pixels;
946   image->error.mean_error_per_pixel=mean_error_per_pixel*MaxRGBDouble;
947   image->error.normalized_mean_error=mean_error_per_pixel/normalize;
948   image->error.normalized_maximum_error=stats.maximum/normalize;
949   return(image->error.normalized_mean_error == 0.0);
950 }
951 
952 /*
953 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
954 %                                                                             %
955 %                                                                             %
956 %                                                                             %
957 %   I n i t i a l i z e D i f f e r e n c e I m a g e O p t i o n s           %
958 %                                                                             %
959 %                                                                             %
960 %                                                                             %
961 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
962 %
963 %  InitializeDifferenceImageOptions() assigns default options to a user-provided
964 %  DifferenceImageOptions structure.  This function should always be used
965 %  to initialize the DifferenceImageOptions structure prior to making any
966 %  changes to it.
967 %
968 %  The format of the InitializeDifferenceImageOptions method is:
969 %
970 %      void InitializeDifferenceImageOptions(DifferenceImageOptions *options,
971 %                                            ExceptionInfo *exception)
972 %
973 %  A description of each parameter follows:
974 %
975 %    o options: pointer to DifferenceImageOptions structure to initialize.
976 %
977 %    o exception: Return any errors or warnings in this structure.
978 %
979 */
980 MagickExport void
InitializeDifferenceImageOptions(DifferenceImageOptions * options,ExceptionInfo * exception)981 InitializeDifferenceImageOptions(DifferenceImageOptions *options,
982                                  ExceptionInfo *exception)
983 {
984   assert(options != (DifferenceImageOptions *) NULL);
985   memset(options,0,sizeof(DifferenceImageOptions));
986   options->channel=AllChannels;
987   options->highlight_style=TintHighlightStyle;
988   (void) QueryColorDatabase(HighlightColor,&options->highlight_color,exception);
989 }
990 
991 
992 /*
993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994 %                                                                             %
995 %                                                                             %
996 %                                                                             %
997 %   I n i t i a l i z e D i f f e r e n c e S t a t i s t i c s               %
998 %                                                                             %
999 %                                                                             %
1000 %                                                                             %
1001 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002 %
1003 %  InitializeDifferenceStatistics() assigns default options to a user-provided
1004 %  DifferenceStatistics structure.
1005 %
1006 %  The format of the InitializeDifferenceStatistics method is:
1007 %
1008 %      void InitializeDifferenceStatistics(DifferenceStatistics *options,
1009 %                                          ExceptionInfo *exception)
1010 %
1011 %  A description of each parameter follows:
1012 %
1013 %    o options: pointer to DifferenceStatistics structure to initialize.
1014 %
1015 %    o exception: Return any errors or warnings in this structure.
1016 %
1017 */
1018 MagickExport void
InitializeDifferenceStatistics(DifferenceStatistics * statistics,ExceptionInfo * exception)1019 InitializeDifferenceStatistics(DifferenceStatistics *statistics,
1020                                ExceptionInfo *exception)
1021 {
1022   ARG_NOT_USED(exception);
1023   assert(statistics != (DifferenceStatistics *) NULL);
1024   statistics->red=0.0;
1025   statistics->green=0.0;
1026   statistics->blue=0.0;
1027   statistics->opacity=0.0;
1028   statistics->combined=0.0;
1029 }
1030