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