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