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