]> granicus.if.org Git - imagemagick/blob - MagickWand/compare.c
Use specific version of MagickCore in MagickWand.pc (#1638)
[imagemagick] / MagickWand / compare.c
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-2019 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 \f
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 \f
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
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(MagickFalse);
196 }
197
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   register 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           return(CompareUsage());
706         if (LocaleCompare("highlight-color",option+1) == 0)
707           {
708             if (*option == '+')
709               break;
710             i++;
711             if (i == (ssize_t) argc)
712               ThrowCompareException(OptionError,"MissingArgument",option);
713             break;
714           }
715         ThrowCompareException(OptionError,"UnrecognizedOption",option)
716       }
717       case 'i':
718       {
719         if (LocaleCompare("identify",option+1) == 0)
720           break;
721         if (LocaleCompare("interlace",option+1) == 0)
722           {
723             ssize_t
724               interlace;
725
726             if (*option == '+')
727               break;
728             i++;
729             if (i == (ssize_t) argc)
730               ThrowCompareException(OptionError,"MissingArgument",option);
731             interlace=ParseCommandOption(MagickInterlaceOptions,MagickFalse,
732               argv[i]);
733             if (interlace < 0)
734               ThrowCompareException(OptionError,"UnrecognizedInterlaceType",
735                 argv[i]);
736             break;
737           }
738         ThrowCompareException(OptionError,"UnrecognizedOption",option)
739       }
740       case 'l':
741       {
742         if (LocaleCompare("level",option+1) == 0)
743           {
744             i++;
745             if (i == (ssize_t) argc)
746               ThrowCompareException(OptionError,"MissingArgument",option);
747             if (IsGeometry(argv[i]) == MagickFalse)
748               ThrowCompareInvalidArgumentException(option,argv[i]);
749             break;
750           }
751         if (LocaleCompare("limit",option+1) == 0)
752           {
753             char
754               *p;
755
756             double
757               value;
758
759             ssize_t
760               resource;
761
762             if (*option == '+')
763               break;
764             i++;
765             if (i == (ssize_t) argc)
766               ThrowCompareException(OptionError,"MissingArgument",option);
767             resource=ParseCommandOption(MagickResourceOptions,MagickFalse,
768               argv[i]);
769             if (resource < 0)
770               ThrowCompareException(OptionError,"UnrecognizedResourceType",
771                 argv[i]);
772             i++;
773             if (i == (ssize_t) argc)
774               ThrowCompareException(OptionError,"MissingArgument",option);
775             value=StringToDouble(argv[i],&p);
776             (void) value;
777             if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
778               ThrowCompareInvalidArgumentException(option,argv[i]);
779             break;
780           }
781         if (LocaleCompare("list",option+1) == 0)
782           {
783             ssize_t
784               list;
785
786             if (*option == '+')
787               break;
788             i++;
789             if (i == (ssize_t) argc)
790               ThrowCompareException(OptionError,"MissingArgument",option);
791             list=ParseCommandOption(MagickListOptions,MagickFalse,argv[i]);
792             if (list < 0)
793               ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
794             status=MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
795               argv+j,exception);
796             DestroyCompare();
797             return(status == 0 ? MagickFalse : MagickTrue);
798           }
799         if (LocaleCompare("log",option+1) == 0)
800           {
801             if (*option == '+')
802               break;
803             i++;
804             if ((i == (ssize_t) argc) || (strchr(argv[i],'%') == (char *) NULL))
805               ThrowCompareException(OptionError,"MissingArgument",option);
806             break;
807           }
808         if (LocaleCompare("lowlight-color",option+1) == 0)
809           {
810             if (*option == '+')
811               break;
812             i++;
813             if (i == (ssize_t) argc)
814               ThrowCompareException(OptionError,"MissingArgument",option);
815             break;
816           }
817         ThrowCompareException(OptionError,"UnrecognizedOption",option)
818       }
819       case 'm':
820       {
821         if (LocaleCompare("matte",option+1) == 0)
822           break;
823         if (LocaleCompare("metric",option+1) == 0)
824           {
825             ssize_t
826               type;
827
828             if (*option == '+')
829               break;
830             i++;
831             if (i == (ssize_t) argc)
832               ThrowCompareException(OptionError,"MissingArgument",option);
833             type=ParseCommandOption(MagickMetricOptions,MagickTrue,argv[i]);
834             if (type < 0)
835               ThrowCompareException(OptionError,"UnrecognizedMetricType",
836                 argv[i]);
837             metric=(MetricType) type;
838             break;
839           }
840         if (LocaleCompare("monitor",option+1) == 0)
841           break;
842         ThrowCompareException(OptionError,"UnrecognizedOption",option)
843       }
844       case 'n':
845       {
846         if (LocaleCompare("negate",option+1) == 0)
847           break;
848         ThrowCompareException(OptionError,"UnrecognizedOption",option)
849       }
850       case 'p':
851       {
852         if (LocaleCompare("passphrase",option+1) == 0)
853           {
854             if (*option == '+')
855               break;
856             i++;
857             if (i == (ssize_t) argc)
858               ThrowCompareException(OptionError,"MissingArgument",option);
859             break;
860           }
861         if (LocaleCompare("precision",option+1) == 0)
862           {
863             if (*option == '+')
864               break;
865             i++;
866             if (i == (ssize_t) argc)
867               ThrowCompareException(OptionError,"MissingArgument",option);
868             if (IsGeometry(argv[i]) == MagickFalse)
869               ThrowCompareInvalidArgumentException(option,argv[i]);
870             break;
871           }
872         if (LocaleCompare("profile",option+1) == 0)
873           {
874             i++;
875             if (i == (ssize_t) argc)
876               ThrowCompareException(OptionError,"MissingArgument",option);
877             break;
878           }
879         ThrowCompareException(OptionError,"UnrecognizedOption",option)
880       }
881       case 'q':
882       {
883         if (LocaleCompare("quality",option+1) == 0)
884           {
885             if (*option == '+')
886               break;
887             i++;
888             if (i == (ssize_t) argc)
889               ThrowCompareException(OptionError,"MissingArgument",option);
890             if (IsGeometry(argv[i]) == MagickFalse)
891               ThrowCompareInvalidArgumentException(option,argv[i]);
892             break;
893           }
894         if (LocaleCompare("quantize",option+1) == 0)
895           {
896             ssize_t
897               colorspace;
898
899             if (*option == '+')
900               break;
901             i++;
902             if (i == (ssize_t) argc)
903               ThrowCompareException(OptionError,"MissingArgument",option);
904             colorspace=ParseCommandOption(MagickColorspaceOptions,
905               MagickFalse,argv[i]);
906             if (colorspace < 0)
907               ThrowCompareException(OptionError,"UnrecognizedColorspace",
908                 argv[i]);
909             break;
910           }
911         if (LocaleCompare("quiet",option+1) == 0)
912           break;
913         ThrowCompareException(OptionError,"UnrecognizedOption",option)
914       }
915       case 'r':
916       {
917         if (LocaleCompare("read-mask",option+1) == 0)
918           {
919             if (*option == '+')
920               break;
921             i++;
922             if (i == (ssize_t) argc)
923               ThrowCompareException(OptionError,"MissingArgument",option);
924             break;
925           }
926         if (LocaleCompare("regard-warnings",option+1) == 0)
927           break;
928         if (LocaleCompare("repage",option+1) == 0)
929           {
930             if (*option == '+')
931               break;
932             i++;
933             if (i == (ssize_t) argc)
934               ThrowCompareException(OptionError,"MissingArgument",option);
935             if (IsGeometry(argv[i]) == MagickFalse)
936               ThrowCompareInvalidArgumentException(option,argv[i]);
937             break;
938           }
939         if (LocaleCompare("resize",option+1) == 0)
940           {
941             if (*option == '+')
942               break;
943             i++;
944             if (i == (ssize_t) argc)
945               ThrowCompareException(OptionError,"MissingArgument",option);
946             if (IsGeometry(argv[i]) == MagickFalse)
947               ThrowCompareInvalidArgumentException(option,argv[i]);
948             break;
949           }
950         if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
951           {
952             respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
953             break;
954           }
955         if (LocaleCompare("rotate",option+1) == 0)
956           {
957             i++;
958             if (i == (ssize_t) argc)
959               ThrowCompareException(OptionError,"MissingArgument",option);
960             if (IsGeometry(argv[i]) == MagickFalse)
961               ThrowCompareInvalidArgumentException(option,argv[i]);
962             break;
963           }
964         ThrowCompareException(OptionError,"UnrecognizedOption",option)
965       }
966       case 's':
967       {
968         if (LocaleCompare("sampling-factor",option+1) == 0)
969           {
970             if (*option == '+')
971               break;
972             i++;
973             if (i == (ssize_t) argc)
974               ThrowCompareException(OptionError,"MissingArgument",option);
975             if (IsGeometry(argv[i]) == MagickFalse)
976               ThrowCompareInvalidArgumentException(option,argv[i]);
977             break;
978           }
979         if (LocaleCompare("seed",option+1) == 0)
980           {
981             if (*option == '+')
982               break;
983             i++;
984             if (i == (ssize_t) argc)
985               ThrowCompareException(OptionError,"MissingArgument",option);
986             if (IsGeometry(argv[i]) == MagickFalse)
987               ThrowCompareInvalidArgumentException(option,argv[i]);
988             break;
989           }
990         if (LocaleCompare("separate",option+1) == 0)
991           break;
992         if (LocaleCompare("set",option+1) == 0)
993           {
994             i++;
995             if (i == (ssize_t) argc)
996               ThrowCompareException(OptionError,"MissingArgument",option);
997             if (*option == '+')
998               break;
999             i++;
1000             if (i == (ssize_t) argc)
1001               ThrowCompareException(OptionError,"MissingArgument",option);
1002             break;
1003           }
1004         if (LocaleCompare("sigmoidal-contrast",option+1) == 0)
1005           {
1006             i++;
1007             if (i == (ssize_t) argc)
1008               ThrowCompareException(OptionError,"MissingArgument",option);
1009             if (IsGeometry(argv[i]) == MagickFalse)
1010               ThrowCompareInvalidArgumentException(option,argv[i]);
1011             break;
1012           }
1013         if (LocaleCompare("similarity-threshold",option+1) == 0)
1014           {
1015             if (*option == '+')
1016               break;
1017             i++;
1018             if (i == (ssize_t) argc)
1019               ThrowCompareException(OptionError,"MissingArgument",option);
1020             if (IsGeometry(argv[i]) == MagickFalse)
1021               ThrowCompareInvalidArgumentException(option,argv[i]);
1022             if (*option == '+')
1023               similarity_threshold=DefaultSimilarityThreshold;
1024             else
1025               similarity_threshold=StringToDouble(argv[i],(char **) NULL);
1026             break;
1027           }
1028         if (LocaleCompare("size",option+1) == 0)
1029           {
1030             if (*option == '+')
1031               break;
1032             i++;
1033             if (i == (ssize_t) argc)
1034               ThrowCompareException(OptionError,"MissingArgument",option);
1035             if (IsGeometry(argv[i]) == MagickFalse)
1036               ThrowCompareInvalidArgumentException(option,argv[i]);
1037             break;
1038           }
1039         if (LocaleCompare("subimage-search",option+1) == 0)
1040           {
1041             if (*option == '+')
1042               {
1043                 subimage_search=MagickFalse;
1044                 break;
1045               }
1046             subimage_search=MagickTrue;
1047             break;
1048           }
1049         if (LocaleCompare("synchronize",option+1) == 0)
1050           break;
1051         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1052       }
1053       case 't':
1054       {
1055         if (LocaleCompare("taint",option+1) == 0)
1056           break;
1057         if (LocaleCompare("transparent-color",option+1) == 0)
1058           {
1059             if (*option == '+')
1060               break;
1061             i++;
1062             if (i == (ssize_t) argc)
1063               ThrowCompareException(OptionError,"MissingArgument",option);
1064             break;
1065           }
1066         if (LocaleCompare("trim",option+1) == 0)
1067           break;
1068         if (LocaleCompare("type",option+1) == 0)
1069           {
1070             ssize_t
1071               type;
1072
1073             if (*option == '+')
1074               break;
1075             i++;
1076             if (i == (ssize_t) argc)
1077               ThrowCompareException(OptionError,"MissingArgument",option);
1078             type=ParseCommandOption(MagickTypeOptions,MagickFalse,argv[i]);
1079             if (type < 0)
1080               ThrowCompareException(OptionError,"UnrecognizedImageType",
1081                 argv[i]);
1082             break;
1083           }
1084         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1085       }
1086       case 'v':
1087       {
1088         if (LocaleCompare("verbose",option+1) == 0)
1089           break;
1090         if ((LocaleCompare("version",option+1) == 0) ||
1091             (LocaleCompare("-version",option+1) == 0))
1092           {
1093             ListMagickVersion(stdout);
1094             break;
1095           }
1096         if (LocaleCompare("virtual-pixel",option+1) == 0)
1097           {
1098             ssize_t
1099               method;
1100
1101             if (*option == '+')
1102               break;
1103             i++;
1104             if (i == (ssize_t) argc)
1105               ThrowCompareException(OptionError,"MissingArgument",option);
1106             method=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
1107               argv[i]);
1108             if (method < 0)
1109               ThrowCompareException(OptionError,
1110                 "UnrecognizedVirtualPixelMethod",argv[i]);
1111             break;
1112           }
1113         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1114       }
1115       case 'w':
1116       {
1117         if (LocaleCompare("write",option+1) == 0)
1118           {
1119             i++;
1120             if (i == (ssize_t) argc)
1121               ThrowCompareException(OptionError,"MissingArgument",option);
1122             break;
1123           }
1124         if (LocaleCompare("write-mask",option+1) == 0)
1125           {
1126             if (*option == '+')
1127               break;
1128             i++;
1129             if (i == (ssize_t) argc)
1130               ThrowCompareException(OptionError,"MissingArgument",option);
1131             break;
1132           }
1133         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1134       }
1135       case '?':
1136         break;
1137       default:
1138         ThrowCompareException(OptionError,"UnrecognizedOption",option)
1139     }
1140     fire=(GetCommandOptionFlags(MagickCommandOptions,MagickFalse,option) &
1141       FireOptionFlag) == 0 ?  MagickFalse : MagickTrue;
1142     if (fire != MagickFalse)
1143       FireImageStack(MagickTrue,MagickTrue,MagickTrue);
1144   }
1145   if (k != 0)
1146     ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
1147   if (i-- != (ssize_t) (argc-1))
1148     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1149   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1150     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1151   FinalizeImageSettings(image_info,image,MagickTrue);
1152   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1153     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1154   image=GetImageFromList(image,0);
1155   reconstruct_image=GetImageFromList(image,1);
1156   offset.x=0;
1157   offset.y=0;
1158   if (subimage_search != MagickFalse)
1159     {
1160       similarity_image=SimilarityImage(image,reconstruct_image,metric,
1161         similarity_threshold,&offset,&similarity_metric,exception);
1162       if (similarity_metric > dissimilarity_threshold)
1163         ThrowCompareException(ImageError,"ImagesTooDissimilar",image->filename);
1164     }
1165   if ((reconstruct_image->columns == image->columns) &&
1166        (reconstruct_image->rows == image->rows))
1167     difference_image=CompareImages(image,reconstruct_image,metric,&distortion,
1168       exception);
1169   else
1170     if (similarity_image == (Image *) NULL)
1171       difference_image=CompareImages(image,reconstruct_image,metric,&distortion,
1172         exception);
1173     else
1174       {
1175         Image
1176           *composite_image;
1177
1178         /*
1179           Determine if reconstructed image is a subimage of the image.
1180         */
1181         composite_image=CloneImage(image,0,0,MagickTrue,exception);
1182         if (composite_image == (Image *) NULL)
1183           difference_image=CompareImages(image,reconstruct_image,metric,
1184             &distortion,exception);
1185         else
1186           {
1187             Image
1188               *distort_image;
1189
1190             RectangleInfo
1191               page;
1192
1193             (void) CompositeImage(composite_image,reconstruct_image,
1194               CopyCompositeOp,MagickTrue,offset.x,offset.y,exception);
1195             difference_image=CompareImages(image,composite_image,metric,
1196               &distortion,exception);
1197             if (difference_image != (Image *) NULL)
1198               {
1199                 difference_image->page.x=offset.x;
1200                 difference_image->page.y=offset.y;
1201               }
1202             composite_image=DestroyImage(composite_image);
1203             page.width=reconstruct_image->columns;
1204             page.height=reconstruct_image->rows;
1205             page.x=offset.x;
1206             page.y=offset.y;
1207             distort_image=CropImage(image,&page,exception);
1208             if (distort_image != (Image *) NULL)
1209               {
1210                 Image
1211                   *sans_image;
1212
1213                 sans_image=CompareImages(distort_image,reconstruct_image,metric,
1214                   &distortion,exception);
1215                 distort_image=DestroyImage(distort_image);
1216                 if (sans_image != (Image *) NULL)
1217                   sans_image=DestroyImage(sans_image);
1218               }
1219           }
1220         if (difference_image != (Image *) NULL)
1221           {
1222             AppendImageToList(&difference_image,similarity_image);
1223             similarity_image=(Image *) NULL;
1224           }
1225       }
1226   if (difference_image == (Image *) NULL)
1227     status=0;
1228   else
1229     {
1230       if (image_info->verbose != MagickFalse)
1231         (void) SetImageColorMetric(image,reconstruct_image,exception);
1232       if (*difference_image->magick == '\0')
1233         (void) CopyMagickString(difference_image->magick,image->magick,
1234           MagickPathExtent);
1235       if (image_info->verbose == MagickFalse)
1236         {
1237           switch (metric)
1238           {
1239             case FuzzErrorMetric:
1240             case MeanAbsoluteErrorMetric:
1241             case MeanSquaredErrorMetric:
1242             case PeakAbsoluteErrorMetric:
1243             case RootMeanSquaredErrorMetric:
1244             {
1245               (void) FormatLocaleFile(stderr,"%.*g (%.*g)",GetMagickPrecision(),
1246                 QuantumRange*distortion,GetMagickPrecision(),
1247                 (double) distortion);
1248               break;
1249             }
1250             case AbsoluteErrorMetric:
1251             case NormalizedCrossCorrelationErrorMetric:
1252             case PeakSignalToNoiseRatioErrorMetric:
1253             case PerceptualHashErrorMetric:
1254             case StructuralSimilarityErrorMetric:
1255             case StructuralDissimilarityErrorMetric:
1256             {
1257               (void) FormatLocaleFile(stderr,"%.*g",GetMagickPrecision(),
1258                 distortion);
1259               break;
1260             }
1261             case MeanErrorPerPixelErrorMetric:
1262             {
1263               (void) FormatLocaleFile(stderr,"%.*g (%.*g, %.*g)",
1264                 GetMagickPrecision(),distortion,
1265                 GetMagickPrecision(),image->error.normalized_mean_error,
1266                 GetMagickPrecision(),image->error.normalized_maximum_error);
1267               break;
1268             }
1269             case UndefinedErrorMetric:
1270               break;
1271           }
1272           if (subimage_search != MagickFalse)
1273             (void) FormatLocaleFile(stderr," @ %.20g,%.20g",
1274               (double) difference_image->page.x,
1275               (double) difference_image->page.y);
1276         }
1277       else
1278         {
1279           double
1280             *channel_distortion;
1281
1282           channel_distortion=GetImageDistortions(image,reconstruct_image,
1283             metric,exception);
1284           (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename);
1285           if ((reconstruct_image->columns != image->columns) ||
1286               (reconstruct_image->rows != image->rows))
1287             (void) FormatLocaleFile(stderr,"Offset: %.20g,%.20g\n",(double)
1288               difference_image->page.x,(double) difference_image->page.y);
1289           (void) FormatLocaleFile(stderr,"  Channel distortion: %s\n",
1290             CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1291           switch (metric)
1292           {
1293             case FuzzErrorMetric:
1294             case MeanAbsoluteErrorMetric:
1295             case MeanSquaredErrorMetric:
1296             case PeakAbsoluteErrorMetric:
1297             case RootMeanSquaredErrorMetric:
1298             {
1299               switch (image->colorspace)
1300               {
1301                 case RGBColorspace:
1302                 default:
1303                 {
1304                   (void) FormatLocaleFile(stderr,"    red: %.*g (%.*g)\n",
1305                     GetMagickPrecision(),QuantumRange*
1306                     channel_distortion[RedPixelChannel],GetMagickPrecision(),
1307                     channel_distortion[RedPixelChannel]);
1308                   (void) FormatLocaleFile(stderr,"    green: %.*g (%.*g)\n",
1309                     GetMagickPrecision(),QuantumRange*
1310                     channel_distortion[GreenPixelChannel],GetMagickPrecision(),
1311                     channel_distortion[GreenPixelChannel]);
1312                   (void) FormatLocaleFile(stderr,"    blue: %.*g (%.*g)\n",
1313                     GetMagickPrecision(),QuantumRange*
1314                     channel_distortion[BluePixelChannel],GetMagickPrecision(),
1315                     channel_distortion[BluePixelChannel]);
1316                   if (image->alpha_trait != UndefinedPixelTrait)
1317                     (void) FormatLocaleFile(stderr,"    alpha: %.*g (%.*g)\n",
1318                       GetMagickPrecision(),QuantumRange*
1319                       channel_distortion[AlphaPixelChannel],
1320                       GetMagickPrecision(),
1321                       channel_distortion[AlphaPixelChannel]);
1322                   break;
1323                 }
1324                 case CMYKColorspace:
1325                 {
1326                   (void) FormatLocaleFile(stderr,"    cyan: %.*g (%.*g)\n",
1327                     GetMagickPrecision(),QuantumRange*
1328                     channel_distortion[CyanPixelChannel],GetMagickPrecision(),
1329                     channel_distortion[CyanPixelChannel]);
1330                   (void) FormatLocaleFile(stderr,"    magenta: %.*g (%.*g)\n",
1331                     GetMagickPrecision(),QuantumRange*
1332                     channel_distortion[MagentaPixelChannel],
1333                     GetMagickPrecision(),
1334                     channel_distortion[MagentaPixelChannel]);
1335                   (void) FormatLocaleFile(stderr,"    yellow: %.*g (%.*g)\n",
1336                     GetMagickPrecision(),QuantumRange*
1337                     channel_distortion[YellowPixelChannel],GetMagickPrecision(),
1338                     channel_distortion[YellowPixelChannel]);
1339                   (void) FormatLocaleFile(stderr,"    black: %.*g (%.*g)\n",
1340                     GetMagickPrecision(),QuantumRange*
1341                     channel_distortion[BlackPixelChannel],GetMagickPrecision(),
1342                     channel_distortion[BlackPixelChannel]);
1343                   if (image->alpha_trait != UndefinedPixelTrait)
1344                     (void) FormatLocaleFile(stderr,"    alpha: %.*g (%.*g)\n",
1345                       GetMagickPrecision(),QuantumRange*
1346                       channel_distortion[AlphaPixelChannel],
1347                       GetMagickPrecision(),
1348                       channel_distortion[AlphaPixelChannel]);
1349                   break;
1350                 }
1351                 case LinearGRAYColorspace:
1352                 case GRAYColorspace:
1353                 {
1354                   (void) FormatLocaleFile(stderr,"    gray: %.*g (%.*g)\n",
1355                     GetMagickPrecision(),QuantumRange*
1356                     channel_distortion[GrayPixelChannel],GetMagickPrecision(),
1357                     channel_distortion[GrayPixelChannel]);
1358                   if (image->alpha_trait != UndefinedPixelTrait)
1359                     (void) FormatLocaleFile(stderr,"    alpha: %.*g (%.*g)\n",
1360                       GetMagickPrecision(),QuantumRange*
1361                       channel_distortion[AlphaPixelChannel],
1362                       GetMagickPrecision(),
1363                       channel_distortion[AlphaPixelChannel]);
1364                   break;
1365                 }
1366               }
1367               (void) FormatLocaleFile(stderr,"    all: %.*g (%.*g)\n",
1368                 GetMagickPrecision(),QuantumRange*
1369                 channel_distortion[MaxPixelChannels],GetMagickPrecision(),
1370                 channel_distortion[MaxPixelChannels]);
1371               break;
1372             }
1373             case AbsoluteErrorMetric:
1374             case NormalizedCrossCorrelationErrorMetric:
1375             case PeakSignalToNoiseRatioErrorMetric:
1376             case PerceptualHashErrorMetric:
1377             case StructuralSimilarityErrorMetric:
1378             case StructuralDissimilarityErrorMetric:
1379             {
1380               switch (image->colorspace)
1381               {
1382                 case RGBColorspace:
1383                 default:
1384                 {
1385                   (void) FormatLocaleFile(stderr,"    red: %.*g\n",
1386                     GetMagickPrecision(),channel_distortion[RedPixelChannel]);
1387                   (void) FormatLocaleFile(stderr,"    green: %.*g\n",
1388                     GetMagickPrecision(),channel_distortion[GreenPixelChannel]);
1389                   (void) FormatLocaleFile(stderr,"    blue: %.*g\n",
1390                     GetMagickPrecision(),channel_distortion[BluePixelChannel]);
1391                   if (image->alpha_trait != UndefinedPixelTrait)
1392                     (void) FormatLocaleFile(stderr,"    alpha: %.*g\n",
1393                       GetMagickPrecision(),
1394                        channel_distortion[AlphaPixelChannel]);
1395                   break;
1396                 }
1397                 case CMYKColorspace:
1398                 {
1399                   (void) FormatLocaleFile(stderr,"    cyan: %.*g\n",
1400                     GetMagickPrecision(),channel_distortion[CyanPixelChannel]);
1401                   (void) FormatLocaleFile(stderr,"    magenta: %.*g\n",
1402                     GetMagickPrecision(),
1403                     channel_distortion[MagentaPixelChannel]);
1404                   (void) FormatLocaleFile(stderr,"    yellow: %.*g\n",
1405                     GetMagickPrecision(),
1406                     channel_distortion[YellowPixelChannel]);
1407                   (void) FormatLocaleFile(stderr,"    black: %.*g\n",
1408                     GetMagickPrecision(),
1409                     channel_distortion[BlackPixelChannel]);
1410                   if (image->alpha_trait != UndefinedPixelTrait)
1411                     (void) FormatLocaleFile(stderr,"    alpha: %.*g\n",
1412                       GetMagickPrecision(),
1413                       channel_distortion[AlphaPixelChannel]);
1414                   break;
1415                 }
1416                 case LinearGRAYColorspace:
1417                 case GRAYColorspace:
1418                 {
1419                   (void) FormatLocaleFile(stderr,"    gray: %.*g\n",
1420                     GetMagickPrecision(),channel_distortion[GrayPixelChannel]);
1421                   if (image->alpha_trait != UndefinedPixelTrait)
1422                     (void) FormatLocaleFile(stderr,"    alpha: %.*g\n",
1423                       GetMagickPrecision(),
1424                       channel_distortion[AlphaPixelChannel]);
1425                   break;
1426                 }
1427               }
1428               (void) FormatLocaleFile(stderr,"    all: %.*g\n",
1429                 GetMagickPrecision(),channel_distortion[MaxPixelChannels]);
1430               break;
1431             }
1432             case MeanErrorPerPixelErrorMetric:
1433             {
1434               (void) FormatLocaleFile(stderr,"    %.*g (%.*g, %.*g)\n",
1435                 GetMagickPrecision(),channel_distortion[MaxPixelChannels],
1436                 GetMagickPrecision(),image->error.normalized_mean_error,
1437                 GetMagickPrecision(),image->error.normalized_maximum_error);
1438               break;
1439             }
1440             case UndefinedErrorMetric:
1441               break;
1442           }
1443           channel_distortion=(double *) RelinquishMagickMemory(
1444             channel_distortion);
1445           if (subimage_search != MagickFalse)
1446             (void) FormatLocaleFile(stderr,"   Offset: %.20g,%.20g\n",(double)
1447               difference_image->page.x,(double) difference_image->page.y);
1448         }
1449       status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
1450       if ((metadata != (char **) NULL) && (format != (char *) NULL))
1451         {
1452           char
1453             *text;
1454
1455           text=InterpretImageProperties(image_info,difference_image,format,
1456             exception);
1457           if (text == (char *) NULL)
1458             ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1459               GetExceptionMessage(errno));
1460           (void) ConcatenateString(&(*metadata),text);
1461           text=DestroyString(text);
1462         }
1463       difference_image=DestroyImageList(difference_image);
1464     }
1465   DestroyCompare();
1466   if ((metric == NormalizedCrossCorrelationErrorMetric) ||
1467       (metric == UndefinedErrorMetric))
1468     {
1469       if (fabs(distortion-1.0) > CompareEpsilon)
1470         (void) SetImageOption(image_info,"compare:dissimilar","true");
1471     }
1472   else
1473     if (fabs(distortion) > CompareEpsilon)
1474       (void) SetImageOption(image_info,"compare:dissimilar","true");
1475   return(status != 0 ? MagickTrue : MagickFalse);
1476 }