1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               CCCC   OOO   M   M  PPPP    AAA   RRRR    EEEEE               %
7 %              C      O   O  MM MM  P   P  A   A  R   R   E                   %
8 %              C      O   O  M M M  PPPP   AAAAA  RRRR    EEE                 %
9 %              C      O   O  M   M  P      A   A  R R     E                   %
10 %               CCCC   OOO   M   M  P      A   A  R  R    EEEEE               %
11 %                                                                             %
12 %                                                                             %
13 %                         Image Comparison Methods                            %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                               December 2003                                 %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %  Use the compare program to mathematically and visually annotate the
37 %  difference between an image and its reconstruction.
38 %
39 */
40 
41 /*
42   Include declarations.
43 */
44 #include "MagickWand/studio.h"
45 #include "MagickWand/MagickWand.h"
46 #include "MagickWand/mogrify-private.h"
47 #include "MagickCore/string-private.h"
48 
49 /*
50 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
51 %                                                                             %
52 %                                                                             %
53 %                                                                             %
54 %   C o m p a r e I m a g e C o m m a n d                                     %
55 %                                                                             %
56 %                                                                             %
57 %                                                                             %
58 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
59 %
60 %  CompareImagesCommand() compares two images and returns the difference between
61 %  them as a distortion metric and as a new image visually annotating their
62 %  differences.
63 %
64 %  The format of the CompareImagesCommand method is:
65 %
66 %      MagickBooleanType CompareImagesCommand(ImageInfo *image_info,int argc,
67 %        char **argv,char **metadata,ExceptionInfo *exception)
68 %
69 %  A description of each parameter follows:
70 %
71 %    o image_info: the image info.
72 %
73 %    o argc: the number of elements in the argument vector.
74 %
75 %    o argv: A text array containing the command line arguments.
76 %
77 %    o metadata: any metadata is returned here.
78 %
79 %    o exception: return any errors or warnings in this structure.
80 %
81 */
82 
CompareUsage(void)83 static MagickBooleanType CompareUsage(void)
84 {
85   static const char
86     channel_operators[] =
87       "  -separate            separate an image channel into a grayscale image",
88     miscellaneous[] =
89       "  -channel mask        set the image channel mask\n"
90       "  -debug events        display copious debugging information\n"
91       "  -help                print program options\n"
92       "  -list type           print a list of supported option arguments\n"
93       "  -log format          format of debugging information",
94     operators[] =
95       "  -brightness-contrast geometry\n"
96       "                       improve brightness / contrast of the image\n"
97       "  -distort method args\n"
98       "                       distort images according to given method and args\n"
99       "  -level value         adjust the level of image contrast\n"
100       "  -resize geometry     resize the image\n"
101       "  -rotate degrees      apply Paeth rotation to the image\n"
102       "  -sigmoidal-contrast geometry\n"
103       "                       increase the contrast without saturating highlights or\n"
104       "  -trim                trim image edges\n"
105       "  -write filename      write images to this file",
106     sequence_operators[] =
107       "  -crop geometry       cut out a rectangular region of the image",
108     settings[] =
109       "  -alpha option        on, activate, off, deactivate, set, opaque, copy\n"
110       "                       transparent, extract, background, or shape\n"
111       "  -authenticate password\n"
112       "                       decipher image with this password\n"
113       "  -background color    background color\n"
114       "  -colorspace type     alternate image colorspace\n"
115       "  -compose operator    set image composite operator\n"
116       "  -compress type       type of pixel compression when writing the image\n"
117       "  -decipher filename   convert cipher pixels to plain pixels\n"
118       "  -define format:option\n"
119       "                       define one or more image format options\n"
120       "  -density geometry    horizontal and vertical density of the image\n"
121       "  -depth value         image depth\n"
122       "  -dissimilarity-threshold value\n"
123       "                       maximum distortion for (sub)image match\n"
124       "  -encipher filename   convert plain pixels to cipher pixels\n"
125       "  -extract geometry    extract area from image\n"
126       "  -format \"string\"     output formatted image characteristics\n"
127       "  -fuzz distance       colors within this distance are considered equal\n"
128       "  -gravity type        horizontal and vertical text placement\n"
129       "  -highlight-color color\n"
130       "                       empasize pixel differences with this color\n"
131       "  -identify            identify the format and characteristics of the image\n"
132       "  -interlace type      type of image interlacing scheme\n"
133       "  -limit type value    pixel cache resource limit\n"
134       "  -lowlight-color color\n"
135       "                       de-emphasize pixel differences with this color\n"
136       "  -metric type         measure differences between images with this metric\n"
137       "  -monitor             monitor progress\n"
138       "  -negate              replace every pixel with its complementary color \n"
139       "  -passphrase filename get the passphrase from this file\n"
140       "  -precision value     maximum number of significant digits to print\n"
141       "  -profile filename    add, delete, or apply an image profile\n"
142       "  -quality value       JPEG/MIFF/PNG compression level\n"
143       "  -quiet               suppress all warning messages\n"
144       "  -quantize colorspace reduce colors in this colorspace\n"
145       "  -read-mask filename  associate a read mask with the image\n"
146       "  -regard-warnings     pay attention to warning messages\n"
147       "  -respect-parentheses settings remain in effect until parenthesis boundary\n"
148       "  -sampling-factor geometry\n"
149       "                       horizontal and vertical sampling factor\n"
150       "  -seed value          seed a new sequence of pseudo-random numbers\n"
151       "  -set attribute value set an image attribute\n"
152       "  -quality value       JPEG/MIFF/PNG compression level\n"
153       "  -repage geometry     size and location of an image canvas\n"
154       "  -similarity-threshold value\n"
155       "                       minimum distortion for (sub)image match\n"
156       "  -size geometry       width and height of image\n"
157       "  -subimage-search     search for subimage\n"
158       "  -synchronize         synchronize image to storage device\n"
159       "  -taint               declare the image as modified\n"
160       "  -transparent-color color\n"
161       "                       transparent color\n"
162       "  -type type           image type\n"
163       "  -verbose             print detailed information about the image\n"
164       "  -version             print version information\n"
165       "  -virtual-pixel method\n"
166       "                       virtual pixel access method\n"
167       "  -write-mask filename  associate a write mask with the image",
168     stack_operators[] =
169       "  -delete indexes      delete the image from the image sequence";
170 
171   ListMagickVersion(stdout);
172   (void) printf("Usage: %s [options ...] image reconstruct difference\n",
173     GetClientName());
174   (void) printf("\nImage Settings:\n");
175   (void) puts(settings);
176   (void) printf("\nImage Operators:\n");
177   (void) puts(operators);
178   (void) printf("\nImage Channel Operators:\n");
179   (void) puts(channel_operators);
180   (void) printf("\nImage Sequence Operators:\n");
181   (void) puts(sequence_operators);
182   (void) printf("\nImage Stack Operators:\n");
183   (void) puts(stack_operators);
184   (void) printf("\nMiscellaneous Options:\n");
185   (void) puts(miscellaneous);
186   (void) printf(
187     "\nBy default, the image format of 'file' is determined by its magic\n");
188   (void) printf(
189     "number.  To specify a particular image format, precede the filename\n");
190   (void) printf(
191     "with an image format name and a colon (i.e. ps:image) or specify the\n");
192   (void) printf(
193     "image type as the filename suffix (i.e. image.ps).  Specify 'file' as\n");
194   (void) printf("'-' for standard input or output.\n");
195   return(MagickTrue);
196 }
197 
CompareImagesCommand(ImageInfo * image_info,int argc,char ** argv,char ** metadata,ExceptionInfo * exception)198 WandExport MagickBooleanType CompareImagesCommand(ImageInfo *image_info,
199   int argc,char **argv,char **metadata,ExceptionInfo *exception)
200 {
201 #define CompareEpsilon  (1.0e-06)
202 #define DefaultDissimilarityThreshold  0.31830988618379067154
203 #define DefaultSimilarityThreshold  (-1.0)
204 #define DestroyCompare() \
205 { \
206   if (similarity_image != (Image *) NULL) \
207     similarity_image=DestroyImageList(similarity_image); \
208   if (difference_image != (Image *) NULL) \
209     difference_image=DestroyImageList(difference_image); \
210   DestroyImageStack(); \
211   for (i=0; i < (ssize_t) argc; i++) \
212     argv[i]=DestroyString(argv[i]); \
213   argv=(char **) RelinquishMagickMemory(argv); \
214 }
215 #define ThrowCompareException(asperity,tag,option) \
216 { \
217   if (exception->severity < (asperity)) \
218     (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \
219       "`%s'",option); \
220   DestroyCompare(); \
221   return(MagickFalse); \
222 }
223 #define ThrowCompareInvalidArgumentException(option,argument) \
224 { \
225   (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \
226     "InvalidArgument","'%s': %s",option,argument); \
227   DestroyCompare(); \
228   return(MagickFalse); \
229 }
230 
231   char
232     *filename,
233     *option;
234 
235   const char
236     *format;
237 
238   double
239     dissimilarity_threshold,
240     distortion,
241     similarity_metric,
242     similarity_threshold;
243 
244   Image
245     *difference_image,
246     *image,
247     *reconstruct_image,
248     *similarity_image;
249 
250   ImageStack
251     image_stack[MaxImageStackDepth+1];
252 
253   MagickBooleanType
254     fire,
255     pend,
256     respect_parenthesis,
257     subimage_search;
258 
259   MagickStatusType
260     status;
261 
262   MetricType
263     metric;
264 
265   RectangleInfo
266     offset;
267 
268   ssize_t
269     i;
270 
271   ssize_t
272     j,
273     k;
274 
275   /*
276     Set defaults.
277   */
278   assert(image_info != (ImageInfo *) NULL);
279   assert(image_info->signature == MagickCoreSignature);
280   if (image_info->debug != MagickFalse)
281     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
282   assert(exception != (ExceptionInfo *) NULL);
283   if (argc == 2)
284     {
285       option=argv[1];
286       if ((LocaleCompare("version",option+1) == 0) ||
287           (LocaleCompare("-version",option+1) == 0))
288         {
289           ListMagickVersion(stdout);
290           return(MagickTrue);
291         }
292     }
293   if (argc < 3)
294     return(CompareUsage());
295   difference_image=NewImageList();
296   similarity_image=NewImageList();
297   dissimilarity_threshold=DefaultDissimilarityThreshold;
298   similarity_threshold=DefaultSimilarityThreshold;
299   distortion=0.0;
300   format=(char *) NULL;
301   j=1;
302   k=0;
303   metric=UndefinedErrorMetric;
304   NewImageStack();
305   option=(char *) NULL;
306   pend=MagickFalse;
307   reconstruct_image=NewImageList();
308   respect_parenthesis=MagickFalse;
309   status=MagickTrue;
310   subimage_search=MagickFalse;
311   /*
312     Compare an image.
313   */
314   ReadCommandlLine(argc,&argv);
315   status=ExpandFilenames(&argc,&argv);
316   if (status == MagickFalse)
317     ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
318       GetExceptionMessage(errno));
319   for (i=1; i < (ssize_t) (argc-1); i++)
320   {
321     option=argv[i];
322     if (LocaleCompare(option,"(") == 0)
323       {
324         FireImageStack(MagickTrue,MagickTrue,pend);
325         if (k == MaxImageStackDepth)
326           ThrowCompareException(OptionError,"ParenthesisNestedTooDeeply",
327             option);
328         PushImageStack();
329         continue;
330       }
331     if (LocaleCompare(option,")") == 0)
332       {
333         FireImageStack(MagickTrue,MagickTrue,MagickTrue);
334         if (k == 0)
335           ThrowCompareException(OptionError,"UnableToParseExpression",option);
336         PopImageStack();
337         continue;
338       }
339     if (IsCommandOption(option) == MagickFalse)
340       {
341         Image
342           *images;
343 
344         /*
345           Read input image.
346         */
347         FireImageStack(MagickFalse,MagickFalse,pend);
348         filename=argv[i];
349         if ((LocaleCompare(filename,"--") == 0) && (i < (ssize_t) (argc-1)))
350           filename=argv[++i];
351         images=ReadImages(image_info,filename,exception);
352         status&=(images != (Image *) NULL) &&
353           (exception->severity < ErrorException);
354         if (images == (Image *) NULL)
355           continue;
356         AppendImageStack(images);
357         continue;
358       }
359     pend=image != (Image *) NULL ? MagickTrue : MagickFalse;
360     switch (*(option+1))
361     {
362       case 'a':
363       {
364         if (LocaleCompare("alpha",option+1) == 0)
365           {
366             ssize_t
367               type;
368 
369             if (*option == '+')
370               break;
371             i++;
372             if (i == (ssize_t) argc)
373               ThrowCompareException(OptionError,"MissingArgument",option);
374             type=ParseCommandOption(MagickAlphaChannelOptions,MagickFalse,
375               argv[i]);
376             if (type < 0)
377               ThrowCompareException(OptionError,
378                 "UnrecognizedAlphaChannelOption",argv[i]);
379             break;
380           }
381         if (LocaleCompare("authenticate",option+1) == 0)
382           {
383             if (*option == '+')
384               break;
385             i++;
386             if (i == (ssize_t) argc)
387               ThrowCompareException(OptionError,"MissingArgument",option);
388             break;
389           }
390         ThrowCompareException(OptionError,"UnrecognizedOption",option);
391       }
392       case 'b':
393       {
394         if (LocaleCompare("background",option+1) == 0)
395           {
396             if (*option == '+')
397               break;
398             i++;
399             if (i == (ssize_t) argc)
400               ThrowCompareException(OptionError,"MissingArgument",option);
401             break;
402           }
403         if (LocaleCompare("brightness-contrast",option+1) == 0)
404           {
405             i++;
406             if (i == (ssize_t) argc)
407               ThrowCompareException(OptionError,"MissingArgument",option);
408             if (IsGeometry(argv[i]) == MagickFalse)
409               ThrowCompareInvalidArgumentException(option,argv[i]);
410             break;
411           }
412         ThrowCompareException(OptionError,"UnrecognizedOption",option);
413       }
414       case 'c':
415       {
416         if (LocaleCompare("cache",option+1) == 0)
417           {
418             if (*option == '+')
419               break;
420             i++;
421             if (i == (ssize_t) argc)
422               ThrowCompareException(OptionError,"MissingArgument",option);
423             if (IsGeometry(argv[i]) == MagickFalse)
424               ThrowCompareInvalidArgumentException(option,argv[i]);
425             break;
426           }
427         if (LocaleCompare("channel",option+1) == 0)
428           {
429             ssize_t
430               channel;
431 
432             if (*option == '+')
433               break;
434             i++;
435             if (i == (ssize_t) argc)
436               ThrowCompareException(OptionError,"MissingArgument",option);
437             channel=ParseChannelOption(argv[i]);
438             if (channel < 0)
439               ThrowCompareException(OptionError,"UnrecognizedChannelType",
440                 argv[i]);
441             break;
442           }
443         if (LocaleCompare("colorspace",option+1) == 0)
444           {
445             ssize_t
446               colorspace;
447 
448             if (*option == '+')
449               break;
450             i++;
451             if (i == (ssize_t) argc)
452               ThrowCompareException(OptionError,"MissingArgument",option);
453             colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
454               argv[i]);
455             if (colorspace < 0)
456               ThrowCompareException(OptionError,"UnrecognizedColorspace",
457                 argv[i]);
458             break;
459           }
460         if (LocaleCompare("compose",option+1) == 0)
461           {
462             ssize_t
463               compose;
464 
465             if (*option == '+')
466               break;
467             i++;
468             if (i == (ssize_t) argc)
469               ThrowCompareException(OptionError,"MissingArgument",option);
470             compose=ParseCommandOption(MagickComposeOptions,MagickFalse,
471               argv[i]);
472             if (compose < 0)
473               ThrowCompareException(OptionError,"UnrecognizedComposeOperator",
474                 argv[i]);
475             break;
476           }
477         if (LocaleCompare("compress",option+1) == 0)
478           {
479             ssize_t
480               compress;
481 
482             if (*option == '+')
483               break;
484             i++;
485             if (i == (ssize_t) argc)
486               ThrowCompareException(OptionError,"MissingArgument",option);
487             compress=ParseCommandOption(MagickCompressOptions,MagickFalse,
488               argv[i]);
489             if (compress < 0)
490               ThrowCompareException(OptionError,"UnrecognizedImageCompression",
491                 argv[i]);
492             break;
493           }
494         if (LocaleCompare("concurrent",option+1) == 0)
495           break;
496         if (LocaleCompare("crop",option+1) == 0)
497           {
498             if (*option == '+')
499               break;
500             i++;
501             if (i == (ssize_t) argc)
502               ThrowCompareException(OptionError,"MissingArgument",option);
503             if (IsGeometry(argv[i]) == MagickFalse)
504               ThrowCompareInvalidArgumentException(option,argv[i]);
505             break;
506           }
507         ThrowCompareException(OptionError,"UnrecognizedOption",option)
508       }
509       case 'd':
510       {
511         if (LocaleCompare("debug",option+1) == 0)
512           {
513             LogEventType
514               event_mask;
515 
516             if (*option == '+')
517               break;
518             i++;
519             if (i == (ssize_t) argc)
520               ThrowCompareException(OptionError,"MissingArgument",option);
521             event_mask=SetLogEventMask(argv[i]);
522             if (event_mask == UndefinedEvents)
523               ThrowCompareException(OptionError,"UnrecognizedEventType",
524                 argv[i]);
525             break;
526           }
527         if (LocaleCompare("decipher",option+1) == 0)
528           {
529             if (*option == '+')
530               break;
531             i++;
532             if (i == (ssize_t) argc)
533               ThrowCompareException(OptionError,"MissingArgument",option);
534             break;
535           }
536         if (LocaleCompare("define",option+1) == 0)
537           {
538             i++;
539             if (i == (ssize_t) argc)
540               ThrowCompareException(OptionError,"MissingArgument",option);
541             if (*option == '+')
542               {
543                 const char
544                   *define;
545 
546                 define=GetImageOption(image_info,argv[i]);
547                 if (define == (const char *) NULL)
548                   ThrowCompareException(OptionError,"NoSuchOption",argv[i]);
549                 break;
550               }
551             break;
552           }
553         if (LocaleCompare("delete",option+1) == 0)
554           {
555             if (*option == '+')
556               break;
557             i++;
558             if (i == (ssize_t) argc)
559               ThrowCompareException(OptionError,"MissingArgument",option);
560             if (IsSceneGeometry(argv[i],MagickFalse) == MagickFalse)
561               ThrowCompareInvalidArgumentException(option,argv[i]);
562             break;
563           }
564         if (LocaleCompare("density",option+1) == 0)
565           {
566             if (*option == '+')
567               break;
568             i++;
569             if (i == (ssize_t) argc)
570               ThrowCompareException(OptionError,"MissingArgument",option);
571             if (IsGeometry(argv[i]) == MagickFalse)
572               ThrowCompareInvalidArgumentException(option,argv[i]);
573             break;
574           }
575         if (LocaleCompare("depth",option+1) == 0)
576           {
577             if (*option == '+')
578               break;
579             i++;
580             if (i == (ssize_t) argc)
581               ThrowCompareException(OptionError,"MissingArgument",option);
582             if (IsGeometry(argv[i]) == MagickFalse)
583               ThrowCompareInvalidArgumentException(option,argv[i]);
584             break;
585           }
586         if (LocaleCompare("dissimilarity-threshold",option+1) == 0)
587           {
588             if (*option == '+')
589               break;
590             i++;
591             if (i == (ssize_t) argc)
592               ThrowCompareException(OptionError,"MissingArgument",option);
593             if (IsGeometry(argv[i]) == MagickFalse)
594               ThrowCompareInvalidArgumentException(option,argv[i]);
595             if (*option == '+')
596               dissimilarity_threshold=DefaultDissimilarityThreshold;
597             else
598               dissimilarity_threshold=StringToDouble(argv[i],(char **) NULL);
599             break;
600           }
601         if (LocaleCompare("distort",option+1) == 0)
602           {
603             ssize_t
604               op;
605 
606             i++;
607             if (i == (ssize_t) argc)
608               ThrowCompareException(OptionError,"MissingArgument",option);
609             op=ParseCommandOption(MagickDistortOptions,MagickFalse,argv[i]);
610             if (op < 0)
611               ThrowCompareException(OptionError,"UnrecognizedDistortMethod",
612                 argv[i]);
613             i++;
614             if (i == (ssize_t) argc)
615               ThrowCompareException(OptionError,"MissingArgument",option);
616             break;
617           }
618         if (LocaleCompare("duration",option+1) == 0)
619           {
620             if (*option == '+')
621               break;
622             i++;
623             if (i == (ssize_t) argc)
624               ThrowCompareException(OptionError,"MissingArgument",option);
625             if (IsGeometry(argv[i]) == MagickFalse)
626               ThrowCompareInvalidArgumentException(option,argv[i]);
627             break;
628           }
629         ThrowCompareException(OptionError,"UnrecognizedOption",option)
630       }
631       case 'e':
632       {
633         if (LocaleCompare("encipher",option+1) == 0)
634           {
635             if (*option == '+')
636               break;
637             i++;
638             if (i == (ssize_t) argc)
639               ThrowCompareException(OptionError,"MissingArgument",option);
640             break;
641           }
642         if (LocaleCompare("extract",option+1) == 0)
643           {
644             if (*option == '+')
645               break;
646             i++;
647             if (i == (ssize_t) argc)
648               ThrowCompareException(OptionError,"MissingArgument",option);
649             if (IsGeometry(argv[i]) == MagickFalse)
650               ThrowCompareInvalidArgumentException(option,argv[i]);
651             break;
652           }
653         ThrowCompareException(OptionError,"UnrecognizedOption",option)
654       }
655       case 'f':
656       {
657         if (LocaleCompare("format",option+1) == 0)
658           {
659             if (*option == '+')
660               break;
661             i++;
662             if (i == (ssize_t) argc)
663               ThrowCompareException(OptionError,"MissingArgument",option);
664             format=argv[i];
665             break;
666           }
667         if (LocaleCompare("fuzz",option+1) == 0)
668           {
669             if (*option == '+')
670               break;
671             i++;
672             if (i == (ssize_t) argc)
673               ThrowCompareException(OptionError,"MissingArgument",option);
674             if (IsGeometry(argv[i]) == MagickFalse)
675               ThrowCompareInvalidArgumentException(option,argv[i]);
676             break;
677           }
678         ThrowCompareException(OptionError,"UnrecognizedOption",option)
679       }
680       case 'g':
681       {
682         if (LocaleCompare("gravity",option+1) == 0)
683           {
684             ssize_t
685               gravity;
686 
687             if (*option == '+')
688               break;
689             i++;
690             if (i == (ssize_t) argc)
691               ThrowCompareException(OptionError,"MissingArgument",option);
692             gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,
693               argv[i]);
694             if (gravity < 0)
695               ThrowCompareException(OptionError,"UnrecognizedGravityType",
696                 argv[i]);
697             break;
698           }
699         ThrowCompareException(OptionError,"UnrecognizedOption",option)
700       }
701       case 'h':
702       {
703         if ((LocaleCompare("help",option+1) == 0) ||
704             (LocaleCompare("-help",option+1) == 0))
705           {
706             DestroyCompare();
707             return(CompareUsage());
708           }
709         if (LocaleCompare("highlight-color",option+1) == 0)
710           {
711             if (*option == '+')
712               break;
713             i++;
714             if (i == (ssize_t) argc)
715               ThrowCompareException(OptionError,"MissingArgument",option);
716             break;
717           }
718         ThrowCompareException(OptionError,"UnrecognizedOption",option)
719       }
720       case 'i':
721       {
722         if (LocaleCompare("identify",option+1) == 0)
723           break;
724         if (LocaleCompare("interlace",option+1) == 0)
725           {
726             ssize_t
727               interlace;
728 
729             if (*option == '+')
730               break;
731             i++;
732             if (i == (ssize_t) argc)
733               ThrowCompareException(OptionError,"MissingArgument",option);
734             interlace=ParseCommandOption(MagickInterlaceOptions,MagickFalse,
735               argv[i]);
736             if (interlace < 0)
737               ThrowCompareException(OptionError,"UnrecognizedInterlaceType",
738                 argv[i]);
739             break;
740           }
741         ThrowCompareException(OptionError,"UnrecognizedOption",option)
742       }
743       case 'l':
744       {
745         if (LocaleCompare("level",option+1) == 0)
746           {
747             i++;
748             if (i == (ssize_t) argc)
749               ThrowCompareException(OptionError,"MissingArgument",option);
750             if (IsGeometry(argv[i]) == MagickFalse)
751               ThrowCompareInvalidArgumentException(option,argv[i]);
752             break;
753           }
754         if (LocaleCompare("limit",option+1) == 0)
755           {
756             char
757               *p;
758 
759             double
760               value;
761 
762             ssize_t
763               resource;
764 
765             if (*option == '+')
766               break;
767             i++;
768             if (i == (ssize_t) argc)
769               ThrowCompareException(OptionError,"MissingArgument",option);
770             resource=ParseCommandOption(MagickResourceOptions,MagickFalse,
771               argv[i]);
772             if (resource < 0)
773               ThrowCompareException(OptionError,"UnrecognizedResourceType",
774                 argv[i]);
775             i++;
776             if (i == (ssize_t) argc)
777               ThrowCompareException(OptionError,"MissingArgument",option);
778             value=StringToDouble(argv[i],&p);
779             (void) value;
780             if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
781               ThrowCompareInvalidArgumentException(option,argv[i]);
782             break;
783           }
784         if (LocaleCompare("list",option+1) == 0)
785           {
786             ssize_t
787               list;
788 
789             if (*option == '+')
790               break;
791             i++;
792             if (i == (ssize_t) argc)
793               ThrowCompareException(OptionError,"MissingArgument",option);
794             list=ParseCommandOption(MagickListOptions,MagickFalse,argv[i]);
795             if (list < 0)
796               ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
797             status=MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
798               argv+j,exception);
799             DestroyCompare();
800             return(status == 0 ? MagickFalse : MagickTrue);
801           }
802         if (LocaleCompare("log",option+1) == 0)
803           {
804             if (*option == '+')
805               break;
806             i++;
807             if ((i == (ssize_t) argc) || (strchr(argv[i],'%') == (char *) NULL))
808               ThrowCompareException(OptionError,"MissingArgument",option);
809             break;
810           }
811         if (LocaleCompare("lowlight-color",option+1) == 0)
812           {
813             if (*option == '+')
814               break;
815             i++;
816             if (i == (ssize_t) argc)
817               ThrowCompareException(OptionError,"MissingArgument",option);
818             break;
819           }
820         ThrowCompareException(OptionError,"UnrecognizedOption",option)
821       }
822       case 'm':
823       {
824         if (LocaleCompare("matte",option+1) == 0)
825           break;
826         if (LocaleCompare("metric",option+1) == 0)
827           {
828             ssize_t
829               type;
830 
831             if (*option == '+')
832               break;
833             i++;
834             if (i == (ssize_t) argc)
835               ThrowCompareException(OptionError,"MissingArgument",option);
836             type=ParseCommandOption(MagickMetricOptions,MagickTrue,argv[i]);
837             if (type < 0)
838               ThrowCompareException(OptionError,"UnrecognizedMetricType",
839                 argv[i]);
840             metric=(MetricType) type;
841             break;
842           }
843         if (LocaleCompare("monitor",option+1) == 0)
844           break;
845         ThrowCompareException(OptionError,"UnrecognizedOption",option)
846       }
847       case 'n':
848       {
849         if (LocaleCompare("negate",option+1) == 0)
850           break;
851         ThrowCompareException(OptionError,"UnrecognizedOption",option)
852       }
853       case 'p':
854       {
855         if (LocaleCompare("passphrase",option+1) == 0)
856           {
857             if (*option == '+')
858               break;
859             i++;
860             if (i == (ssize_t) argc)
861               ThrowCompareException(OptionError,"MissingArgument",option);
862             break;
863           }
864         if (LocaleCompare("precision",option+1) == 0)
865           {
866             if (*option == '+')
867               break;
868             i++;
869             if (i == (ssize_t) argc)
870               ThrowCompareException(OptionError,"MissingArgument",option);
871             if (IsGeometry(argv[i]) == MagickFalse)
872               ThrowCompareInvalidArgumentException(option,argv[i]);
873             break;
874           }
875         if (LocaleCompare("profile",option+1) == 0)
876           {
877             i++;
878             if (i == (ssize_t) argc)
879               ThrowCompareException(OptionError,"MissingArgument",option);
880             break;
881           }
882         ThrowCompareException(OptionError,"UnrecognizedOption",option)
883       }
884       case 'q':
885       {
886         if (LocaleCompare("quality",option+1) == 0)
887           {
888             if (*option == '+')
889               break;
890             i++;
891             if (i == (ssize_t) argc)
892               ThrowCompareException(OptionError,"MissingArgument",option);
893             if (IsGeometry(argv[i]) == MagickFalse)
894               ThrowCompareInvalidArgumentException(option,argv[i]);
895             break;
896           }
897         if (LocaleCompare("quantize",option+1) == 0)
898           {
899             ssize_t
900               colorspace;
901 
902             if (*option == '+')
903               break;
904             i++;
905             if (i == (ssize_t) argc)
906               ThrowCompareException(OptionError,"MissingArgument",option);
907             colorspace=ParseCommandOption(MagickColorspaceOptions,
908               MagickFalse,argv[i]);
909             if (colorspace < 0)
910               ThrowCompareException(OptionError,"UnrecognizedColorspace",
911                 argv[i]);
912             break;
913           }
914         if (LocaleCompare("quiet",option+1) == 0)
915           break;
916         ThrowCompareException(OptionError,"UnrecognizedOption",option)
917       }
918       case 'r':
919       {
920         if (LocaleCompare("read-mask",option+1) == 0)
921           {
922             if (*option == '+')
923               break;
924             i++;
925             if (i == (ssize_t) argc)
926               ThrowCompareException(OptionError,"MissingArgument",option);
927             break;
928           }
929         if (LocaleCompare("regard-warnings",option+1) == 0)
930           break;
931         if (LocaleCompare("repage",option+1) == 0)
932           {
933             if (*option == '+')
934               break;
935             i++;
936             if (i == (ssize_t) argc)
937               ThrowCompareException(OptionError,"MissingArgument",option);
938             if (IsGeometry(argv[i]) == MagickFalse)
939               ThrowCompareInvalidArgumentException(option,argv[i]);
940             break;
941           }
942         if (LocaleCompare("resize",option+1) == 0)
943           {
944             if (*option == '+')
945               break;
946             i++;
947             if (i == (ssize_t) argc)
948               ThrowCompareException(OptionError,"MissingArgument",option);
949             if (IsGeometry(argv[i]) == MagickFalse)
950               ThrowCompareInvalidArgumentException(option,argv[i]);
951             break;
952           }
953         if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
954           {
955             respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
956             break;
957           }
958         if (LocaleCompare("rotate",option+1) == 0)
959           {
960             i++;
961             if (i == (ssize_t) argc)
962               ThrowCompareException(OptionError,"MissingArgument",option);
963             if (IsGeometry(argv[i]) == MagickFalse)
964               ThrowCompareInvalidArgumentException(option,argv[i]);
965             break;
966           }
967         ThrowCompareException(OptionError,"UnrecognizedOption",option)
968       }
969       case 's':
970       {
971         if (LocaleCompare("sampling-factor",option+1) == 0)
972           {
973             if (*option == '+')
974               break;
975             i++;
976             if (i == (ssize_t) argc)
977               ThrowCompareException(OptionError,"MissingArgument",option);
978             if (IsGeometry(argv[i]) == MagickFalse)
979               ThrowCompareInvalidArgumentException(option,argv[i]);
980             break;
981           }
982         if (LocaleCompare("seed",option+1) == 0)
983           {
984             if (*option == '+')
985               break;
986             i++;
987             if (i == (ssize_t) argc)
988               ThrowCompareException(OptionError,"MissingArgument",option);
989             if (IsGeometry(argv[i]) == MagickFalse)
990               ThrowCompareInvalidArgumentException(option,argv[i]);
991             break;
992           }
993         if (LocaleCompare("separate",option+1) == 0)
994           break;
995         if (LocaleCompare("set",option+1) == 0)
996           {
997             i++;
998             if (i == (ssize_t) argc)
999               ThrowCompareException(OptionError,"MissingArgument",option);
1000             if (*option == '+')
1001               break;
1002             i++;
1003             if (i == (ssize_t) argc)
1004               ThrowCompareException(OptionError,"MissingArgument",option);
1005             break;
1006           }
1007         if (LocaleCompare("sigmoidal-contrast",option+1) == 0)
1008           {
1009             i++;
1010             if (i == (ssize_t) argc)
1011               ThrowCompareException(OptionError,"MissingArgument",option);
1012             if (IsGeometry(argv[i]) == MagickFalse)
1013               ThrowCompareInvalidArgumentException(option,argv[i]);
1014             break;
1015           }
1016         if (LocaleCompare("similarity-threshold",option+1) == 0)
1017           {
1018             if (*option == '+')
1019               break;
1020             i++;
1021             if (i == (ssize_t) argc)
1022               ThrowCompareException(OptionError,"MissingArgument",option);
1023             if (IsGeometry(argv[i]) == MagickFalse)
1024               ThrowCompareInvalidArgumentException(option,argv[i]);
1025             if (*option == '+')
1026               similarity_threshold=DefaultSimilarityThreshold;
1027             else
1028               similarity_threshold=StringToDouble(argv[i],(char **) NULL);
1029             break;
1030           }
1031         if (LocaleCompare("size",option+1) == 0)
1032           {
1033             if (*option == '+')
1034               break;
1035             i++;
1036             if (i == (ssize_t) argc)
1037               ThrowCompareException(OptionError,"MissingArgument",option);
1038             if (IsGeometry(argv[i]) == MagickFalse)
1039               ThrowCompareInvalidArgumentException(option,argv[i]);
1040             break;
1041           }
1042         if (LocaleCompare("subimage-search",option+1) == 0)
1043           {
1044             if (*option == '+')
1045               {
1046                 subimage_search=MagickFalse;
1047                 break;
1048               }
1049             subimage_search=MagickTrue;
1050             break;
1051           }
1052         if (LocaleCompare("synchronize",option+1) == 0)
1053           break;
1054         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1055       }
1056       case 't':
1057       {
1058         if (LocaleCompare("taint",option+1) == 0)
1059           break;
1060         if (LocaleCompare("transparent-color",option+1) == 0)
1061           {
1062             if (*option == '+')
1063               break;
1064             i++;
1065             if (i == (ssize_t) argc)
1066               ThrowCompareException(OptionError,"MissingArgument",option);
1067             break;
1068           }
1069         if (LocaleCompare("trim",option+1) == 0)
1070           break;
1071         if (LocaleCompare("type",option+1) == 0)
1072           {
1073             ssize_t
1074               type;
1075 
1076             if (*option == '+')
1077               break;
1078             i++;
1079             if (i == (ssize_t) argc)
1080               ThrowCompareException(OptionError,"MissingArgument",option);
1081             type=ParseCommandOption(MagickTypeOptions,MagickFalse,argv[i]);
1082             if (type < 0)
1083               ThrowCompareException(OptionError,"UnrecognizedImageType",
1084                 argv[i]);
1085             break;
1086           }
1087         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1088       }
1089       case 'v':
1090       {
1091         if (LocaleCompare("verbose",option+1) == 0)
1092           break;
1093         if ((LocaleCompare("version",option+1) == 0) ||
1094             (LocaleCompare("-version",option+1) == 0))
1095           {
1096             ListMagickVersion(stdout);
1097             break;
1098           }
1099         if (LocaleCompare("virtual-pixel",option+1) == 0)
1100           {
1101             ssize_t
1102               method;
1103 
1104             if (*option == '+')
1105               break;
1106             i++;
1107             if (i == (ssize_t) argc)
1108               ThrowCompareException(OptionError,"MissingArgument",option);
1109             method=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
1110               argv[i]);
1111             if (method < 0)
1112               ThrowCompareException(OptionError,
1113                 "UnrecognizedVirtualPixelMethod",argv[i]);
1114             break;
1115           }
1116         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1117       }
1118       case 'w':
1119       {
1120         if (LocaleCompare("write",option+1) == 0)
1121           {
1122             i++;
1123             if (i == (ssize_t) argc)
1124               ThrowCompareException(OptionError,"MissingArgument",option);
1125             break;
1126           }
1127         if (LocaleCompare("write-mask",option+1) == 0)
1128           {
1129             if (*option == '+')
1130               break;
1131             i++;
1132             if (i == (ssize_t) argc)
1133               ThrowCompareException(OptionError,"MissingArgument",option);
1134             break;
1135           }
1136         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1137       }
1138       case '?':
1139         break;
1140       default:
1141         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1142     }
1143     fire=(GetCommandOptionFlags(MagickCommandOptions,MagickFalse,option) &
1144       FireOptionFlag) == 0 ?  MagickFalse : MagickTrue;
1145     if (fire != MagickFalse)
1146       FireImageStack(MagickTrue,MagickTrue,MagickTrue);
1147   }
1148   if (k != 0)
1149     ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
1150   if (i-- != (ssize_t) (argc-1))
1151     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1152   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1153     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1154   FinalizeImageSettings(image_info,image,MagickTrue);
1155   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1156     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1157   image=GetImageFromList(image,0);
1158   reconstruct_image=GetImageFromList(image,1);
1159   offset.x=0;
1160   offset.y=0;
1161   if (subimage_search != MagickFalse)
1162     {
1163       similarity_image=SimilarityImage(image,reconstruct_image,metric,
1164         similarity_threshold,&offset,&similarity_metric,exception);
1165       if (similarity_metric > dissimilarity_threshold)
1166         ThrowCompareException(ImageError,"ImagesTooDissimilar",image->filename);
1167     }
1168   if ((reconstruct_image->columns == image->columns) &&
1169        (reconstruct_image->rows == image->rows))
1170     difference_image=CompareImages(image,reconstruct_image,metric,&distortion,
1171       exception);
1172   else
1173     if (similarity_image == (Image *) NULL)
1174       difference_image=CompareImages(image,reconstruct_image,metric,&distortion,
1175         exception);
1176     else
1177       {
1178         Image
1179           *composite_image;
1180 
1181         /*
1182           Determine if reconstructed image is a subimage of the image.
1183         */
1184         composite_image=CloneImage(image,0,0,MagickTrue,exception);
1185         if (composite_image == (Image *) NULL)
1186           difference_image=CompareImages(image,reconstruct_image,metric,
1187             &distortion,exception);
1188         else
1189           {
1190             Image
1191               *distort_image;
1192 
1193             RectangleInfo
1194               page;
1195 
1196             (void) CompositeImage(composite_image,reconstruct_image,
1197               CopyCompositeOp,MagickTrue,offset.x,offset.y,exception);
1198             difference_image=CompareImages(image,composite_image,metric,
1199               &distortion,exception);
1200             if (difference_image != (Image *) NULL)
1201               {
1202                 difference_image->page.x=offset.x;
1203                 difference_image->page.y=offset.y;
1204               }
1205             composite_image=DestroyImage(composite_image);
1206             page.width=reconstruct_image->columns;
1207             page.height=reconstruct_image->rows;
1208             page.x=offset.x;
1209             page.y=offset.y;
1210             distort_image=CropImage(image,&page,exception);
1211             if (distort_image != (Image *) NULL)
1212               {
1213                 Image
1214                   *sans_image;
1215 
1216                 sans_image=CompareImages(distort_image,reconstruct_image,metric,
1217                   &distortion,exception);
1218                 distort_image=DestroyImage(distort_image);
1219                 if (sans_image != (Image *) NULL)
1220                   sans_image=DestroyImage(sans_image);
1221               }
1222           }
1223         if (difference_image != (Image *) NULL)
1224           {
1225             AppendImageToList(&difference_image,similarity_image);
1226             similarity_image=(Image *) NULL;
1227           }
1228       }
1229   if (difference_image == (Image *) NULL)
1230     status=0;
1231   else
1232     {
1233       if (image_info->verbose != MagickFalse)
1234         (void) SetImageColorMetric(image,reconstruct_image,exception);
1235       if (*difference_image->magick == '\0')
1236         (void) CopyMagickString(difference_image->magick,image->magick,
1237           MagickPathExtent);
1238       if (image_info->verbose == MagickFalse)
1239         {
1240           switch (metric)
1241           {
1242             case FuzzErrorMetric:
1243             case MeanAbsoluteErrorMetric:
1244             case MeanSquaredErrorMetric:
1245             case PeakAbsoluteErrorMetric:
1246             case RootMeanSquaredErrorMetric:
1247             {
1248               (void) FormatLocaleFile(stderr,"%.*g (%.*g)",GetMagickPrecision(),
1249                 QuantumRange*distortion,GetMagickPrecision(),
1250                 (double) distortion);
1251               break;
1252             }
1253             case AbsoluteErrorMetric:
1254             case NormalizedCrossCorrelationErrorMetric:
1255             case PeakSignalToNoiseRatioErrorMetric:
1256             case PerceptualHashErrorMetric:
1257             case StructuralSimilarityErrorMetric:
1258             case StructuralDissimilarityErrorMetric:
1259             {
1260               (void) FormatLocaleFile(stderr,"%.*g",GetMagickPrecision(),
1261                 distortion);
1262               break;
1263             }
1264             case MeanErrorPerPixelErrorMetric:
1265             {
1266               (void) FormatLocaleFile(stderr,"%.*g (%.*g, %.*g)",
1267                 GetMagickPrecision(),distortion,
1268                 GetMagickPrecision(),image->error.normalized_mean_error,
1269                 GetMagickPrecision(),image->error.normalized_maximum_error);
1270               break;
1271             }
1272             case UndefinedErrorMetric:
1273               break;
1274           }
1275           if (subimage_search != MagickFalse)
1276             (void) FormatLocaleFile(stderr," @ %.20g,%.20g",
1277               (double) difference_image->page.x,
1278               (double) difference_image->page.y);
1279         }
1280       else
1281         {
1282           double
1283             *channel_distortion;
1284 
1285           channel_distortion=GetImageDistortions(image,reconstruct_image,
1286             metric,exception);
1287           (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename);
1288           if ((reconstruct_image->columns != image->columns) ||
1289               (reconstruct_image->rows != image->rows))
1290             (void) FormatLocaleFile(stderr,"Offset: %.20g,%.20g\n",(double)
1291               difference_image->page.x,(double) difference_image->page.y);
1292           (void) FormatLocaleFile(stderr,"  Channel distortion: %s\n",
1293             CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1294           switch (metric)
1295           {
1296             case FuzzErrorMetric:
1297             case MeanAbsoluteErrorMetric:
1298             case MeanSquaredErrorMetric:
1299             case PeakAbsoluteErrorMetric:
1300             case RootMeanSquaredErrorMetric:
1301             {
1302               switch (image->colorspace)
1303               {
1304                 case RGBColorspace:
1305                 default:
1306                 {
1307                   (void) FormatLocaleFile(stderr,"    red: %.*g (%.*g)\n",
1308                     GetMagickPrecision(),QuantumRange*
1309                     channel_distortion[RedPixelChannel],GetMagickPrecision(),
1310                     channel_distortion[RedPixelChannel]);
1311                   (void) FormatLocaleFile(stderr,"    green: %.*g (%.*g)\n",
1312                     GetMagickPrecision(),QuantumRange*
1313                     channel_distortion[GreenPixelChannel],GetMagickPrecision(),
1314                     channel_distortion[GreenPixelChannel]);
1315                   (void) FormatLocaleFile(stderr,"    blue: %.*g (%.*g)\n",
1316                     GetMagickPrecision(),QuantumRange*
1317                     channel_distortion[BluePixelChannel],GetMagickPrecision(),
1318                     channel_distortion[BluePixelChannel]);
1319                   if (image->alpha_trait != UndefinedPixelTrait)
1320                     (void) FormatLocaleFile(stderr,"    alpha: %.*g (%.*g)\n",
1321                       GetMagickPrecision(),QuantumRange*
1322                       channel_distortion[AlphaPixelChannel],
1323                       GetMagickPrecision(),
1324                       channel_distortion[AlphaPixelChannel]);
1325                   break;
1326                 }
1327                 case CMYKColorspace:
1328                 {
1329                   (void) FormatLocaleFile(stderr,"    cyan: %.*g (%.*g)\n",
1330                     GetMagickPrecision(),QuantumRange*
1331                     channel_distortion[CyanPixelChannel],GetMagickPrecision(),
1332                     channel_distortion[CyanPixelChannel]);
1333                   (void) FormatLocaleFile(stderr,"    magenta: %.*g (%.*g)\n",
1334                     GetMagickPrecision(),QuantumRange*
1335                     channel_distortion[MagentaPixelChannel],
1336                     GetMagickPrecision(),
1337                     channel_distortion[MagentaPixelChannel]);
1338                   (void) FormatLocaleFile(stderr,"    yellow: %.*g (%.*g)\n",
1339                     GetMagickPrecision(),QuantumRange*
1340                     channel_distortion[YellowPixelChannel],GetMagickPrecision(),
1341                     channel_distortion[YellowPixelChannel]);
1342                   (void) FormatLocaleFile(stderr,"    black: %.*g (%.*g)\n",
1343                     GetMagickPrecision(),QuantumRange*
1344                     channel_distortion[BlackPixelChannel],GetMagickPrecision(),
1345                     channel_distortion[BlackPixelChannel]);
1346                   if (image->alpha_trait != UndefinedPixelTrait)
1347                     (void) FormatLocaleFile(stderr,"    alpha: %.*g (%.*g)\n",
1348                       GetMagickPrecision(),QuantumRange*
1349                       channel_distortion[AlphaPixelChannel],
1350                       GetMagickPrecision(),
1351                       channel_distortion[AlphaPixelChannel]);
1352                   break;
1353                 }
1354                 case LinearGRAYColorspace:
1355                 case GRAYColorspace:
1356                 {
1357                   (void) FormatLocaleFile(stderr,"    gray: %.*g (%.*g)\n",
1358                     GetMagickPrecision(),QuantumRange*
1359                     channel_distortion[GrayPixelChannel],GetMagickPrecision(),
1360                     channel_distortion[GrayPixelChannel]);
1361                   if (image->alpha_trait != UndefinedPixelTrait)
1362                     (void) FormatLocaleFile(stderr,"    alpha: %.*g (%.*g)\n",
1363                       GetMagickPrecision(),QuantumRange*
1364                       channel_distortion[AlphaPixelChannel],
1365                       GetMagickPrecision(),
1366                       channel_distortion[AlphaPixelChannel]);
1367                   break;
1368                 }
1369               }
1370               (void) FormatLocaleFile(stderr,"    all: %.*g (%.*g)\n",
1371                 GetMagickPrecision(),QuantumRange*
1372                 channel_distortion[MaxPixelChannels],GetMagickPrecision(),
1373                 channel_distortion[MaxPixelChannels]);
1374               break;
1375             }
1376             case AbsoluteErrorMetric:
1377             case NormalizedCrossCorrelationErrorMetric:
1378             case PeakSignalToNoiseRatioErrorMetric:
1379             case PerceptualHashErrorMetric:
1380             case StructuralSimilarityErrorMetric:
1381             case StructuralDissimilarityErrorMetric:
1382             {
1383               switch (image->colorspace)
1384               {
1385                 case RGBColorspace:
1386                 default:
1387                 {
1388                   (void) FormatLocaleFile(stderr,"    red: %.*g\n",
1389                     GetMagickPrecision(),channel_distortion[RedPixelChannel]);
1390                   (void) FormatLocaleFile(stderr,"    green: %.*g\n",
1391                     GetMagickPrecision(),channel_distortion[GreenPixelChannel]);
1392                   (void) FormatLocaleFile(stderr,"    blue: %.*g\n",
1393                     GetMagickPrecision(),channel_distortion[BluePixelChannel]);
1394                   if (image->alpha_trait != UndefinedPixelTrait)
1395                     (void) FormatLocaleFile(stderr,"    alpha: %.*g\n",
1396                       GetMagickPrecision(),
1397                        channel_distortion[AlphaPixelChannel]);
1398                   break;
1399                 }
1400                 case CMYKColorspace:
1401                 {
1402                   (void) FormatLocaleFile(stderr,"    cyan: %.*g\n",
1403                     GetMagickPrecision(),channel_distortion[CyanPixelChannel]);
1404                   (void) FormatLocaleFile(stderr,"    magenta: %.*g\n",
1405                     GetMagickPrecision(),
1406                     channel_distortion[MagentaPixelChannel]);
1407                   (void) FormatLocaleFile(stderr,"    yellow: %.*g\n",
1408                     GetMagickPrecision(),
1409                     channel_distortion[YellowPixelChannel]);
1410                   (void) FormatLocaleFile(stderr,"    black: %.*g\n",
1411                     GetMagickPrecision(),
1412                     channel_distortion[BlackPixelChannel]);
1413                   if (image->alpha_trait != UndefinedPixelTrait)
1414                     (void) FormatLocaleFile(stderr,"    alpha: %.*g\n",
1415                       GetMagickPrecision(),
1416                       channel_distortion[AlphaPixelChannel]);
1417                   break;
1418                 }
1419                 case LinearGRAYColorspace:
1420                 case GRAYColorspace:
1421                 {
1422                   (void) FormatLocaleFile(stderr,"    gray: %.*g\n",
1423                     GetMagickPrecision(),channel_distortion[GrayPixelChannel]);
1424                   if (image->alpha_trait != UndefinedPixelTrait)
1425                     (void) FormatLocaleFile(stderr,"    alpha: %.*g\n",
1426                       GetMagickPrecision(),
1427                       channel_distortion[AlphaPixelChannel]);
1428                   break;
1429                 }
1430               }
1431               (void) FormatLocaleFile(stderr,"    all: %.*g\n",
1432                 GetMagickPrecision(),channel_distortion[MaxPixelChannels]);
1433               break;
1434             }
1435             case MeanErrorPerPixelErrorMetric:
1436             {
1437               (void) FormatLocaleFile(stderr,"    %.*g (%.*g, %.*g)\n",
1438                 GetMagickPrecision(),channel_distortion[MaxPixelChannels],
1439                 GetMagickPrecision(),image->error.normalized_mean_error,
1440                 GetMagickPrecision(),image->error.normalized_maximum_error);
1441               break;
1442             }
1443             case UndefinedErrorMetric:
1444               break;
1445           }
1446           channel_distortion=(double *) RelinquishMagickMemory(
1447             channel_distortion);
1448           if (subimage_search != MagickFalse)
1449             (void) FormatLocaleFile(stderr,"   Offset: %.20g,%.20g\n",(double)
1450               difference_image->page.x,(double) difference_image->page.y);
1451         }
1452       status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
1453       if ((metadata != (char **) NULL) && (format != (char *) NULL))
1454         {
1455           char
1456             *text;
1457 
1458           text=InterpretImageProperties(image_info,difference_image,format,
1459             exception);
1460           if (text == (char *) NULL)
1461             ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1462               GetExceptionMessage(errno));
1463           (void) ConcatenateString(&(*metadata),text);
1464           text=DestroyString(text);
1465         }
1466       difference_image=DestroyImageList(difference_image);
1467     }
1468   DestroyCompare();
1469   if ((metric == NormalizedCrossCorrelationErrorMetric) ||
1470       (metric == UndefinedErrorMetric))
1471     {
1472       if (fabs(distortion-1.0) > CompareEpsilon)
1473         (void) SetImageOption(image_info,"compare:dissimilar","true");
1474     }
1475   else
1476     if (fabs(distortion) > CompareEpsilon)
1477       (void) SetImageOption(image_info,"compare:dissimilar","true");
1478   return(status != 0 ? MagickTrue : MagickFalse);
1479 }
1480