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