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