]> granicus.if.org Git - imagemagick/blob - wand/compare.c
(no commit message)
[imagemagick] / wand / 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-2010 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 "wand/studio.h"
45 #include "wand/MagickWand.h"
46 #include "wand/mogrify-private.h"
47 #include "magick/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 %  CompareImageCommand() 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 CompareImageCommand method is:
65 %
66 %      MagickBooleanType CompareImageCommand(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 RMSE 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       "-size geometry       width and height of image",
140       "-transparent-color color",
141       "                     transparent color",
142       "-type type           image type",
143       "-verbose             print detailed information about the image",
144       "-version             print version information",
145       "-virtual-pixel method",
146       "                     virtual pixel access method",
147       (char *) NULL
148     };
149
150   (void) printf("Version: %s\n",GetMagickVersion((size_t *) NULL));
151   (void) printf("Copyright: %s\n",GetMagickCopyright());
152   (void) printf("Features: %s\n\n",GetMagickFeatures());
153   (void) printf("Usage: %s [options ...] image reconstruct difference\n",
154     GetClientName());
155   (void) printf("\nImage Settings:\n");
156   for (p=settings; *p != (char *) NULL; p++)
157     (void) printf("  %s\n",*p);
158   (void) printf("\nMiscellaneous Options:\n");
159   for (p=miscellaneous; *p != (char *) NULL; p++)
160     (void) printf("  %s\n",*p);
161   (void) printf(
162     "\nBy default, the image format of `file' is determined by its magic\n");
163   (void) printf(
164     "number.  To specify a particular image format, precede the filename\n");
165   (void) printf(
166     "with an image format name and a colon (i.e. ps:image) or specify the\n");
167   (void) printf(
168     "image type as the filename suffix (i.e. image.ps).  Specify 'file' as\n");
169   (void) printf("'-' for standard input or output.\n");
170   return(MagickFalse);
171 }
172
173 WandExport MagickBooleanType CompareImageCommand(ImageInfo *image_info,
174   int argc,char **argv,char **metadata,ExceptionInfo *exception)
175 {
176 #define DefaultDissimilarityThreshold  0.31830988618379067154
177 #define DestroyCompare() \
178 { \
179   if (similarity_image != (Image *) NULL) \
180     similarity_image=DestroyImageList(similarity_image); \
181   if (difference_image != (Image *) NULL) \
182     difference_image=DestroyImageList(difference_image); \
183   DestroyImageStack(); \
184   for (i=0; i < (ssize_t) argc; i++) \
185     argv[i]=DestroyString(argv[i]); \
186   argv=(char **) RelinquishMagickMemory(argv); \
187 }
188 #define ThrowCompareException(asperity,tag,option) \
189 { \
190   if (exception->severity < (asperity)) \
191     (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \
192       "`%s'",option); \
193   DestroyCompare(); \
194   return(MagickFalse); \
195 }
196 #define ThrowCompareInvalidArgumentException(option,argument) \
197 { \
198   (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \
199     "InvalidArgument","`%s': %s",option,argument); \
200   DestroyCompare(); \
201   return(MagickFalse); \
202 }
203
204   char
205     *filename,
206     *option;
207
208   const char
209     *format;
210
211   ChannelType
212     channels;
213
214   double
215     dissimilarity_threshold,
216     distortion,
217     similarity_metric;
218
219   Image
220     *difference_image,
221     *image,
222     *reconstruct_image,
223     *similarity_image;
224
225   ImageStack
226     image_stack[MaxImageStackDepth+1];
227
228   ssize_t
229     j,
230     k;
231
232   MagickBooleanType
233     fire,
234     pend;
235
236   MagickStatusType
237     status;
238
239   MetricType
240     metric;
241
242   RectangleInfo
243     offset;
244
245   register ssize_t
246     i;
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           (void) fprintf(stdout,"Version: %s\n",
263             GetMagickVersion((size_t *) NULL));
264           (void) fprintf(stdout,"Copyright: %s\n",GetMagickCopyright());
265           (void) fprintf(stdout,"Features: %s\n\n",GetMagickFeatures());
266           return(MagickFalse);
267         }
268     }
269   if (argc < 3)
270     return(CompareUsage());
271   channels=AllChannels;
272   difference_image=NewImageList();
273   similarity_image=NewImageList();
274   dissimilarity_threshold=DefaultDissimilarityThreshold;
275   distortion=0.0;
276   format=(char *) NULL;
277   j=1;
278   k=0;
279   metric=UndefinedMetric;
280   NewImageStack();
281   option=(char *) NULL;
282   pend=MagickFalse;
283   reconstruct_image=NewImageList();
284   status=MagickTrue;
285   /*
286     Compare an image.
287   */
288   ReadCommandlLine(argc,&argv);
289   status=ExpandFilenames(&argc,&argv);
290   if (status == MagickFalse)
291     ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
292       GetExceptionMessage(errno));
293   for (i=1; i < (ssize_t) (argc-1); i++)
294   {
295     option=argv[i];
296     if (LocaleCompare(option,"(") == 0)
297       {
298         FireImageStack(MagickTrue,MagickTrue,pend);
299         if (k == MaxImageStackDepth)
300           ThrowCompareException(OptionError,"ParenthesisNestedTooDeeply",
301             option);
302         PushImageStack();
303         continue;
304       }
305     if (LocaleCompare(option,")") == 0)
306       {
307         FireImageStack(MagickTrue,MagickTrue,MagickTrue);
308         if (k == 0)
309           ThrowCompareException(OptionError,"UnableToParseExpression",option);
310         PopImageStack();
311         continue;
312       }
313     if (IsMagickOption(option) == MagickFalse)
314       {
315         Image
316           *images;
317
318         /*
319           Read input image.
320         */
321         FireImageStack(MagickFalse,MagickFalse,pend);
322         filename=argv[i];
323         if ((LocaleCompare(filename,"--") == 0) && (i < (ssize_t) (argc-1)))
324           filename=argv[++i];
325         (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
326         images=ReadImages(image_info,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=ParseMagickOption(MagickAlphaOptions,MagickFalse,argv[i]);
350             if (type < 0)
351               ThrowCompareException(OptionError,"UnrecognizedAlphaChannelType",
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             channels=(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=ParseMagickOption(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=ParseMagickOption(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=ParseMagickOption(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]);
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=ParseMagickOption(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=ParseMagickOption(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=strtod(argv[i],&p);
660             if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
661               ThrowCompareInvalidArgumentException(option,argv[i]);
662             break;
663           }
664         if (LocaleCompare("list",option+1) == 0)
665           {
666             ssize_t
667               list;
668
669             if (*option == '+')
670               break;
671             i++;
672             if (i == (ssize_t) argc)
673               ThrowCompareException(OptionError,"MissingArgument",option);
674             list=ParseMagickOption(MagickListOptions,MagickFalse,argv[i]);
675             if (list < 0)
676               ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
677             status=MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
678               argv+j,exception);
679             DestroyCompare();
680             return(status != 0 ? MagickFalse : MagickTrue);
681           }
682         if (LocaleCompare("log",option+1) == 0)
683           {
684             if (*option == '+')
685               break;
686             i++;
687             if ((i == (ssize_t) argc) || (strchr(argv[i],'%') == (char *) NULL))
688               ThrowCompareException(OptionError,"MissingArgument",option);
689             break;
690           }
691         if (LocaleCompare("lowlight-color",option+1) == 0)
692           {
693             if (*option == '+')
694               break;
695             i++;
696             if (i == (ssize_t) (argc-1))
697               ThrowCompareException(OptionError,"MissingArgument",option);
698             break;
699           }
700         ThrowCompareException(OptionError,"UnrecognizedOption",option)
701       }
702       case 'm':
703       {
704         if (LocaleCompare("matte",option+1) == 0)
705           break;
706         if (LocaleCompare("metric",option+1) == 0)
707           {
708             ssize_t
709               type;
710
711             if (*option == '+')
712               break;
713             i++;
714             if (i == (ssize_t) argc)
715               ThrowCompareException(OptionError,"MissingArgument",option);
716             type=ParseMagickOption(MagickMetricOptions,MagickTrue,argv[i]);
717             if (type < 0)
718               ThrowCompareException(OptionError,"UnrecognizedMetricType",
719                 argv[i]);
720             metric=(MetricType) type;
721             break;
722           }
723         if (LocaleCompare("monitor",option+1) == 0)
724           break;
725         ThrowCompareException(OptionError,"UnrecognizedOption",option)
726       }
727       case 'p':
728       {
729         if (LocaleCompare("passphrase",option+1) == 0)
730           {
731             if (*option == '+')
732               break;
733             i++;
734             if (i == (ssize_t) argc)
735               ThrowCompareException(OptionError,"MissingArgument",option);
736             break;
737           }
738         if (LocaleCompare("profile",option+1) == 0)
739           {
740             i++;
741             if (i == (ssize_t) (argc-1))
742               ThrowCompareException(OptionError,"MissingArgument",option);
743             break;
744           }
745         ThrowCompareException(OptionError,"UnrecognizedOption",option)
746       }
747       case 'q':
748       {
749         if (LocaleCompare("quality",option+1) == 0)
750           {
751             if (*option == '+')
752               break;
753             i++;
754             if (i == (ssize_t) (argc-1))
755               ThrowCompareException(OptionError,"MissingArgument",option);
756             if (IsGeometry(argv[i]) == MagickFalse)
757               ThrowCompareInvalidArgumentException(option,argv[i]);
758             break;
759           }
760         if (LocaleCompare("quantize",option+1) == 0)
761           {
762             ssize_t
763               colorspace;
764
765             if (*option == '+')
766               break;
767             i++;
768             if (i == (ssize_t) (argc-1))
769               ThrowCompareException(OptionError,"MissingArgument",option);
770             colorspace=ParseMagickOption(MagickColorspaceOptions,
771               MagickFalse,argv[i]);
772             if (colorspace < 0)
773               ThrowCompareException(OptionError,"UnrecognizedColorspace",
774                 argv[i]);
775             break;
776           }
777         if (LocaleCompare("quiet",option+1) == 0)
778           break;
779         ThrowCompareException(OptionError,"UnrecognizedOption",option)
780       }
781       case 'r':
782       {
783         if (LocaleCompare("regard-warnings",option+1) == 0)
784           break;
785         if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
786           {
787             respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
788             break;
789           }
790         ThrowCompareException(OptionError,"UnrecognizedOption",option)
791       }
792       case 's':
793       {
794         if (LocaleCompare("sampling-factor",option+1) == 0)
795           {
796             if (*option == '+')
797               break;
798             i++;
799             if (i == (ssize_t) argc)
800               ThrowCompareException(OptionError,"MissingArgument",option);
801             if (IsGeometry(argv[i]) == MagickFalse)
802               ThrowCompareInvalidArgumentException(option,argv[i]);
803             break;
804           }
805         if (LocaleCompare("seed",option+1) == 0)
806           {
807             if (*option == '+')
808               break;
809             i++;
810             if (i == (ssize_t) (argc-1))
811               ThrowCompareException(OptionError,"MissingArgument",option);
812             if (IsGeometry(argv[i]) == MagickFalse)
813               ThrowCompareInvalidArgumentException(option,argv[i]);
814             break;
815           }
816         if (LocaleCompare("set",option+1) == 0)
817           {
818             i++;
819             if (i == (ssize_t) argc)
820               ThrowCompareException(OptionError,"MissingArgument",option);
821             if (*option == '+')
822               break;
823             i++;
824             if (i == (ssize_t) argc)
825               ThrowCompareException(OptionError,"MissingArgument",option);
826             break;
827           }
828         if (LocaleCompare("size",option+1) == 0)
829           {
830             if (*option == '+')
831               break;
832             i++;
833             if (i == (ssize_t) argc)
834               ThrowCompareException(OptionError,"MissingArgument",option);
835             if (IsGeometry(argv[i]) == MagickFalse)
836               ThrowCompareInvalidArgumentException(option,argv[i]);
837             break;
838           }
839         ThrowCompareException(OptionError,"UnrecognizedOption",option)
840       }
841       case 't':
842       {
843         if (LocaleCompare("transparent-color",option+1) == 0)
844           {
845             if (*option == '+')
846               break;
847             i++;
848             if (i == (ssize_t) (argc-1))
849               ThrowCompareException(OptionError,"MissingArgument",option);
850             break;
851           }
852         if (LocaleCompare("type",option+1) == 0)
853           {
854             ssize_t
855               type;
856
857             if (*option == '+')
858               break;
859             i++;
860             if (i == (ssize_t) argc)
861               ThrowCompareException(OptionError,"MissingArgument",option);
862             type=ParseMagickOption(MagickTypeOptions,MagickFalse,argv[i]);
863             if (type < 0)
864               ThrowCompareException(OptionError,"UnrecognizedImageType",
865                 argv[i]);
866             break;
867           }
868         ThrowCompareException(OptionError,"UnrecognizedOption",option)
869       }
870       case 'v':
871       {
872         if (LocaleCompare("verbose",option+1) == 0)
873           break;
874         if ((LocaleCompare("version",option+1) == 0) ||
875             (LocaleCompare("-version",option+1) == 0))
876           {
877             (void) fprintf(stdout,"Version: %s\n",
878               GetMagickVersion((size_t *) NULL));
879             (void) fprintf(stdout,"Copyright: %s\n",GetMagickCopyright());
880             (void) fprintf(stdout,"Features: %s\n\n",GetMagickFeatures());
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=ParseMagickOption(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=ParseMagickOption(MagickImageListOptions,MagickFalse,option+1) < 0 ?
908       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   similarity_image=SimilarityImage(image,reconstruct_image,&offset,
924     &similarity_metric,exception);
925   if (similarity_metric > dissimilarity_threshold)
926     ThrowCompareException(ImageError,"ImagesTooDissimilar",image->filename);
927   if ((reconstruct_image->columns == image->columns) &&
928       (reconstruct_image->rows == image->rows))
929     difference_image=CompareImageChannels(image,reconstruct_image,channels,
930       metric,&distortion,exception);
931   else
932     if (similarity_image != (Image *) NULL)
933       {
934         Image
935           *composite_image;
936
937         /*
938           Determine if reconstructed image is a subimage of the image.
939         */
940         composite_image=CloneImage(image,0,0,MagickTrue,exception);
941         if (composite_image == (Image *) NULL)
942           difference_image=CompareImageChannels(image,reconstruct_image,
943             channels,metric,&distortion,exception);
944         else
945           {
946             (void) CompositeImage(composite_image,CopyCompositeOp,
947               reconstruct_image,offset.x,offset.y);
948             difference_image=CompareImageChannels(image,composite_image,
949               channels,metric,&distortion,exception);
950             if (difference_image != (Image *) NULL)
951               {
952                 difference_image->page.x=offset.x;
953                 difference_image->page.y=offset.y;
954               }
955             composite_image=DestroyImage(composite_image);
956           }
957         if (difference_image == (Image *) NULL)
958           similarity_image=DestroyImage(similarity_image);
959         else
960           {
961             AppendImageToList(&difference_image,similarity_image);
962             similarity_image=(Image *) NULL;
963           }
964       }
965   if (difference_image == (Image *) NULL)
966     status=0;
967   else
968     {
969       if (image_info->verbose != MagickFalse)
970         (void) IsImagesEqual(image,reconstruct_image);
971       if (*difference_image->magick == '\0')
972         (void) CopyMagickString(difference_image->magick,image->magick,
973           MaxTextExtent);
974       if (image_info->verbose == MagickFalse)
975         {
976           switch (metric)
977           {
978             case MeanAbsoluteErrorMetric:
979             case MeanSquaredErrorMetric:
980             case RootMeanSquaredErrorMetric:
981             case PeakAbsoluteErrorMetric:
982             {
983               (void) fprintf(stderr,"%g (%g)",QuantumRange*distortion,
984                 (double) distortion);
985               if ((reconstruct_image->columns != image->columns) ||
986                   (reconstruct_image->rows != image->rows))
987                 (void) fprintf(stderr," @ %.20g,%.20g",(double)
988                   difference_image->page.x,(double) difference_image->page.y);
989               (void) fprintf(stderr,"\n");
990               break;
991             }
992             case AbsoluteErrorMetric:
993             case PeakSignalToNoiseRatioMetric:
994             {
995               (void) fprintf(stderr,"%g",distortion);
996               if ((reconstruct_image->columns != image->columns) ||
997                   (reconstruct_image->rows != image->rows))
998                 (void) fprintf(stderr," @ %.20g,%.20g",(double)
999                   difference_image->page.x,(double) difference_image->page.y);
1000               (void) fprintf(stderr,"\n");
1001               break;
1002             }
1003             case MeanErrorPerPixelMetric:
1004             {
1005               (void) fprintf(stderr,"%g (%g, %g)",distortion,
1006                 image->error.normalized_mean_error,
1007                 image->error.normalized_maximum_error);
1008               if ((reconstruct_image->columns != image->columns) ||
1009                   (reconstruct_image->rows != image->rows))
1010                 (void) fprintf(stderr," @ %.20g,%.20g",(double)
1011                   difference_image->page.x,(double) difference_image->page.y);
1012               (void) fprintf(stderr,"\n");
1013               break;
1014             }
1015             case UndefinedMetric:
1016               break;
1017           }
1018         }
1019       else
1020         {
1021           double
1022             *channel_distortion;
1023
1024           channel_distortion=GetImageChannelDistortions(image,reconstruct_image,
1025             metric,&image->exception);
1026           (void) fprintf(stderr,"Image: %s\n",image->filename);
1027           if ((reconstruct_image->columns != image->columns) ||
1028               (reconstruct_image->rows != image->rows))
1029             (void) fprintf(stderr,"Offset: %.20g,%.20g\n",(double)
1030               difference_image->page.x,(double) difference_image->page.y);
1031           (void) fprintf(stderr,"  Channel distortion: %s\n",
1032             MagickOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1033           switch (metric)
1034           {
1035             case MeanAbsoluteErrorMetric:
1036             case MeanSquaredErrorMetric:
1037             case RootMeanSquaredErrorMetric:
1038             case PeakAbsoluteErrorMetric:
1039             {
1040               switch (image->colorspace)
1041               {
1042                 case RGBColorspace:
1043                 default:
1044                 {
1045                   (void) fprintf(stderr,"    red: %g (%g)\n",
1046                     QuantumRange*channel_distortion[RedChannel],
1047                     channel_distortion[RedChannel]);
1048                   (void) fprintf(stderr,"    green: %g (%g)\n",
1049                     QuantumRange*channel_distortion[GreenChannel],
1050                     channel_distortion[GreenChannel]);
1051                   (void) fprintf(stderr,"    blue: %g (%g)\n",
1052                     QuantumRange*channel_distortion[BlueChannel],
1053                     channel_distortion[BlueChannel]);
1054                   if (image->matte != MagickFalse)
1055                     (void) fprintf(stderr,"    alpha: %g (%g)\n",
1056                       QuantumRange*channel_distortion[OpacityChannel],
1057                       channel_distortion[OpacityChannel]);
1058                   break;
1059                 }
1060                 case CMYKColorspace:
1061                 {
1062                   (void) fprintf(stderr,"    cyan: %g (%g)\n",
1063                     QuantumRange*channel_distortion[CyanChannel],
1064                     channel_distortion[CyanChannel]);
1065                   (void) fprintf(stderr,"    magenta: %g (%g)\n",
1066                     QuantumRange*channel_distortion[MagentaChannel],
1067                     channel_distortion[MagentaChannel]);
1068                   (void) fprintf(stderr,"    yellow: %g (%g)\n",
1069                     QuantumRange*channel_distortion[YellowChannel],
1070                     channel_distortion[YellowChannel]);
1071                   (void) fprintf(stderr,"    black: %g (%g)\n",
1072                     QuantumRange*channel_distortion[BlackChannel],
1073                     channel_distortion[BlackChannel]);
1074                   if (image->matte != MagickFalse)
1075                     (void) fprintf(stderr,"    alpha: %g (%g)\n",
1076                       QuantumRange*channel_distortion[OpacityChannel],
1077                       channel_distortion[OpacityChannel]);
1078                   break;
1079                 }
1080                 case GRAYColorspace:
1081                 {
1082                   (void) fprintf(stderr,"    gray: %g (%g)\n",
1083                     QuantumRange*channel_distortion[GrayChannel],
1084                     channel_distortion[GrayChannel]);
1085                   if (image->matte != MagickFalse)
1086                     (void) fprintf(stderr,"    alpha: %g (%g)\n",
1087                       QuantumRange*channel_distortion[OpacityChannel],
1088                       channel_distortion[OpacityChannel]);
1089                   break;
1090                 }
1091               }
1092               (void) fprintf(stderr,"    all: %g (%g)\n",
1093                 QuantumRange*channel_distortion[AllChannels],
1094                 channel_distortion[AllChannels]);
1095               break;
1096             }
1097             case AbsoluteErrorMetric:
1098             case PeakSignalToNoiseRatioMetric:
1099             {
1100               switch (image->colorspace)
1101               {
1102                 case RGBColorspace:
1103                 default:
1104                 {
1105                   (void) fprintf(stderr,"    red: %g\n",
1106                     channel_distortion[RedChannel]);
1107                   (void) fprintf(stderr,"    green: %g\n",
1108                     channel_distortion[GreenChannel]);
1109                   (void) fprintf(stderr,"    blue: %g\n",
1110                     channel_distortion[BlueChannel]);
1111                   if (image->matte != MagickFalse)
1112                     (void) fprintf(stderr,"    alpha: %g\n",
1113                       channel_distortion[OpacityChannel]);
1114                   break;
1115                 }
1116                 case CMYKColorspace:
1117                 {
1118                   (void) fprintf(stderr,"    cyan: %g\n",
1119                     channel_distortion[CyanChannel]);
1120                   (void) fprintf(stderr,"    magenta: %g\n",
1121                     channel_distortion[MagentaChannel]);
1122                   (void) fprintf(stderr,"    yellow: %g\n",
1123                     channel_distortion[YellowChannel]);
1124                   (void) fprintf(stderr,"    black: %g\n",
1125                     channel_distortion[BlackChannel]);
1126                   if (image->matte != MagickFalse)
1127                     (void) fprintf(stderr,"    alpha: %g\n",
1128                       channel_distortion[OpacityChannel]);
1129                   break;
1130                 }
1131                 case GRAYColorspace:
1132                 {
1133                   (void) fprintf(stderr,"    gray: %g\n",
1134                     channel_distortion[GrayChannel]);
1135                   if (image->matte != MagickFalse)
1136                     (void) fprintf(stderr,"    alpha: %g\n",
1137                       channel_distortion[OpacityChannel]);
1138                   break;
1139                 }
1140               }
1141               (void) fprintf(stderr,"    all: %g\n",
1142                 channel_distortion[AllChannels]);
1143               break;
1144             }
1145             case MeanErrorPerPixelMetric:
1146             {
1147               (void) fprintf(stderr,"    %g (%g, %g)\n",
1148                 channel_distortion[AllChannels],
1149                 image->error.normalized_mean_error,
1150                 image->error.normalized_maximum_error);
1151               break;
1152             }
1153             case UndefinedMetric:
1154               break;
1155           }
1156           channel_distortion=(double *) RelinquishMagickMemory(
1157             channel_distortion);
1158         }
1159       status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
1160       if ((metadata != (char **) NULL) && (format != (char *) NULL))
1161         {
1162           char
1163             *text;
1164
1165           text=InterpretImageProperties(image_info,difference_image,format);
1166           if (text == (char *) NULL)
1167             ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1168               GetExceptionMessage(errno));
1169           (void) ConcatenateString(&(*metadata),text);
1170           (void) ConcatenateString(&(*metadata),"\n");
1171           text=DestroyString(text);
1172         }
1173       difference_image=DestroyImageList(difference_image);
1174     }
1175   DestroyCompare();
1176   return(status != 0 ? MagickTrue : MagickFalse);
1177 }