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