]> 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-2009 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 \f
48 /*
49 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50 %                                                                             %
51 %                                                                             %
52 %                                                                             %
53 %   C o m p a r e I m a g e C o m m a n d                                     %
54 %                                                                             %
55 %                                                                             %
56 %                                                                             %
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58 %
59 %  CompareImageCommand() compares two images and returns the difference between
60 %  them as a distortion metric and as a new image visually annotating their
61 %  differences.
62 %
63 %  The format of the CompareImageCommand method is:
64 %
65 %      MagickBooleanType CompareImageCommand(ImageInfo *image_info,int argc,
66 %        char **argv,char **metadata,ExceptionInfo *exception)
67 %
68 %  A description of each parameter follows:
69 %
70 %    o image_info: the image info.
71 %
72 %    o argc: the number of elements in the argument vector.
73 %
74 %    o argv: A text array containing the command line arguments.
75 %
76 %    o metadata: any metadata is returned here.
77 %
78 %    o exception: return any errors or warnings in this structure.
79 %
80 */
81
82 static MagickBooleanType CompareUsage(void)
83 {
84   const char
85     **p;
86
87   static const char
88     *miscellaneous[]=
89     {
90       "-debug events        display copious debugging information",
91       "-help                print program options",
92       "-list type           print a list of supported option arguments",
93       "-log format          format of debugging information",
94       (char *) NULL
95     },
96     *settings[]=
97     {
98       "-alpha option        on, activate, off, deactivate, set, opaque, copy",
99       "                     transparent, extract, background, or shape",
100       "-authenticate password",
101       "                     decipher image with this password",
102       "-channel type        apply option to select image channels",
103       "-colorspace type     alternate image colorspace",
104       "-compose operator    set image composite operator",
105       "-compress type       type of pixel compression when writing the image",
106       "-decipher filename   convert cipher pixels to plain pixels",
107       "-define format:option",
108       "                     define one or more image format options",
109       "-density geometry    horizontal and vertical density of the image",
110       "-depth value         image depth",
111       "-dissimilarity-threshold value",
112       "                     maximum RMSE for (sub)image match",
113       "-encipher filename   convert plain pixels to cipher pixels",
114       "-extract geometry    extract area from image",
115       "-format \"string\"     output formatted image characteristics",
116       "-fuzz distance       colors within this distance are considered equal",
117       "-highlight-color color",
118       "                     empasize pixel differences with this color",
119       "-identify            identify the format and characteristics of the image",
120       "-interlace type      type of image interlacing scheme",
121       "-limit type value    pixel cache resource limit",
122       "-lowlight-color color",
123       "                     de-emphasize pixel differences with this color",
124       "-metric type         measure differences between images with this metric",
125       "-monitor             monitor progress",
126       "-passphrase filename get the passphrase from this file",
127       "-profile filename    add, delete, or apply an image profile",
128       "-quality value       JPEG/MIFF/PNG compression level",
129       "-quiet               suppress all warning messages",
130       "-quantize colorspace reduce colors in this colorspace",
131       "-regard-warnings     pay attention to warning messages",
132       "-respect-parentheses settings remain in effect until parenthesis boundary",
133       "-sampling-factor geometry",
134       "                     horizontal and vertical sampling factor",
135       "-seed value          seed a new sequence of pseudo-random numbers",
136       "-set attribute value set an image attribute",
137       "-quality value       JPEG/MIFF/PNG compression level",
138       "-size geometry       width and height of image",
139       "-transparent-color color",
140       "                     transparent color",
141       "-type type           image type",
142       "-verbose             print detailed information about the image",
143       "-version             print version information",
144       "-virtual-pixel method",
145       "                     virtual pixel access method",
146       (char *) NULL
147     };
148
149   (void) printf("Version: %s\n",GetMagickVersion((unsigned long *) NULL));
150   (void) printf("Copyright: %s\n\n",GetMagickCopyright());
151   (void) printf("Usage: %s [options ...] image reconstruct difference\n",
152     GetClientName());
153   (void) printf("\nImage Settings:\n");
154   for (p=settings; *p != (char *) NULL; p++)
155     (void) printf("  %s\n",*p);
156   (void) printf("\nMiscellaneous Options:\n");
157   for (p=miscellaneous; *p != (char *) NULL; p++)
158     (void) printf("  %s\n",*p);
159   (void) printf(
160     "\nBy default, the image format of `file' is determined by its magic\n");
161   (void) printf(
162     "number.  To specify a particular image format, precede the filename\n");
163   (void) printf(
164     "with an image format name and a colon (i.e. ps:image) or specify the\n");
165   (void) printf(
166     "image type as the filename suffix (i.e. image.ps).  Specify 'file' as\n");
167   (void) printf("'-' for standard input or output.\n");
168   return(MagickFalse);
169 }
170
171 WandExport MagickBooleanType CompareImageCommand(ImageInfo *image_info,
172   int argc,char **argv,char **metadata,ExceptionInfo *exception)
173 {
174 #define DefaultDissimilarityThreshold  0.2
175 #define DestroyCompare() \
176 { \
177   if (similarity_image != (Image *) NULL) \
178     similarity_image=DestroyImageList(similarity_image); \
179   if (difference_image != (Image *) NULL) \
180     difference_image=DestroyImageList(difference_image); \
181   DestroyImageStack(); \
182   for (i=0; i < (long) argc; i++) \
183     argv[i]=DestroyString(argv[i]); \
184   argv=(char **) RelinquishMagickMemory(argv); \
185 }
186 #define ThrowCompareException(asperity,tag,option) \
187 { \
188   if (exception->severity < (asperity)) \
189     (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \
190       "`%s'",option); \
191   DestroyCompare(); \
192   return(MagickFalse); \
193 }
194 #define ThrowCompareInvalidArgumentException(option,argument) \
195 { \
196   (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \
197     "InvalidArgument","`%s': %s",option,argument); \
198   DestroyCompare(); \
199   return(MagickFalse); \
200 }
201
202   char
203     *filename,
204     *option;
205
206   const char
207     *format;
208
209   ChannelType
210     channels;
211
212   double
213     dissimilarity_threshold,
214     distortion,
215     similarity_metric;
216
217   Image
218     *difference_image,
219     *image,
220     *reconstruct_image,
221     *similarity_image;
222
223   ImageStack
224     image_stack[MaxImageStackDepth+1];
225
226   long
227     j,
228     k;
229
230   MagickBooleanType
231     fire,
232     pend;
233
234   MagickStatusType
235     status;
236
237   MetricType
238     metric;
239
240   RectangleInfo
241     offset;
242
243   register long
244     i;
245
246   /*
247     Set defaults.
248   */
249   assert(image_info != (ImageInfo *) NULL);
250   assert(image_info->signature == MagickSignature);
251   if (image_info->debug != MagickFalse)
252     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
253   assert(exception != (ExceptionInfo *) NULL);
254   if (argc == 2)
255     {
256       option=argv[1];
257       if ((LocaleCompare("version",option+1) == 0) ||
258           (LocaleCompare("-version",option+1) == 0))
259         {
260           (void) fprintf(stdout,"Version: %s\n",
261             GetMagickVersion((unsigned long *) NULL));
262           (void) fprintf(stdout,"Copyright: %s\n\n",GetMagickCopyright());
263           return(MagickFalse);
264         }
265     }
266   if (argc < 3)
267     {
268       (void) CompareUsage();
269       return(MagickTrue);
270     }
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 < (long) (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 < (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             long
342               type;
343
344             if (*option == '+')
345               break;
346             i++;
347             if (i == (long) 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 == (long) 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 == (long) 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             long
382               channel;
383
384             if (*option == '+')
385               break;
386             i++;
387             if (i == (long) (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             long
399               colorspace;
400
401             if (*option == '+')
402               break;
403             i++;
404             if (i == (long) (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             long
416               compose;
417
418             if (*option == '+')
419               break;
420             i++;
421             if (i == (long) 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             long
433               compress;
434
435             if (*option == '+')
436               break;
437             i++;
438             if (i == (long) (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         ThrowCompareException(OptionError,"UnrecognizedOption",option)
448       }
449       case 'd':
450       {
451         if (LocaleCompare("debug",option+1) == 0)
452           {
453             LogEventType
454               event_mask;
455
456             if (*option == '+')
457               break;
458             i++;
459             if (i == (long) argc)
460               ThrowCompareException(OptionError,"MissingArgument",option);
461             event_mask=SetLogEventMask(argv[i]);
462             if (event_mask == UndefinedEvents)
463               ThrowCompareException(OptionError,"UnrecognizedEventType",
464                 argv[i]);
465             break;
466           }
467         if (LocaleCompare("decipher",option+1) == 0)
468           {
469             if (*option == '+')
470               break;
471             i++;
472             if (i == (long) (argc-1))
473               ThrowCompareException(OptionError,"MissingArgument",option);
474             break;
475           }
476         if (LocaleCompare("define",option+1) == 0)
477           {
478             i++;
479             if (i == (long) argc)
480               ThrowCompareException(OptionError,"MissingArgument",option);
481             if (*option == '+')
482               {
483                 const char
484                   *define;
485
486                 define=GetImageOption(image_info,argv[i]);
487                 if (define == (const char *) NULL)
488                   ThrowCompareException(OptionError,"NoSuchOption",argv[i]);
489                 break;
490               }
491             break;
492           }
493         if (LocaleCompare("density",option+1) == 0)
494           {
495             if (*option == '+')
496               break;
497             i++;
498             if (i == (long) argc)
499               ThrowCompareException(OptionError,"MissingArgument",option);
500             if (IsGeometry(argv[i]) == MagickFalse)
501               ThrowCompareInvalidArgumentException(option,argv[i]);
502             break;
503           }
504         if (LocaleCompare("depth",option+1) == 0)
505           {
506             if (*option == '+')
507               break;
508             i++;
509             if (i == (long) argc)
510               ThrowCompareException(OptionError,"MissingArgument",option);
511             if (IsGeometry(argv[i]) == MagickFalse)
512               ThrowCompareInvalidArgumentException(option,argv[i]);
513             break;
514           }
515         if (LocaleCompare("dissimilarity-threshold",option+1) == 0)
516           {
517             if (*option == '+')
518               break;
519             i++;
520             if (i == (long) argc)
521               ThrowCompareException(OptionError,"MissingArgument",option);
522             if (IsGeometry(argv[i]) == MagickFalse)
523               ThrowCompareInvalidArgumentException(option,argv[i]);
524             if (*option == '+')
525               dissimilarity_threshold=DefaultDissimilarityThreshold;
526             else
527               dissimilarity_threshold=atof(argv[i]);
528             break;
529           }
530         ThrowCompareException(OptionError,"UnrecognizedOption",option)
531       }
532       case 'e':
533       {
534         if (LocaleCompare("encipher",option+1) == 0)
535           {
536             if (*option == '+')
537               break;
538             i++;
539             if (i == (long) (argc-1))
540               ThrowCompareException(OptionError,"MissingArgument",option);
541             break;
542           }
543         if (LocaleCompare("extract",option+1) == 0)
544           {
545             if (*option == '+')
546               break;
547             i++;
548             if (i == (long) (argc-1))
549               ThrowCompareException(OptionError,"MissingArgument",option);
550             if (IsGeometry(argv[i]) == MagickFalse)
551               ThrowCompareInvalidArgumentException(option,argv[i]);
552             break;
553           }
554         ThrowCompareException(OptionError,"UnrecognizedOption",option)
555       }
556       case 'f':
557       {
558         if (LocaleCompare("format",option+1) == 0)
559           {
560             if (*option == '+')
561               break;
562             i++;
563             if (i == (long) argc)
564               ThrowCompareException(OptionError,"MissingArgument",option);
565             format=argv[i];
566             break;
567           }
568         if (LocaleCompare("fuzz",option+1) == 0)
569           {
570             if (*option == '+')
571               break;
572             i++;
573             if (i == (long) (argc-1))
574               ThrowCompareException(OptionError,"MissingArgument",option);
575             if (IsGeometry(argv[i]) == MagickFalse)
576               ThrowCompareInvalidArgumentException(option,argv[i]);
577             break;
578           }
579         ThrowCompareException(OptionError,"UnrecognizedOption",option)
580       }
581       case 'h':
582       {
583         if ((LocaleCompare("help",option+1) == 0) ||
584             (LocaleCompare("-help",option+1) == 0))
585           return(CompareUsage());
586         if (LocaleCompare("highlight-color",option+1) == 0)
587           {
588             if (*option == '+')
589               break;
590             i++;
591             if (i == (long) (argc-1))
592               ThrowCompareException(OptionError,"MissingArgument",option);
593             break;
594           }
595         ThrowCompareException(OptionError,"UnrecognizedOption",option)
596       }
597       case 'i':
598       {
599         if (LocaleCompare("identify",option+1) == 0)
600           break;
601         if (LocaleCompare("interlace",option+1) == 0)
602           {
603             long
604               interlace;
605
606             if (*option == '+')
607               break;
608             i++;
609             if (i == (long) argc)
610               ThrowCompareException(OptionError,"MissingArgument",option);
611             interlace=ParseMagickOption(MagickInterlaceOptions,MagickFalse,
612               argv[i]);
613             if (interlace < 0)
614               ThrowCompareException(OptionError,"UnrecognizedInterlaceType",
615                 argv[i]);
616             break;
617           }
618         ThrowCompareException(OptionError,"UnrecognizedOption",option)
619       }
620       case 'l':
621       {
622         if (LocaleCompare("limit",option+1) == 0)
623           {
624             char
625               *p;
626
627             double
628               value;
629
630             long
631               resource;
632
633             if (*option == '+')
634               break;
635             i++;
636             if (i == (long) argc)
637               ThrowCompareException(OptionError,"MissingArgument",option);
638             resource=ParseMagickOption(MagickResourceOptions,MagickFalse,
639               argv[i]);
640             if (resource < 0)
641               ThrowCompareException(OptionError,"UnrecognizedResourceType",
642                 argv[i]);
643             i++;
644             if (i == (long) argc)
645               ThrowCompareException(OptionError,"MissingArgument",option);
646             value=strtod(argv[i],&p);
647             if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
648               ThrowCompareInvalidArgumentException(option,argv[i]);
649             break;
650           }
651         if (LocaleCompare("list",option+1) == 0)
652           {
653             long
654               list;
655
656             if (*option == '+')
657               break;
658             i++;
659             if (i == (long) argc)
660               ThrowCompareException(OptionError,"MissingArgument",option);
661             list=ParseMagickOption(MagickListOptions,MagickFalse,argv[i]);
662             if (list < 0)
663               ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
664             (void) MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
665               argv+j,exception);
666             DestroyCompare();
667             return(MagickTrue);
668           }
669         if (LocaleCompare("log",option+1) == 0)
670           {
671             if (*option == '+')
672               break;
673             i++;
674             if ((i == (long) argc) || (strchr(argv[i],'%') == (char *) NULL))
675               ThrowCompareException(OptionError,"MissingArgument",option);
676             break;
677           }
678         if (LocaleCompare("lowlight-color",option+1) == 0)
679           {
680             if (*option == '+')
681               break;
682             i++;
683             if (i == (long) (argc-1))
684               ThrowCompareException(OptionError,"MissingArgument",option);
685             break;
686           }
687         ThrowCompareException(OptionError,"UnrecognizedOption",option)
688       }
689       case 'm':
690       {
691         if (LocaleCompare("matte",option+1) == 0)
692           break;
693         if (LocaleCompare("metric",option+1) == 0)
694           {
695             long
696               type;
697
698             if (*option == '+')
699               break;
700             i++;
701             if (i == (long) argc)
702               ThrowCompareException(OptionError,"MissingArgument",option);
703             type=ParseMagickOption(MagickMetricOptions,MagickTrue,argv[i]);
704             if (type < 0)
705               ThrowCompareException(OptionError,"UnrecognizedMetricType",
706                 argv[i]);
707             metric=(MetricType) type;
708             break;
709           }
710         if (LocaleCompare("monitor",option+1) == 0)
711           break;
712         ThrowCompareException(OptionError,"UnrecognizedOption",option)
713       }
714       case 'p':
715       {
716         if (LocaleCompare("passphrase",option+1) == 0)
717           {
718             if (*option == '+')
719               break;
720             i++;
721             if (i == (long) argc)
722               ThrowCompareException(OptionError,"MissingArgument",option);
723             break;
724           }
725         if (LocaleCompare("profile",option+1) == 0)
726           {
727             i++;
728             if (i == (long) (argc-1))
729               ThrowCompareException(OptionError,"MissingArgument",option);
730             break;
731           }
732         ThrowCompareException(OptionError,"UnrecognizedOption",option)
733       }
734       case 'q':
735       {
736         if (LocaleCompare("quality",option+1) == 0)
737           {
738             if (*option == '+')
739               break;
740             i++;
741             if (i == (long) (argc-1))
742               ThrowCompareException(OptionError,"MissingArgument",option);
743             if (IsGeometry(argv[i]) == MagickFalse)
744               ThrowCompareInvalidArgumentException(option,argv[i]);
745             break;
746           }
747         if (LocaleCompare("quantize",option+1) == 0)
748           {
749             long
750               colorspace;
751
752             if (*option == '+')
753               break;
754             i++;
755             if (i == (long) (argc-1))
756               ThrowCompareException(OptionError,"MissingArgument",option);
757             colorspace=ParseMagickOption(MagickColorspaceOptions,
758               MagickFalse,argv[i]);
759             if (colorspace < 0)
760               ThrowCompareException(OptionError,"UnrecognizedColorspace",
761                 argv[i]);
762             break;
763           }
764         if (LocaleCompare("quiet",option+1) == 0)
765           break;
766         ThrowCompareException(OptionError,"UnrecognizedOption",option)
767       }
768       case 'r':
769       {
770         if (LocaleCompare("regard-warnings",option+1) == 0)
771           break;
772         if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
773           {
774             respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
775             break;
776           }
777         ThrowCompareException(OptionError,"UnrecognizedOption",option)
778       }
779       case 's':
780       {
781         if (LocaleCompare("sampling-factor",option+1) == 0)
782           {
783             if (*option == '+')
784               break;
785             i++;
786             if (i == (long) argc)
787               ThrowCompareException(OptionError,"MissingArgument",option);
788             if (IsGeometry(argv[i]) == MagickFalse)
789               ThrowCompareInvalidArgumentException(option,argv[i]);
790             break;
791           }
792         if (LocaleCompare("seed",option+1) == 0)
793           {
794             if (*option == '+')
795               break;
796             i++;
797             if (i == (long) (argc-1))
798               ThrowCompareException(OptionError,"MissingArgument",option);
799             if (IsGeometry(argv[i]) == MagickFalse)
800               ThrowCompareInvalidArgumentException(option,argv[i]);
801             break;
802           }
803         if (LocaleCompare("set",option+1) == 0)
804           {
805             i++;
806             if (i == (long) argc)
807               ThrowCompareException(OptionError,"MissingArgument",option);
808             if (*option == '+')
809               break;
810             i++;
811             if (i == (long) argc)
812               ThrowCompareException(OptionError,"MissingArgument",option);
813             break;
814           }
815         if (LocaleCompare("size",option+1) == 0)
816           {
817             if (*option == '+')
818               break;
819             i++;
820             if (i == (long) argc)
821               ThrowCompareException(OptionError,"MissingArgument",option);
822             if (IsGeometry(argv[i]) == MagickFalse)
823               ThrowCompareInvalidArgumentException(option,argv[i]);
824             break;
825           }
826         ThrowCompareException(OptionError,"UnrecognizedOption",option)
827       }
828       case 't':
829       {
830         if (LocaleCompare("transparent-color",option+1) == 0)
831           {
832             if (*option == '+')
833               break;
834             i++;
835             if (i == (long) (argc-1))
836               ThrowCompareException(OptionError,"MissingArgument",option);
837             break;
838           }
839         if (LocaleCompare("type",option+1) == 0)
840           {
841             long
842               type;
843
844             if (*option == '+')
845               break;
846             i++;
847             if (i == (long) argc)
848               ThrowCompareException(OptionError,"MissingArgument",option);
849             type=ParseMagickOption(MagickTypeOptions,MagickFalse,argv[i]);
850             if (type < 0)
851               ThrowCompareException(OptionError,"UnrecognizedImageType",
852                 argv[i]);
853             break;
854           }
855         ThrowCompareException(OptionError,"UnrecognizedOption",option)
856       }
857       case 'v':
858       {
859         if (LocaleCompare("verbose",option+1) == 0)
860           break;
861         if ((LocaleCompare("version",option+1) == 0) ||
862             (LocaleCompare("-version",option+1) == 0))
863           {
864             (void) fprintf(stdout,"Version: %s\n",
865               GetMagickVersion((unsigned long *) NULL));
866             (void) fprintf(stdout,"Copyright: %s\n\n",GetMagickCopyright());
867             break;
868           }
869         if (LocaleCompare("virtual-pixel",option+1) == 0)
870           {
871             long
872               method;
873
874             if (*option == '+')
875               break;
876             i++;
877             if (i == (long) (argc-1))
878               ThrowCompareException(OptionError,"MissingArgument",option);
879             method=ParseMagickOption(MagickVirtualPixelOptions,MagickFalse,
880               argv[i]);
881             if (method < 0)
882               ThrowCompareException(OptionError,
883                 "UnrecognizedVirtualPixelMethod",argv[i]);
884             break;
885           }
886         ThrowCompareException(OptionError,"UnrecognizedOption",option)
887       }
888       case '?':
889         break;
890       default:
891         ThrowCompareException(OptionError,"UnrecognizedOption",option)
892     }
893     fire=ParseMagickOption(MagickImageListOptions,MagickFalse,option+1) < 0 ?
894       MagickFalse : MagickTrue;
895     if (fire != MagickFalse)
896       FireImageStack(MagickTrue,MagickTrue,MagickTrue);
897   }
898   if (k != 0)
899     ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
900   if (i-- != (long) (argc-1))
901     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
902   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
903     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
904   FinalizeImageSettings(image_info,image,MagickTrue);
905   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
906     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
907   image=GetImageFromList(image,0);
908   reconstruct_image=GetImageFromList(image,1);
909   similarity_image=SimilarityImage(image,reconstruct_image,&offset,
910     &similarity_metric,exception);
911   if (similarity_metric > dissimilarity_threshold)
912     ThrowCompareException(ImageError,"ImagesTooDissimilar",image->filename);
913   if ((reconstruct_image->columns == image->columns) &&
914       (reconstruct_image->rows == image->rows))
915     difference_image=CompareImageChannels(image,reconstruct_image,channels,
916       metric,&distortion,exception);
917   else
918     if (similarity_image != (Image *) NULL)
919       {
920         Image
921           *composite_image;
922
923         /*
924           Determine if reconstructed image is a subimage of the image.
925         */
926         composite_image=CloneImage(image,0,0,MagickTrue,exception);
927         if (composite_image == (Image *) NULL)
928           difference_image=CompareImageChannels(image,reconstruct_image,
929             channels,metric,&distortion,exception);
930         else
931           {
932             (void) CompositeImage(composite_image,CopyCompositeOp,
933               reconstruct_image,offset.x,offset.y);
934             difference_image=CompareImageChannels(image,composite_image,
935               channels,metric,&distortion,exception);
936             if (difference_image != (Image *) NULL)
937               {
938                 difference_image->page.x=offset.x;
939                 difference_image->page.y=offset.y;
940               }
941             composite_image=DestroyImage(composite_image);
942           }
943         if (difference_image == (Image *) NULL)
944           similarity_image=DestroyImage(similarity_image);
945         else
946           {
947             AppendImageToList(&difference_image,similarity_image);
948             similarity_image=(Image *) NULL;
949           }
950       }
951   if (difference_image == (Image *) NULL)
952     status=0;
953   else
954     {
955       if (image_info->verbose != MagickFalse)
956         (void) IsImagesEqual(image,reconstruct_image);
957       if (*difference_image->magick == '\0')
958         (void) CopyMagickString(difference_image->magick,image->magick,
959           MaxTextExtent);
960       if (image_info->verbose == MagickFalse)
961         {
962           switch (metric)
963           {
964             case MeanAbsoluteErrorMetric:
965             case MeanSquaredErrorMetric:
966             case RootMeanSquaredErrorMetric:
967             case PeakAbsoluteErrorMetric:
968             {
969               (void) fprintf(stderr,"%g (%g)",QuantumRange*distortion,(double)
970                 distortion);
971               if ((reconstruct_image->columns != image->columns) ||
972                   (reconstruct_image->rows != image->rows))
973                 (void) fprintf(stderr," @ %ld,%ld",difference_image->page.x,
974                   difference_image->page.y);
975               (void) fprintf(stderr,"\n");
976               break;
977             }
978             case AbsoluteErrorMetric:
979             case PeakSignalToNoiseRatioMetric:
980             {
981               (void) fprintf(stderr,"%g",distortion);
982               if ((reconstruct_image->columns != image->columns) ||
983                   (reconstruct_image->rows != image->rows))
984                 (void) fprintf(stderr," @ %ld,%ld",difference_image->page.x,
985                   difference_image->page.y);
986               (void) fprintf(stderr,"\n");
987               break;
988             }
989             case MeanErrorPerPixelMetric:
990             {
991               (void) fprintf(stderr,"%g (%g, %g)",distortion,
992                 image->error.normalized_mean_error,
993                 image->error.normalized_maximum_error);
994               if ((reconstruct_image->columns != image->columns) ||
995                   (reconstruct_image->rows != image->rows))
996                 (void) fprintf(stderr," @ %ld,%ld",difference_image->page.x,
997                   difference_image->page.y);
998               (void) fprintf(stderr,"\n");
999               break;
1000             }
1001             case UndefinedMetric:
1002               break;
1003           }
1004         }
1005       else
1006         {
1007           double
1008             *channel_distortion;
1009
1010           channel_distortion=GetImageChannelDistortions(image,reconstruct_image,
1011             metric,&image->exception);
1012           (void) fprintf(stderr,"Image: %s\n",image->filename);
1013           if ((reconstruct_image->columns != image->columns) ||
1014               (reconstruct_image->rows != image->rows))
1015             (void) fprintf(stderr,"Offset: %ld,%ld\n",difference_image->page.x,
1016               difference_image->page.y);
1017           (void) fprintf(stderr,"  Channel distortion: %s\n",
1018             MagickOptionToMnemonic(MagickMetricOptions,(long) metric));
1019           switch (metric)
1020           {
1021             case MeanAbsoluteErrorMetric:
1022             case MeanSquaredErrorMetric:
1023             case RootMeanSquaredErrorMetric:
1024             case PeakAbsoluteErrorMetric:
1025             {
1026               switch (image->colorspace)
1027               {
1028                 case RGBColorspace:
1029                 default:
1030                 {
1031                   (void) fprintf(stderr,"    red: %g (%g)\n",
1032                     QuantumRange*channel_distortion[RedChannel],
1033                     channel_distortion[RedChannel]);
1034                   (void) fprintf(stderr,"    green: %g (%g)\n",
1035                     QuantumRange*channel_distortion[GreenChannel],
1036                     channel_distortion[GreenChannel]);
1037                   (void) fprintf(stderr,"    blue: %g (%g)\n",
1038                     QuantumRange*channel_distortion[BlueChannel],
1039                     channel_distortion[BlueChannel]);
1040                   if (image->matte != MagickFalse)
1041                     (void) fprintf(stderr,"    alpha: %g (%g)\n",
1042                       QuantumRange*channel_distortion[OpacityChannel],
1043                       channel_distortion[OpacityChannel]);
1044                   break;
1045                 }
1046                 case CMYKColorspace:
1047                 {
1048                   (void) fprintf(stderr,"    cyan: %g (%g)\n",
1049                     QuantumRange*channel_distortion[CyanChannel],
1050                     channel_distortion[CyanChannel]);
1051                   (void) fprintf(stderr,"    magenta: %g (%g)\n",
1052                     QuantumRange*channel_distortion[MagentaChannel],
1053                     channel_distortion[MagentaChannel]);
1054                   (void) fprintf(stderr,"    yellow: %g (%g)\n",
1055                     QuantumRange*channel_distortion[YellowChannel],
1056                     channel_distortion[YellowChannel]);
1057                   (void) fprintf(stderr,"    black: %g (%g)\n",
1058                     QuantumRange*channel_distortion[BlackChannel],
1059                     channel_distortion[BlackChannel]);
1060                   if (image->matte != MagickFalse)
1061                     (void) fprintf(stderr,"    alpha: %g (%g)\n",
1062                       QuantumRange*channel_distortion[OpacityChannel],
1063                       channel_distortion[OpacityChannel]);
1064                   break;
1065                 }
1066                 case GRAYColorspace:
1067                 {
1068                   (void) fprintf(stderr,"    gray: %g (%g)\n",
1069                     QuantumRange*channel_distortion[GrayChannel],
1070                     channel_distortion[GrayChannel]);
1071                   if (image->matte != MagickFalse)
1072                     (void) fprintf(stderr,"    alpha: %g (%g)\n",
1073                       QuantumRange*channel_distortion[OpacityChannel],
1074                       channel_distortion[OpacityChannel]);
1075                   break;
1076                 }
1077               }
1078               (void) fprintf(stderr,"    all: %g (%g)\n",
1079                 QuantumRange*channel_distortion[AllChannels],
1080                 channel_distortion[AllChannels]);
1081               break;
1082             }
1083             case AbsoluteErrorMetric:
1084             case PeakSignalToNoiseRatioMetric:
1085             {
1086               switch (image->colorspace)
1087               {
1088                 case RGBColorspace:
1089                 default:
1090                 {
1091                   (void) fprintf(stderr,"    red: %g\n",
1092                     channel_distortion[RedChannel]);
1093                   (void) fprintf(stderr,"    green: %g\n",
1094                     channel_distortion[GreenChannel]);
1095                   (void) fprintf(stderr,"    blue: %g\n",
1096                     channel_distortion[BlueChannel]);
1097                   if (image->matte != MagickFalse)
1098                     (void) fprintf(stderr,"    alpha: %g\n",
1099                       channel_distortion[OpacityChannel]);
1100                   break;
1101                 }
1102                 case CMYKColorspace:
1103                 {
1104                   (void) fprintf(stderr,"    cyan: %g\n",
1105                     channel_distortion[CyanChannel]);
1106                   (void) fprintf(stderr,"    magenta: %g\n",
1107                     channel_distortion[MagentaChannel]);
1108                   (void) fprintf(stderr,"    yellow: %g\n",
1109                     channel_distortion[YellowChannel]);
1110                   (void) fprintf(stderr,"    black: %g\n",
1111                     channel_distortion[BlackChannel]);
1112                   if (image->matte != MagickFalse)
1113                     (void) fprintf(stderr,"    alpha: %g\n",
1114                       channel_distortion[OpacityChannel]);
1115                   break;
1116                 }
1117                 case GRAYColorspace:
1118                 {
1119                   (void) fprintf(stderr,"    gray: %g\n",
1120                     channel_distortion[GrayChannel]);
1121                   if (image->matte != MagickFalse)
1122                     (void) fprintf(stderr,"    alpha: %g\n",
1123                       channel_distortion[OpacityChannel]);
1124                   break;
1125                 }
1126               }
1127               (void) fprintf(stderr,"    all: %g\n",
1128                 channel_distortion[AllChannels]);
1129               break;
1130             }
1131             case MeanErrorPerPixelMetric:
1132             {
1133               (void) fprintf(stderr,"    %g (%g, %g)\n",
1134                 channel_distortion[AllChannels],
1135                 image->error.normalized_mean_error,
1136                 image->error.normalized_maximum_error);
1137               break;
1138             }
1139             case UndefinedMetric:
1140               break;
1141           }
1142           channel_distortion=(double *) RelinquishMagickMemory(
1143             channel_distortion);
1144         }
1145       status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
1146       if ((metadata != (char **) NULL) && (format != (char *) NULL))
1147         {
1148           char
1149             *text;
1150
1151           text=InterpretImageProperties(image_info,difference_image,format);
1152           if (text == (char *) NULL)
1153             ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1154               GetExceptionMessage(errno));
1155           (void) ConcatenateString(&(*metadata),text);
1156           (void) ConcatenateString(&(*metadata),"\n");
1157           text=DestroyString(text);
1158         }
1159       difference_image=DestroyImageList(difference_image);
1160     }
1161   DestroyCompare();
1162   return(status != 0 ? MagickTrue : MagickFalse);
1163 }