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