]> granicus.if.org Git - imagemagick/blob - MagickWand/compare.c
(no commit message)
[imagemagick] / MagickWand / compare.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               CCCC   OOO   M   M  PPPP    AAA   RRRR    EEEEE               %
7 %              C      O   O  MM MM  P   P  A   A  R   R   E                   %
8 %              C      O   O  M M M  PPPP   AAAAA  RRRR    EEE                 %
9 %              C      O   O  M   M  P      A   A  R R     E                   %
10 %               CCCC   OOO   M   M  P      A   A  R  R    EEEEE               %
11 %                                                                             %
12 %                                                                             %
13 %                         Image Comparison Methods                            %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                               December 2003                                 %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %  Use the compare program to mathematically and visually annotate the
37 %  difference between an image and its reconstruction.
38 %
39 */
40 \f
41 /*
42   Include declarations.
43 */
44 #include "MagickWand/studio.h"
45 #include "MagickWand/MagickWand.h"
46 #include "MagickWand/mogrify-private.h"
47 #include "MagickCore/string-private.h"
48 \f
49 /*
50 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
51 %                                                                             %
52 %                                                                             %
53 %                                                                             %
54 %   C o m p a r e I m a g e C o m m a n d                                     %
55 %                                                                             %
56 %                                                                             %
57 %                                                                             %
58 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
59 %
60 %  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       "-debug events        display copious debugging information",
92       "-help                print program options",
93       "-list type           print a list of supported option arguments",
94       "-log format          format of debugging information",
95       (char *) NULL
96     },
97     *settings[]=
98     {
99       "-alpha option        on, activate, off, deactivate, set, opaque, copy",
100       "                     transparent, extract, background, or shape",
101       "-authenticate password",
102       "                     decipher image with this password",
103       "-channel type        apply option to select image channels",
104       "-colorspace type     alternate image colorspace",
105       "-compose operator    set image composite operator",
106       "-compress type       type of pixel compression when writing the image",
107       "-decipher filename   convert cipher pixels to plain pixels",
108       "-define format:option",
109       "                     define one or more image format options",
110       "-density geometry    horizontal and vertical density of the image",
111       "-depth value         image depth",
112       "-dissimilarity-threshold value",
113       "                     maximum distortion for (sub)image match",
114       "-encipher filename   convert plain pixels to cipher pixels",
115       "-extract geometry    extract area from image",
116       "-format \"string\"     output formatted image characteristics",
117       "-fuzz distance       colors within this distance are considered equal",
118       "-highlight-color color",
119       "                     empasize pixel differences with this color",
120       "-identify            identify the format and characteristics of the image",
121       "-interlace type      type of image interlacing scheme",
122       "-limit type value    pixel cache resource limit",
123       "-lowlight-color color",
124       "                     de-emphasize pixel differences with this color",
125       "-metric type         measure differences between images with this metric",
126       "-monitor             monitor progress",
127       "-passphrase filename get the passphrase from this file",
128       "-profile filename    add, delete, or apply an image profile",
129       "-quality value       JPEG/MIFF/PNG compression level",
130       "-quiet               suppress all warning messages",
131       "-quantize colorspace reduce colors in this colorspace",
132       "-regard-warnings     pay attention to warning messages",
133       "-respect-parentheses settings remain in effect until parenthesis boundary",
134       "-sampling-factor geometry",
135       "                     horizontal and vertical sampling factor",
136       "-seed value          seed a new sequence of pseudo-random numbers",
137       "-set attribute value set an image attribute",
138       "-quality value       JPEG/MIFF/PNG compression level",
139       "-size geometry       width and height of image",
140       "-subimage-search     search for subimage",
141       "-transparent-color color",
142       "                     transparent color",
143       "-type type           image type",
144       "-verbose             print detailed information about the image",
145       "-version             print version information",
146       "-virtual-pixel method",
147       "                     virtual pixel access method",
148       (char *) NULL
149     };
150
151   (void) printf("Version: %s\n",GetMagickVersion((size_t *) NULL));
152   (void) printf("Copyright: %s\n",GetMagickCopyright());
153   (void) printf("Features: %s\n\n",GetMagickFeatures());
154   (void) printf("Usage: %s [options ...] image reconstruct difference\n",
155     GetClientName());
156   (void) printf("\nImage Settings:\n");
157   for (p=settings; *p != (char *) NULL; p++)
158     (void) printf("  %s\n",*p);
159   (void) printf("\nMiscellaneous Options:\n");
160   for (p=miscellaneous; *p != (char *) NULL; p++)
161     (void) printf("  %s\n",*p);
162   (void) printf(
163     "\nBy default, the image format of `file' is determined by its magic\n");
164   (void) printf(
165     "number.  To specify a particular image format, precede the filename\n");
166   (void) printf(
167     "with an image format name and a colon (i.e. ps:image) or specify the\n");
168   (void) printf(
169     "image type as the filename suffix (i.e. image.ps).  Specify 'file' as\n");
170   (void) printf("'-' for standard input or output.\n");
171   return(MagickFalse);
172 }
173
174 WandExport MagickBooleanType CompareImagesCommand(ImageInfo *image_info,
175   int argc,char **argv,char **metadata,ExceptionInfo *exception)
176 {
177 #define DefaultDissimilarityThreshold  0.31830988618379067154
178 #define DestroyCompare() \
179 { \
180   if (similarity_image != (Image *) NULL) \
181     similarity_image=DestroyImageList(similarity_image); \
182   if (difference_image != (Image *) NULL) \
183     difference_image=DestroyImageList(difference_image); \
184   DestroyImageStack(); \
185   for (i=0; i < (ssize_t) argc; i++) \
186     argv[i]=DestroyString(argv[i]); \
187   argv=(char **) RelinquishMagickMemory(argv); \
188 }
189 #define ThrowCompareException(asperity,tag,option) \
190 { \
191   if (exception->severity < (asperity)) \
192     (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \
193       "`%s'",option); \
194   DestroyCompare(); \
195   return(MagickFalse); \
196 }
197 #define ThrowCompareInvalidArgumentException(option,argument) \
198 { \
199   (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \
200     "InvalidArgument","`%s': %s",option,argument); \
201   DestroyCompare(); \
202   return(MagickFalse); \
203 }
204
205   char
206     *filename,
207     *option;
208
209   const char
210     *format;
211
212   double
213     dissimilarity_threshold,
214     distortion,
215     similarity_metric;
216
217   Image
218     *difference_image,
219     *image,
220     *reconstruct_image,
221     *similarity_image;
222
223   ImageStack
224     image_stack[MaxImageStackDepth+1];
225
226   MagickBooleanType
227     fire,
228     pend,
229     respect_parenthesis,
230     subimage_search;
231
232   MagickStatusType
233     status;
234
235   MetricType
236     metric;
237
238   RectangleInfo
239     offset;
240
241   register ssize_t
242     i;
243
244   ssize_t
245     j,
246     k;
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) FormatLocaleFile(stdout,"Version: %s\n",
263             GetMagickVersion((size_t *) NULL));
264           (void) FormatLocaleFile(stdout,"Copyright: %s\n",
265             GetMagickCopyright());
266           (void) FormatLocaleFile(stdout,"Features: %s\n\n",
267             GetMagickFeatures());
268           return(MagickFalse);
269         }
270     }
271   if (argc < 3)
272     return(CompareUsage());
273   difference_image=NewImageList();
274   similarity_image=NewImageList();
275   dissimilarity_threshold=DefaultDissimilarityThreshold;
276   distortion=0.0;
277   format=(char *) NULL;
278   j=1;
279   k=0;
280   metric=UndefinedMetric;
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         (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             ssize_t
345               type;
346
347             if (*option == '+')
348               break;
349             i++;
350             if (i == (ssize_t) argc)
351               ThrowCompareException(OptionError,"MissingArgument",option);
352             type=ParseCommandOption(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 == (ssize_t) 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 == (ssize_t) 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             ssize_t
385               channel;
386
387             if (*option == '+')
388               break;
389             i++;
390             if (i == (ssize_t) (argc-1))
391               ThrowCompareException(OptionError,"MissingArgument",option);
392             channel=ParseChannelOption(argv[i]);
393             if (channel < 0)
394               ThrowCompareException(OptionError,"UnrecognizedChannelType",
395                 argv[i]);
396             SetPixelChannelMap(image,(ChannelType) channel);
397             break;
398           }
399         if (LocaleCompare("colorspace",option+1) == 0)
400           {
401             ssize_t
402               colorspace;
403
404             if (*option == '+')
405               break;
406             i++;
407             if (i == (ssize_t) (argc-1))
408               ThrowCompareException(OptionError,"MissingArgument",option);
409             colorspace=ParseCommandOption(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             ssize_t
419               compose;
420
421             if (*option == '+')
422               break;
423             i++;
424             if (i == (ssize_t) argc)
425               ThrowCompareException(OptionError,"MissingArgument",option);
426             compose=ParseCommandOption(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             ssize_t
436               compress;
437
438             if (*option == '+')
439               break;
440             i++;
441             if (i == (ssize_t) (argc-1))
442               ThrowCompareException(OptionError,"MissingArgument",option);
443             compress=ParseCommandOption(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 == (ssize_t) 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 == (ssize_t) (argc-1))
478               ThrowCompareException(OptionError,"MissingArgument",option);
479             break;
480           }
481         if (LocaleCompare("define",option+1) == 0)
482           {
483             i++;
484             if (i == (ssize_t) 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 == (ssize_t) 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 == (ssize_t) 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 == (ssize_t) 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                 (char **) NULL);
534             break;
535           }
536         if (LocaleCompare("duration",option+1) == 0)
537           {
538             if (*option == '+')
539               break;
540             i++;
541             if (i == (ssize_t) (argc-1))
542               ThrowCompareException(OptionError,"MissingArgument",option);
543             if (IsGeometry(argv[i]) == MagickFalse)
544               ThrowCompareInvalidArgumentException(option,argv[i]);
545             break;
546           }
547         ThrowCompareException(OptionError,"UnrecognizedOption",option)
548       }
549       case 'e':
550       {
551         if (LocaleCompare("encipher",option+1) == 0)
552           {
553             if (*option == '+')
554               break;
555             i++;
556             if (i == (ssize_t) (argc-1))
557               ThrowCompareException(OptionError,"MissingArgument",option);
558             break;
559           }
560         if (LocaleCompare("extract",option+1) == 0)
561           {
562             if (*option == '+')
563               break;
564             i++;
565             if (i == (ssize_t) (argc-1))
566               ThrowCompareException(OptionError,"MissingArgument",option);
567             if (IsGeometry(argv[i]) == MagickFalse)
568               ThrowCompareInvalidArgumentException(option,argv[i]);
569             break;
570           }
571         ThrowCompareException(OptionError,"UnrecognizedOption",option)
572       }
573       case 'f':
574       {
575         if (LocaleCompare("format",option+1) == 0)
576           {
577             if (*option == '+')
578               break;
579             i++;
580             if (i == (ssize_t) argc)
581               ThrowCompareException(OptionError,"MissingArgument",option);
582             format=argv[i];
583             break;
584           }
585         if (LocaleCompare("fuzz",option+1) == 0)
586           {
587             if (*option == '+')
588               break;
589             i++;
590             if (i == (ssize_t) (argc-1))
591               ThrowCompareException(OptionError,"MissingArgument",option);
592             if (IsGeometry(argv[i]) == MagickFalse)
593               ThrowCompareInvalidArgumentException(option,argv[i]);
594             break;
595           }
596         ThrowCompareException(OptionError,"UnrecognizedOption",option)
597       }
598       case 'h':
599       {
600         if ((LocaleCompare("help",option+1) == 0) ||
601             (LocaleCompare("-help",option+1) == 0))
602           return(CompareUsage());
603         if (LocaleCompare("highlight-color",option+1) == 0)
604           {
605             if (*option == '+')
606               break;
607             i++;
608             if (i == (ssize_t) (argc-1))
609               ThrowCompareException(OptionError,"MissingArgument",option);
610             break;
611           }
612         ThrowCompareException(OptionError,"UnrecognizedOption",option)
613       }
614       case 'i':
615       {
616         if (LocaleCompare("identify",option+1) == 0)
617           break;
618         if (LocaleCompare("interlace",option+1) == 0)
619           {
620             ssize_t
621               interlace;
622
623             if (*option == '+')
624               break;
625             i++;
626             if (i == (ssize_t) argc)
627               ThrowCompareException(OptionError,"MissingArgument",option);
628             interlace=ParseCommandOption(MagickInterlaceOptions,MagickFalse,
629               argv[i]);
630             if (interlace < 0)
631               ThrowCompareException(OptionError,"UnrecognizedInterlaceType",
632                 argv[i]);
633             break;
634           }
635         ThrowCompareException(OptionError,"UnrecognizedOption",option)
636       }
637       case 'l':
638       {
639         if (LocaleCompare("limit",option+1) == 0)
640           {
641             char
642               *p;
643
644             double
645               value;
646
647             ssize_t
648               resource;
649
650             if (*option == '+')
651               break;
652             i++;
653             if (i == (ssize_t) argc)
654               ThrowCompareException(OptionError,"MissingArgument",option);
655             resource=ParseCommandOption(MagickResourceOptions,MagickFalse,
656               argv[i]);
657             if (resource < 0)
658               ThrowCompareException(OptionError,"UnrecognizedResourceType",
659                 argv[i]);
660             i++;
661             if (i == (ssize_t) argc)
662               ThrowCompareException(OptionError,"MissingArgument",option);
663             value=StringToDouble(argv[i],&p);
664             (void) value;
665             if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
666               ThrowCompareInvalidArgumentException(option,argv[i]);
667             break;
668           }
669         if (LocaleCompare("list",option+1) == 0)
670           {
671             ssize_t
672               list;
673
674             if (*option == '+')
675               break;
676             i++;
677             if (i == (ssize_t) argc)
678               ThrowCompareException(OptionError,"MissingArgument",option);
679             list=ParseCommandOption(MagickListOptions,MagickFalse,argv[i]);
680             if (list < 0)
681               ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
682             status=MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
683               argv+j,exception);
684             DestroyCompare();
685             return(status != 0 ? MagickFalse : MagickTrue);
686           }
687         if (LocaleCompare("log",option+1) == 0)
688           {
689             if (*option == '+')
690               break;
691             i++;
692             if ((i == (ssize_t) argc) || (strchr(argv[i],'%') == (char *) NULL))
693               ThrowCompareException(OptionError,"MissingArgument",option);
694             break;
695           }
696         if (LocaleCompare("lowlight-color",option+1) == 0)
697           {
698             if (*option == '+')
699               break;
700             i++;
701             if (i == (ssize_t) (argc-1))
702               ThrowCompareException(OptionError,"MissingArgument",option);
703             break;
704           }
705         ThrowCompareException(OptionError,"UnrecognizedOption",option)
706       }
707       case 'm':
708       {
709         if (LocaleCompare("matte",option+1) == 0)
710           break;
711         if (LocaleCompare("metric",option+1) == 0)
712           {
713             ssize_t
714               type;
715
716             if (*option == '+')
717               break;
718             i++;
719             if (i == (ssize_t) argc)
720               ThrowCompareException(OptionError,"MissingArgument",option);
721             type=ParseCommandOption(MagickMetricOptions,MagickTrue,argv[i]);
722             if (type < 0)
723               ThrowCompareException(OptionError,"UnrecognizedMetricType",
724                 argv[i]);
725             metric=(MetricType) type;
726             break;
727           }
728         if (LocaleCompare("monitor",option+1) == 0)
729           break;
730         ThrowCompareException(OptionError,"UnrecognizedOption",option)
731       }
732       case 'p':
733       {
734         if (LocaleCompare("passphrase",option+1) == 0)
735           {
736             if (*option == '+')
737               break;
738             i++;
739             if (i == (ssize_t) argc)
740               ThrowCompareException(OptionError,"MissingArgument",option);
741             break;
742           }
743         if (LocaleCompare("profile",option+1) == 0)
744           {
745             i++;
746             if (i == (ssize_t) (argc-1))
747               ThrowCompareException(OptionError,"MissingArgument",option);
748             break;
749           }
750         ThrowCompareException(OptionError,"UnrecognizedOption",option)
751       }
752       case 'q':
753       {
754         if (LocaleCompare("quality",option+1) == 0)
755           {
756             if (*option == '+')
757               break;
758             i++;
759             if (i == (ssize_t) (argc-1))
760               ThrowCompareException(OptionError,"MissingArgument",option);
761             if (IsGeometry(argv[i]) == MagickFalse)
762               ThrowCompareInvalidArgumentException(option,argv[i]);
763             break;
764           }
765         if (LocaleCompare("quantize",option+1) == 0)
766           {
767             ssize_t
768               colorspace;
769
770             if (*option == '+')
771               break;
772             i++;
773             if (i == (ssize_t) (argc-1))
774               ThrowCompareException(OptionError,"MissingArgument",option);
775             colorspace=ParseCommandOption(MagickColorspaceOptions,
776               MagickFalse,argv[i]);
777             if (colorspace < 0)
778               ThrowCompareException(OptionError,"UnrecognizedColorspace",
779                 argv[i]);
780             break;
781           }
782         if (LocaleCompare("quiet",option+1) == 0)
783           break;
784         ThrowCompareException(OptionError,"UnrecognizedOption",option)
785       }
786       case 'r':
787       {
788         if (LocaleCompare("regard-warnings",option+1) == 0)
789           break;
790         if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
791           {
792             respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
793             break;
794           }
795         ThrowCompareException(OptionError,"UnrecognizedOption",option)
796       }
797       case 's':
798       {
799         if (LocaleCompare("sampling-factor",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("seed",option+1) == 0)
811           {
812             if (*option == '+')
813               break;
814             i++;
815             if (i == (ssize_t) (argc-1))
816               ThrowCompareException(OptionError,"MissingArgument",option);
817             if (IsGeometry(argv[i]) == MagickFalse)
818               ThrowCompareInvalidArgumentException(option,argv[i]);
819             break;
820           }
821         if (LocaleCompare("set",option+1) == 0)
822           {
823             i++;
824             if (i == (ssize_t) argc)
825               ThrowCompareException(OptionError,"MissingArgument",option);
826             if (*option == '+')
827               break;
828             i++;
829             if (i == (ssize_t) argc)
830               ThrowCompareException(OptionError,"MissingArgument",option);
831             break;
832           }
833         if (LocaleCompare("size",option+1) == 0)
834           {
835             if (*option == '+')
836               break;
837             i++;
838             if (i == (ssize_t) argc)
839               ThrowCompareException(OptionError,"MissingArgument",option);
840             if (IsGeometry(argv[i]) == MagickFalse)
841               ThrowCompareInvalidArgumentException(option,argv[i]);
842             break;
843           }
844         if (LocaleCompare("subimage-search",option+1) == 0)
845           {
846             if (*option == '+')
847               {
848                 subimage_search=MagickFalse;
849                 break;
850               }
851             subimage_search=MagickTrue;
852             break;
853           }
854         if (LocaleCompare("synchronize",option+1) == 0)
855           break;
856         ThrowCompareException(OptionError,"UnrecognizedOption",option)
857       }
858       case 't':
859       {
860         if (LocaleCompare("taint",option+1) == 0)
861           break;
862         if (LocaleCompare("transparent-color",option+1) == 0)
863           {
864             if (*option == '+')
865               break;
866             i++;
867             if (i == (ssize_t) (argc-1))
868               ThrowCompareException(OptionError,"MissingArgument",option);
869             break;
870           }
871         if (LocaleCompare("type",option+1) == 0)
872           {
873             ssize_t
874               type;
875
876             if (*option == '+')
877               break;
878             i++;
879             if (i == (ssize_t) argc)
880               ThrowCompareException(OptionError,"MissingArgument",option);
881             type=ParseCommandOption(MagickTypeOptions,MagickFalse,argv[i]);
882             if (type < 0)
883               ThrowCompareException(OptionError,"UnrecognizedImageType",
884                 argv[i]);
885             break;
886           }
887         ThrowCompareException(OptionError,"UnrecognizedOption",option)
888       }
889       case 'v':
890       {
891         if (LocaleCompare("verbose",option+1) == 0)
892           break;
893         if ((LocaleCompare("version",option+1) == 0) ||
894             (LocaleCompare("-version",option+1) == 0))
895           {
896             (void) FormatLocaleFile(stdout,"Version: %s\n",
897               GetMagickVersion((size_t *) NULL));
898             (void) FormatLocaleFile(stdout,"Copyright: %s\n",
899               GetMagickCopyright());
900             (void) FormatLocaleFile(stdout,"Features: %s\n\n",
901               GetMagickFeatures());
902             break;
903           }
904         if (LocaleCompare("virtual-pixel",option+1) == 0)
905           {
906             ssize_t
907               method;
908
909             if (*option == '+')
910               break;
911             i++;
912             if (i == (ssize_t) (argc-1))
913               ThrowCompareException(OptionError,"MissingArgument",option);
914             method=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
915               argv[i]);
916             if (method < 0)
917               ThrowCompareException(OptionError,
918                 "UnrecognizedVirtualPixelMethod",argv[i]);
919             break;
920           }
921         ThrowCompareException(OptionError,"UnrecognizedOption",option)
922       }
923       case '?':
924         break;
925       default:
926         ThrowCompareException(OptionError,"UnrecognizedOption",option)
927     }
928     fire=(GetCommandOptionFlags(MagickCommandOptions,MagickFalse,option) &
929       FireOptionFlag) == 0 ?  MagickFalse : MagickTrue;
930     if (fire != MagickFalse)
931       FireImageStack(MagickTrue,MagickTrue,MagickTrue);
932   }
933   if (k != 0)
934     ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
935   if (i-- != (ssize_t) (argc-1))
936     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
937   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
938     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
939   FinalizeImageSettings(image_info,image,MagickTrue);
940   if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
941     ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
942   image=GetImageFromList(image,0);
943   reconstruct_image=GetImageFromList(image,1);
944   if (subimage_search != MagickFalse)
945     {
946       similarity_image=SimilarityImage(image,reconstruct_image,metric,&offset,
947         &similarity_metric,exception);
948       if (similarity_metric > dissimilarity_threshold)
949         ThrowCompareException(ImageError,"ImagesTooDissimilar",image->filename);
950     }
951   if ((reconstruct_image->columns == image->columns) &&
952       (reconstruct_image->rows == image->rows))
953     difference_image=CompareImages(image,reconstruct_image,metric,&distortion,
954       exception);
955   else
956     if (similarity_image == (Image *) NULL)
957       ThrowCompareException(OptionError,"ImageWidthsOrHeightsDiffer",
958         image->filename)
959     else
960       {
961         Image
962           *composite_image;
963
964         /*
965           Determine if reconstructed image is a subimage of the image.
966         */
967         composite_image=CloneImage(image,0,0,MagickTrue,exception);
968         if (composite_image == (Image *) NULL)
969           difference_image=CompareImages(image,reconstruct_image,metric,
970             &distortion,exception);
971         else
972           {
973             (void) CompositeImage(composite_image,CopyCompositeOp,
974               reconstruct_image,offset.x,offset.y,exception);
975             difference_image=CompareImages(image,composite_image,metric,
976               &distortion,exception);
977             if (difference_image != (Image *) NULL)
978               {
979                 difference_image->page.x=offset.x;
980                 difference_image->page.y=offset.y;
981               }
982             composite_image=DestroyImage(composite_image);
983           }
984         if (difference_image != (Image *) NULL)
985           {
986             AppendImageToList(&difference_image,similarity_image);
987             similarity_image=(Image *) NULL;
988           }
989       }
990   if (difference_image == (Image *) NULL)
991     status=0;
992   else
993     {
994       if (image_info->verbose != MagickFalse)
995         (void) IsImagesEqual(image,reconstruct_image,exception);
996       if (*difference_image->magick == '\0')
997         (void) CopyMagickString(difference_image->magick,image->magick,
998           MaxTextExtent);
999       if (image_info->verbose == MagickFalse)
1000         {
1001           switch (metric)
1002           {
1003             case FuzzErrorMetric:
1004             case MeanAbsoluteErrorMetric:
1005             case MeanSquaredErrorMetric:
1006             case RootMeanSquaredErrorMetric:
1007             case PeakAbsoluteErrorMetric:
1008             {
1009               (void) FormatLocaleFile(stderr,"%g (%g)",QuantumRange*distortion,
1010                 (double) distortion);
1011               if ((reconstruct_image->columns != image->columns) ||
1012                   (reconstruct_image->rows != image->rows))
1013                 (void) FormatLocaleFile(stderr," @ %.20g,%.20g",(double)
1014                   difference_image->page.x,(double) difference_image->page.y);
1015               (void) FormatLocaleFile(stderr,"\n");
1016               break;
1017             }
1018             case AbsoluteErrorMetric:
1019             case NormalizedCrossCorrelationErrorMetric:
1020             case PeakSignalToNoiseRatioMetric:
1021             {
1022               (void) FormatLocaleFile(stderr,"%g",distortion);
1023               if ((reconstruct_image->columns != image->columns) ||
1024                   (reconstruct_image->rows != image->rows))
1025                 (void) FormatLocaleFile(stderr," @ %.20g,%.20g",(double)
1026                   difference_image->page.x,(double) difference_image->page.y);
1027               (void) FormatLocaleFile(stderr,"\n");
1028               break;
1029             }
1030             case MeanErrorPerPixelMetric:
1031             {
1032               (void) FormatLocaleFile(stderr,"%g (%g, %g)",distortion,
1033                 image->error.normalized_mean_error,
1034                 image->error.normalized_maximum_error);
1035               if ((reconstruct_image->columns != image->columns) ||
1036                   (reconstruct_image->rows != image->rows))
1037                 (void) FormatLocaleFile(stderr," @ %.20g,%.20g",(double)
1038                   difference_image->page.x,(double) difference_image->page.y);
1039               (void) FormatLocaleFile(stderr,"\n");
1040               break;
1041             }
1042             case UndefinedMetric:
1043               break;
1044           }
1045         }
1046       else
1047         {
1048           double
1049             *channel_distortion;
1050
1051           channel_distortion=GetImageDistortions(image,reconstruct_image,
1052             metric,exception);
1053           (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename);
1054           if ((reconstruct_image->columns != image->columns) ||
1055               (reconstruct_image->rows != image->rows))
1056             (void) FormatLocaleFile(stderr,"Offset: %.20g,%.20g\n",(double)
1057               difference_image->page.x,(double) difference_image->page.y);
1058           (void) FormatLocaleFile(stderr,"  Channel distortion: %s\n",
1059             CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1060           switch (metric)
1061           {
1062             case FuzzErrorMetric:
1063             case MeanAbsoluteErrorMetric:
1064             case MeanSquaredErrorMetric:
1065             case RootMeanSquaredErrorMetric:
1066             case PeakAbsoluteErrorMetric:
1067             {
1068               switch (image->colorspace)
1069               {
1070                 case RGBColorspace:
1071                 default:
1072                 {
1073                   (void) FormatLocaleFile(stderr,"    red: %g (%g)\n",
1074                     QuantumRange*channel_distortion[RedPixelChannel],
1075                     channel_distortion[RedPixelChannel]);
1076                   (void) FormatLocaleFile(stderr,"    green: %g (%g)\n",
1077                     QuantumRange*channel_distortion[GreenPixelChannel],
1078                     channel_distortion[GreenPixelChannel]);
1079                   (void) FormatLocaleFile(stderr,"    blue: %g (%g)\n",
1080                     QuantumRange*channel_distortion[BluePixelChannel],
1081                     channel_distortion[BluePixelChannel]);
1082                   if (image->matte != MagickFalse)
1083                     (void) FormatLocaleFile(stderr,"    alpha: %g (%g)\n",
1084                       QuantumRange*channel_distortion[AlphaPixelChannel],
1085                       channel_distortion[AlphaPixelChannel]);
1086                   break;
1087                 }
1088                 case CMYKColorspace:
1089                 {
1090                   (void) FormatLocaleFile(stderr,"    cyan: %g (%g)\n",
1091                     QuantumRange*channel_distortion[CyanPixelChannel],
1092                     channel_distortion[CyanPixelChannel]);
1093                   (void) FormatLocaleFile(stderr,"    magenta: %g (%g)\n",
1094                     QuantumRange*channel_distortion[MagentaPixelChannel],
1095                     channel_distortion[MagentaPixelChannel]);
1096                   (void) FormatLocaleFile(stderr,"    yellow: %g (%g)\n",
1097                     QuantumRange*channel_distortion[YellowPixelChannel],
1098                     channel_distortion[YellowPixelChannel]);
1099                   (void) FormatLocaleFile(stderr,"    black: %g (%g)\n",
1100                     QuantumRange*channel_distortion[BlackPixelChannel],
1101                     channel_distortion[BlackPixelChannel]);
1102                   if (image->matte != MagickFalse)
1103                     (void) FormatLocaleFile(stderr,"    alpha: %g (%g)\n",
1104                       QuantumRange*channel_distortion[AlphaPixelChannel],
1105                       channel_distortion[AlphaPixelChannel]);
1106                   break;
1107                 }
1108                 case GRAYColorspace:
1109                 {
1110                   (void) FormatLocaleFile(stderr,"    gray: %g (%g)\n",
1111                     QuantumRange*channel_distortion[GrayPixelChannel],
1112                     channel_distortion[GrayPixelChannel]);
1113                   if (image->matte != MagickFalse)
1114                     (void) FormatLocaleFile(stderr,"    alpha: %g (%g)\n",
1115                       QuantumRange*channel_distortion[AlphaPixelChannel],
1116                       channel_distortion[AlphaPixelChannel]);
1117                   break;
1118                 }
1119               }
1120               (void) FormatLocaleFile(stderr,"    all: %g (%g)\n",
1121                 QuantumRange*channel_distortion[MaxPixelChannels],
1122                 channel_distortion[MaxPixelChannels]);
1123               break;
1124             }
1125             case AbsoluteErrorMetric:
1126             case NormalizedCrossCorrelationErrorMetric:
1127             case PeakSignalToNoiseRatioMetric:
1128             {
1129               switch (image->colorspace)
1130               {
1131                 case RGBColorspace:
1132                 default:
1133                 {
1134                   (void) FormatLocaleFile(stderr,"    red: %g\n",
1135                     channel_distortion[RedPixelChannel]);
1136                   (void) FormatLocaleFile(stderr,"    green: %g\n",
1137                     channel_distortion[GreenPixelChannel]);
1138                   (void) FormatLocaleFile(stderr,"    blue: %g\n",
1139                     channel_distortion[BluePixelChannel]);
1140                   if (image->matte != MagickFalse)
1141                     (void) FormatLocaleFile(stderr,"    alpha: %g\n",
1142                       channel_distortion[AlphaPixelChannel]);
1143                   break;
1144                 }
1145                 case CMYKColorspace:
1146                 {
1147                   (void) FormatLocaleFile(stderr,"    cyan: %g\n",
1148                     channel_distortion[CyanPixelChannel]);
1149                   (void) FormatLocaleFile(stderr,"    magenta: %g\n",
1150                     channel_distortion[MagentaPixelChannel]);
1151                   (void) FormatLocaleFile(stderr,"    yellow: %g\n",
1152                     channel_distortion[YellowPixelChannel]);
1153                   (void) FormatLocaleFile(stderr,"    black: %g\n",
1154                     channel_distortion[BlackPixelChannel]);
1155                   if (image->matte != MagickFalse)
1156                     (void) FormatLocaleFile(stderr,"    alpha: %g\n",
1157                       channel_distortion[AlphaPixelChannel]);
1158                   break;
1159                 }
1160                 case GRAYColorspace:
1161                 {
1162                   (void) FormatLocaleFile(stderr,"    gray: %g\n",
1163                     channel_distortion[GrayPixelChannel]);
1164                   if (image->matte != MagickFalse)
1165                     (void) FormatLocaleFile(stderr,"    alpha: %g\n",
1166                       channel_distortion[AlphaPixelChannel]);
1167                   break;
1168                 }
1169               }
1170               (void) FormatLocaleFile(stderr,"    all: %g\n",
1171                 channel_distortion[MaxPixelChannels]);
1172               break;
1173             }
1174             case MeanErrorPerPixelMetric:
1175             {
1176               (void) FormatLocaleFile(stderr,"    %g (%g, %g)\n",
1177                 channel_distortion[MaxPixelChannels],
1178                 image->error.normalized_mean_error,
1179                 image->error.normalized_maximum_error);
1180               break;
1181             }
1182             case UndefinedMetric:
1183               break;
1184           }
1185           channel_distortion=(double *) RelinquishMagickMemory(
1186             channel_distortion);
1187         }
1188       status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
1189       if ((metadata != (char **) NULL) && (format != (char *) NULL))
1190         {
1191           char
1192             *text;
1193
1194           text=InterpretImageProperties(image_info,difference_image,format,
1195             exception);
1196           if (text == (char *) NULL)
1197             ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1198               GetExceptionMessage(errno));
1199           (void) ConcatenateString(&(*metadata),text);
1200           (void) ConcatenateString(&(*metadata),"\n");
1201           text=DestroyString(text);
1202         }
1203       difference_image=DestroyImageList(difference_image);
1204     }
1205   DestroyCompare();
1206   return(status != 0 ? MagickTrue : MagickFalse);
1207 }