MagickWand  6.9.13-23
Convert, Edit, Or Compose Bitmap Images
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 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 % https://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 
41 /*
42  Include declarations.
43 */
44 #include "wand/studio.h"
45 #include "wand/MagickWand.h"
46 #include "wand/mogrify-private.h"
47 #include "magick/image-private.h"
48 #include "magick/string-private.h"
49 
50 /*
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 % %
53 % %
54 % %
55 % C o m p a r e I m a g e C o m m a n d %
56 % %
57 % %
58 % %
59 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60 %
61 % CompareImageCommand() compares two images and returns the difference between
62 % them as a distortion metric and as a new image visually annotating their
63 % differences.
64 %
65 % The format of the CompareImageCommand method is:
66 %
67 % MagickBooleanType CompareImageCommand(ImageInfo *image_info,int argc,
68 % char **argv,char **metadata,ExceptionInfo *exception)
69 %
70 % A description of each parameter follows:
71 %
72 % o image_info: the image info.
73 %
74 % o argc: the number of elements in the argument vector.
75 %
76 % o argv: A text array containing the command line arguments.
77 %
78 % o metadata: any metadata is returned here.
79 %
80 % o exception: return any errors or warnings in this structure.
81 %
82 */
83 
84 static MagickBooleanType CompareUsage(void)
85 {
86  static const char
87  miscellaneous[] =
88  " -debug events display copious debugging information\n"
89  " -help print program options\n"
90  " -list type print a list of supported option arguments\n"
91  " -log format format of debugging information",
92  operators[] =
93  " -brightness-contrast geometry\n"
94  " improve brightness / contrast of the image\n"
95  " -distort method args\n"
96  " distort images according to given method and args\n"
97  " -level value adjust the level of image contrast\n"
98  " -resize geometry resize the image\n"
99  " -rotate degrees apply Paeth rotation to the image\n"
100  " -sigmoidal-contrast geometry\n"
101  " increase the contrast without saturating highlights or\n"
102  " -trim trim image edges",
103  sequence_operators[] =
104  " -crop geometry cut out a rectangular region of the image\n"
105  " -separate separate an image channel into a grayscale image\n"
106  " -write filename write images to this file",
107  settings[] =
108  " -alpha option on, activate, off, deactivate, set, opaque, copy\n"
109  " transparent, extract, background, or shape\n"
110  " -authenticate password\n"
111  " decipher image with this password\n"
112  " -background color background color\n"
113  " -channel type apply option to select image channels\n"
114  " -colorspace type alternate image colorspace\n"
115  " -compose operator set image composite operator\n"
116  " -compress type type of pixel compression when writing the image\n"
117  " -decipher filename convert cipher pixels to plain pixels\n"
118  " -define format:option\n"
119  " define one or more image format options\n"
120  " -density geometry horizontal and vertical density of the image\n"
121  " -depth value image depth\n"
122  " -dissimilarity-threshold value\n"
123  " maximum distortion for (sub)image match\n"
124  " -encipher filename convert plain pixels to cipher pixels\n"
125  " -extract geometry extract area from image\n"
126  " -format \"string\" output formatted image characteristics\n"
127  " -fuzz distance colors within this distance are considered equal\n"
128  " -gravity type horizontal and vertical text placement\n"
129  " -highlight-color color\n"
130  " emphasize pixel differences with this color\n"
131  " -identify identify the format and characteristics of the image\n"
132  " -interlace type type of image interlacing scheme\n"
133  " -limit type value pixel cache resource limit\n"
134  " -lowlight-color color\n"
135  " de-emphasize pixel differences with this color\n"
136  " -mask filename associate a mask with the image\n"
137  " -metric type measure differences between images with this metric\n"
138  " -monitor monitor progress\n"
139  " -passphrase filename get the passphrase from this file\n"
140  " -precision value maximum number of significant digits to print\n"
141  " -profile filename add, delete, or apply an image profile\n"
142  " -quality value JPEG/MIFF/PNG compression level\n"
143  " -quiet suppress all warning messages\n"
144  " -quantize colorspace reduce colors in this colorspace\n"
145  " -regard-warnings pay attention to warning messages\n"
146  " -repage geometry size and location of an image canvas\n"
147  " -respect-parentheses settings remain in effect until parenthesis boundary\n"
148  " -sampling-factor geometry\n"
149  " horizontal and vertical sampling factor\n"
150  " -seed value seed a new sequence of pseudo-random numbers\n"
151  " -set attribute value set an image attribute\n"
152  " -quality value JPEG/MIFF/PNG compression level\n"
153  " -similarity-threshold value\n"
154  " minimum distortion for (sub)image match\n"
155  " -size geometry width and height of image\n"
156  " -subimage-search search for subimage\n"
157  " -synchronize synchronize image to storage device\n"
158  " -taint declare the image as modified\n"
159  " -transparent-color color\n"
160  " transparent color\n"
161  " -type type image type\n"
162  " -verbose print detailed information about the image\n"
163  " -version print version information\n"
164  " -virtual-pixel method\n"
165  " virtual pixel access method",
166  stack_operators[] =
167  " -delete indexes delete the image from the image sequence";
168 
169  ListMagickVersion(stdout);
170  (void) printf("Usage: %s [options ...] image reconstruct difference\n",
171  GetClientName());
172  (void) printf("\nImage Settings:\n");
173  (void) puts(settings);
174  (void) printf("\nImage Operators:\n");
175  (void) puts(operators);
176  (void) printf("\nImage Sequence Operators:\n");
177  (void) puts(sequence_operators);
178  (void) printf("\nImage Stack Operators:\n");
179  (void) puts(stack_operators);
180  (void) printf("\nMiscellaneous Options:\n");
181  (void) puts(miscellaneous);
182  (void) printf(
183  "\nBy default, the image format of `file' is determined by its magic\n");
184  (void) printf(
185  "number. To specify a particular image format, precede the filename\n");
186  (void) printf(
187  "with an image format name and a colon (i.e. ps:image) or specify the\n");
188  (void) printf(
189  "image type as the filename suffix (i.e. image.ps). Specify 'file' as\n");
190  (void) printf("'-' for standard input or output.\n");
191  return(MagickTrue);
192 }
193 
194 WandExport MagickBooleanType CompareImageCommand(ImageInfo *image_info,
195  int argc,char **argv,char **metadata,ExceptionInfo *exception)
196 {
197 #define CompareEpsilon (1.0e-06)
198 #define DefaultDissimilarityThreshold (1.0/MagickPI)
199 #define DefaultSimilarityThreshold (-1.0)
200 #define DestroyCompare() \
201 { \
202  if (similarity_image != (Image *) NULL) \
203  similarity_image=DestroyImageList(similarity_image); \
204  if (difference_image != (Image *) NULL) \
205  difference_image=DestroyImageList(difference_image); \
206  DestroyImageStack(); \
207  for (i=0; i < (ssize_t) argc; i++) \
208  argv[i]=DestroyString(argv[i]); \
209  argv=(char **) RelinquishMagickMemory(argv); \
210 }
211 #define ThrowCompareException(asperity,tag,option) \
212 { \
213  if (exception->severity < (asperity)) \
214  (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \
215  "`%s'",option); \
216  DestroyCompare(); \
217  return(MagickFalse); \
218 }
219 #define ThrowCompareInvalidArgumentException(option,argument) \
220 { \
221  (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \
222  "InvalidArgument","`%s': %s",option,argument); \
223  DestroyCompare(); \
224  return(MagickFalse); \
225 }
226 
227  char
228  *filename,
229  *option;
230 
231  const char
232  *format;
233 
234  ChannelType
235  channels;
236 
237  double
238  dissimilarity_threshold,
239  distortion,
240  similarity_metric,
241  similarity_threshold;
242 
243  Image
244  *difference_image,
245  *image = (Image *) NULL,
246  *reconstruct_image,
247  *similarity_image;
248 
249  ImageInfo
250  *restore_info;
251 
252  ImageStack
253  image_stack[MaxImageStackDepth+1];
254 
255  MagickBooleanType
256  fire,
257  pend,
258  respect_parenthesis,
259  similar = MagickTrue,
260  subimage_search;
261 
262  MagickStatusType
263  status;
264 
265  MetricType
266  metric;
267 
268  RectangleInfo
269  offset;
270 
271  ssize_t
272  i;
273 
274  ssize_t
275  j,
276  k;
277 
278  /*
279  Set defaults.
280  */
281  assert(image_info != (ImageInfo *) NULL);
282  assert(image_info->signature == MagickCoreSignature);
283  assert(exception != (ExceptionInfo *) NULL);
284  if (IsEventLogging() != MagickFalse)
285  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
286  if (argc == 2)
287  {
288  option=argv[1];
289  if ((LocaleCompare("version",option+1) == 0) ||
290  (LocaleCompare("-version",option+1) == 0))
291  {
292  ListMagickVersion(stdout);
293  return(MagickTrue);
294  }
295  }
296  if (argc < 3)
297  {
298  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
299  "MissingArgument","%s","");
300  (void) CompareUsage();
301  return(MagickFalse);
302  }
303  restore_info=image_info;
304  channels=DefaultChannels;
305  difference_image=NewImageList();
306  similarity_image=NewImageList();
307  dissimilarity_threshold=DefaultDissimilarityThreshold;
308  similarity_threshold=DefaultSimilarityThreshold;
309  distortion=0.0;
310  format=(char *) NULL;
311  j=1;
312  k=0;
313  metric=UndefinedErrorMetric;
314  NewImageStack();
315  option=(char *) NULL;
316  pend=MagickFalse;
317  reconstruct_image=NewImageList();
318  respect_parenthesis=MagickFalse;
319  status=MagickTrue;
320  subimage_search=MagickFalse;
321  /*
322  Compare an image.
323  */
324  ReadCommandlLine(argc,&argv);
325  status=ExpandFilenames(&argc,&argv);
326  if (status == MagickFalse)
327  ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
328  GetExceptionMessage(errno));
329  for (i=1; i < (ssize_t) (argc-1); i++)
330  {
331  option=argv[i];
332  if (LocaleCompare(option,"(") == 0)
333  {
334  FireImageStack(MagickTrue,MagickTrue,pend);
335  if (k == MaxImageStackDepth)
336  ThrowCompareException(OptionError,"ParenthesisNestedTooDeeply",
337  option);
338  PushImageStack();
339  continue;
340  }
341  if (LocaleCompare(option,")") == 0)
342  {
343  FireImageStack(MagickTrue,MagickTrue,MagickTrue);
344  if (k == 0)
345  ThrowCompareException(OptionError,"UnableToParseExpression",option);
346  PopImageStack();
347  continue;
348  }
349  if (IsCommandOption(option) == MagickFalse)
350  {
351  Image
352  *images;
353 
354  /*
355  Read input image.
356  */
357  FireImageStack(MagickFalse,MagickFalse,pend);
358  filename=argv[i];
359  if ((LocaleCompare(filename,"--") == 0) && (i < (ssize_t) (argc-1)))
360  filename=argv[++i];
361  (void) SetImageOption(image_info,"filename",filename);
362  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
363  images=ReadImages(image_info,exception);
364  status&=(images != (Image *) NULL) &&
365  (exception->severity < ErrorException);
366  if (images == (Image *) NULL)
367  continue;
368  AppendImageStack(images);
369  continue;
370  }
371  pend=image != (Image *) NULL ? MagickTrue : MagickFalse;
372  switch (*(option+1))
373  {
374  case 'a':
375  {
376  if (LocaleCompare("alpha",option+1) == 0)
377  {
378  ssize_t
379  type;
380 
381  if (*option == '+')
382  break;
383  i++;
384  if (i == (ssize_t) argc)
385  ThrowCompareException(OptionError,"MissingArgument",option);
386  type=ParseCommandOption(MagickAlphaOptions,MagickFalse,argv[i]);
387  if (type < 0)
388  ThrowCompareException(OptionError,"UnrecognizedAlphaChannelType",
389  argv[i]);
390  break;
391  }
392  if (LocaleCompare("authenticate",option+1) == 0)
393  {
394  if (*option == '+')
395  break;
396  i++;
397  if (i == (ssize_t) argc)
398  ThrowCompareException(OptionError,"MissingArgument",option);
399  break;
400  }
401  ThrowCompareException(OptionError,"UnrecognizedOption",option);
402  }
403  case 'b':
404  {
405  if (LocaleCompare("background",option+1) == 0)
406  {
407  if (*option == '+')
408  break;
409  i++;
410  if (i == (ssize_t) argc)
411  ThrowCompareException(OptionError,"MissingArgument",option);
412  break;
413  }
414  if (LocaleCompare("brightness-contrast",option+1) == 0)
415  {
416  i++;
417  if (i == (ssize_t) argc)
418  ThrowCompareException(OptionError,"MissingArgument",option);
419  if (IsGeometry(argv[i]) == MagickFalse)
420  ThrowCompareInvalidArgumentException(option,argv[i]);
421  break;
422  }
423  ThrowCompareException(OptionError,"UnrecognizedOption",option);
424  }
425  case 'c':
426  {
427  if (LocaleCompare("cache",option+1) == 0)
428  {
429  if (*option == '+')
430  break;
431  i++;
432  if (i == (ssize_t) argc)
433  ThrowCompareException(OptionError,"MissingArgument",option);
434  if (IsGeometry(argv[i]) == MagickFalse)
435  ThrowCompareInvalidArgumentException(option,argv[i]);
436  break;
437  }
438  if (LocaleCompare("channel",option+1) == 0)
439  {
440  ssize_t
441  channel;
442 
443  if (*option == '+')
444  break;
445  i++;
446  if (i == (ssize_t) argc)
447  ThrowCompareException(OptionError,"MissingArgument",option);
448  channel=ParseChannelOption(argv[i]);
449  if (channel < 0)
450  ThrowCompareException(OptionError,"UnrecognizedChannelType",
451  argv[i]);
452  channels=(ChannelType) channel;
453  break;
454  }
455  if (LocaleCompare("colorspace",option+1) == 0)
456  {
457  ssize_t
458  colorspace;
459 
460  if (*option == '+')
461  break;
462  i++;
463  if (i == (ssize_t) argc)
464  ThrowCompareException(OptionError,"MissingArgument",option);
465  colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
466  argv[i]);
467  if (colorspace < 0)
468  ThrowCompareException(OptionError,"UnrecognizedColorspace",
469  argv[i]);
470  break;
471  }
472  if (LocaleCompare("compose",option+1) == 0)
473  {
474  ssize_t
475  compose;
476 
477  if (*option == '+')
478  break;
479  i++;
480  if (i == (ssize_t) argc)
481  ThrowCompareException(OptionError,"MissingArgument",option);
482  compose=ParseCommandOption(MagickComposeOptions,MagickFalse,
483  argv[i]);
484  if (compose < 0)
485  ThrowCompareException(OptionError,"UnrecognizedComposeOperator",
486  argv[i]);
487  break;
488  }
489  if (LocaleCompare("compress",option+1) == 0)
490  {
491  ssize_t
492  compress;
493 
494  if (*option == '+')
495  break;
496  i++;
497  if (i == (ssize_t) argc)
498  ThrowCompareException(OptionError,"MissingArgument",option);
499  compress=ParseCommandOption(MagickCompressOptions,MagickFalse,
500  argv[i]);
501  if (compress < 0)
502  ThrowCompareException(OptionError,"UnrecognizedImageCompression",
503  argv[i]);
504  break;
505  }
506  if (LocaleCompare("concurrent",option+1) == 0)
507  break;
508  if (LocaleCompare("crop",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  ThrowCompareException(OptionError,"UnrecognizedOption",option)
520  }
521  case 'd':
522  {
523  if (LocaleCompare("debug",option+1) == 0)
524  {
525  LogEventType
526  event_mask;
527 
528  if (*option == '+')
529  break;
530  i++;
531  if (i == (ssize_t) argc)
532  ThrowCompareException(OptionError,"MissingArgument",option);
533  event_mask=SetLogEventMask(argv[i]);
534  if (event_mask == UndefinedEvents)
535  ThrowCompareException(OptionError,"UnrecognizedEventType",
536  argv[i]);
537  break;
538  }
539  if (LocaleCompare("decipher",option+1) == 0)
540  {
541  if (*option == '+')
542  break;
543  i++;
544  if (i == (ssize_t) argc)
545  ThrowCompareException(OptionError,"MissingArgument",option);
546  break;
547  }
548  if (LocaleCompare("define",option+1) == 0)
549  {
550  i++;
551  if (i == (ssize_t) argc)
552  ThrowCompareException(OptionError,"MissingArgument",option);
553  if (*option == '+')
554  {
555  const char
556  *define;
557 
558  define=GetImageOption(image_info,argv[i]);
559  if (define == (const char *) NULL)
560  ThrowCompareException(OptionError,"NoSuchOption",argv[i]);
561  break;
562  }
563  break;
564  }
565  if (LocaleCompare("delete",option+1) == 0)
566  {
567  if (*option == '+')
568  break;
569  i++;
570  if (i == (ssize_t) argc)
571  ThrowCompareException(OptionError,"MissingArgument",option);
572  if (IsSceneGeometry(argv[i],MagickFalse) == MagickFalse)
573  ThrowCompareInvalidArgumentException(option,argv[i]);
574  break;
575  }
576  if (LocaleCompare("density",option+1) == 0)
577  {
578  if (*option == '+')
579  break;
580  i++;
581  if (i == (ssize_t) argc)
582  ThrowCompareException(OptionError,"MissingArgument",option);
583  if (IsGeometry(argv[i]) == MagickFalse)
584  ThrowCompareInvalidArgumentException(option,argv[i]);
585  break;
586  }
587  if (LocaleCompare("depth",option+1) == 0)
588  {
589  if (*option == '+')
590  break;
591  i++;
592  if (i == (ssize_t) argc)
593  ThrowCompareException(OptionError,"MissingArgument",option);
594  if (IsGeometry(argv[i]) == MagickFalse)
595  ThrowCompareInvalidArgumentException(option,argv[i]);
596  break;
597  }
598  if (LocaleCompare("dissimilarity-threshold",option+1) == 0)
599  {
600  if (*option == '+')
601  break;
602  i++;
603  if (i == (ssize_t) argc)
604  ThrowCompareException(OptionError,"MissingArgument",option);
605  if (IsGeometry(argv[i]) == MagickFalse)
606  ThrowCompareInvalidArgumentException(option,argv[i]);
607  if (*option == '+')
608  dissimilarity_threshold=DefaultDissimilarityThreshold;
609  else
610  dissimilarity_threshold=StringToDouble(argv[i],(char **) NULL);
611  break;
612  }
613  if (LocaleCompare("distort",option+1) == 0)
614  {
615  ssize_t
616  op;
617 
618  i++;
619  if (i == (ssize_t) argc)
620  ThrowCompareException(OptionError,"MissingArgument",option);
621  op=ParseCommandOption(MagickDistortOptions,MagickFalse,argv[i]);
622  if (op < 0)
623  ThrowCompareException(OptionError,"UnrecognizedDistortMethod",
624  argv[i]);
625  i++;
626  if (i == (ssize_t) argc)
627  ThrowCompareException(OptionError,"MissingArgument",option);
628  break;
629  }
630  if (LocaleCompare("duration",option+1) == 0)
631  {
632  if (*option == '+')
633  break;
634  i++;
635  if (i == (ssize_t) argc)
636  ThrowCompareException(OptionError,"MissingArgument",option);
637  if (IsGeometry(argv[i]) == MagickFalse)
638  ThrowCompareInvalidArgumentException(option,argv[i]);
639  break;
640  }
641  ThrowCompareException(OptionError,"UnrecognizedOption",option)
642  }
643  case 'e':
644  {
645  if (LocaleCompare("encipher",option+1) == 0)
646  {
647  if (*option == '+')
648  break;
649  i++;
650  if (i == (ssize_t) argc)
651  ThrowCompareException(OptionError,"MissingArgument",option);
652  break;
653  }
654  if (LocaleCompare("extract",option+1) == 0)
655  {
656  if (*option == '+')
657  break;
658  i++;
659  if (i == (ssize_t) argc)
660  ThrowCompareException(OptionError,"MissingArgument",option);
661  if (IsGeometry(argv[i]) == MagickFalse)
662  ThrowCompareInvalidArgumentException(option,argv[i]);
663  break;
664  }
665  ThrowCompareException(OptionError,"UnrecognizedOption",option)
666  }
667  case 'f':
668  {
669  if (LocaleCompare("format",option+1) == 0)
670  {
671  if (*option == '+')
672  break;
673  i++;
674  if (i == (ssize_t) argc)
675  ThrowCompareException(OptionError,"MissingArgument",option);
676  format=argv[i];
677  break;
678  }
679  if (LocaleCompare("fuzz",option+1) == 0)
680  {
681  if (*option == '+')
682  break;
683  i++;
684  if (i == (ssize_t) argc)
685  ThrowCompareException(OptionError,"MissingArgument",option);
686  if (IsGeometry(argv[i]) == MagickFalse)
687  ThrowCompareInvalidArgumentException(option,argv[i]);
688  break;
689  }
690  ThrowCompareException(OptionError,"UnrecognizedOption",option)
691  }
692  case 'g':
693  {
694  if (LocaleCompare("gravity",option+1) == 0)
695  {
696  ssize_t
697  gravity;
698 
699  if (*option == '+')
700  break;
701  i++;
702  if (i == (ssize_t) argc)
703  ThrowCompareException(OptionError,"MissingArgument",option);
704  gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,
705  argv[i]);
706  if (gravity < 0)
707  ThrowCompareException(OptionError,"UnrecognizedGravityType",
708  argv[i]);
709  break;
710  }
711  ThrowCompareException(OptionError,"UnrecognizedOption",option)
712  }
713  case 'h':
714  {
715  if ((LocaleCompare("help",option+1) == 0) ||
716  (LocaleCompare("-help",option+1) == 0))
717  {
718  DestroyCompare();
719  return(CompareUsage());
720  }
721  if (LocaleCompare("highlight-color",option+1) == 0)
722  {
723  if (*option == '+')
724  break;
725  i++;
726  if (i == (ssize_t) argc)
727  ThrowCompareException(OptionError,"MissingArgument",option);
728  break;
729  }
730  ThrowCompareException(OptionError,"UnrecognizedOption",option)
731  }
732  case 'i':
733  {
734  if (LocaleCompare("identify",option+1) == 0)
735  break;
736  if (LocaleCompare("interlace",option+1) == 0)
737  {
738  ssize_t
739  interlace;
740 
741  if (*option == '+')
742  break;
743  i++;
744  if (i == (ssize_t) argc)
745  ThrowCompareException(OptionError,"MissingArgument",option);
746  interlace=ParseCommandOption(MagickInterlaceOptions,MagickFalse,
747  argv[i]);
748  if (interlace < 0)
749  ThrowCompareException(OptionError,"UnrecognizedInterlaceType",
750  argv[i]);
751  break;
752  }
753  ThrowCompareException(OptionError,"UnrecognizedOption",option)
754  }
755  case 'l':
756  {
757  if (LocaleCompare("level",option+1) == 0)
758  {
759  i++;
760  if (i == (ssize_t) argc)
761  ThrowCompareException(OptionError,"MissingArgument",option);
762  if (IsGeometry(argv[i]) == MagickFalse)
763  ThrowCompareInvalidArgumentException(option,argv[i]);
764  break;
765  }
766  if (LocaleCompare("limit",option+1) == 0)
767  {
768  char
769  *p;
770 
771  double
772  value;
773 
774  ssize_t
775  resource;
776 
777  if (*option == '+')
778  break;
779  i++;
780  if (i == (ssize_t) argc)
781  ThrowCompareException(OptionError,"MissingArgument",option);
782  resource=ParseCommandOption(MagickResourceOptions,MagickFalse,
783  argv[i]);
784  if (resource < 0)
785  ThrowCompareException(OptionError,"UnrecognizedResourceType",
786  argv[i]);
787  i++;
788  if (i == (ssize_t) argc)
789  ThrowCompareException(OptionError,"MissingArgument",option);
790  value=StringToDouble(argv[i],&p);
791  (void) value;
792  if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
793  ThrowCompareInvalidArgumentException(option,argv[i]);
794  break;
795  }
796  if (LocaleCompare("list",option+1) == 0)
797  {
798  ssize_t
799  list;
800 
801  if (*option == '+')
802  break;
803  i++;
804  if (i == (ssize_t) argc)
805  ThrowCompareException(OptionError,"MissingArgument",option);
806  list=ParseCommandOption(MagickListOptions,MagickFalse,argv[i]);
807  if (list < 0)
808  ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
809  status=MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
810  argv+j,exception);
811  DestroyCompare();
812  return(status == 0 ? MagickFalse : MagickTrue);
813  }
814  if (LocaleCompare("log",option+1) == 0)
815  {
816  if (*option == '+')
817  break;
818  i++;
819  if ((i == (ssize_t) argc) || (strchr(argv[i],'%') == (char *) NULL))
820  ThrowCompareException(OptionError,"MissingArgument",option);
821  break;
822  }
823  if (LocaleCompare("lowlight-color",option+1) == 0)
824  {
825  if (*option == '+')
826  break;
827  i++;
828  if (i == (ssize_t) argc)
829  ThrowCompareException(OptionError,"MissingArgument",option);
830  break;
831  }
832  ThrowCompareException(OptionError,"UnrecognizedOption",option)
833  }
834  case 'm':
835  {
836  if (LocaleCompare("mask",option+1) == 0)
837  {
838  if (*option == '+')
839  break;
840  i++;
841  if (i == (ssize_t) argc)
842  ThrowCompareException(OptionError,"MissingArgument",option);
843  break;
844  }
845  if (LocaleCompare("matte",option+1) == 0)
846  break;
847  if (LocaleCompare("metric",option+1) == 0)
848  {
849  ssize_t
850  type;
851 
852  if (*option == '+')
853  break;
854  i++;
855  if (i == (ssize_t) argc)
856  ThrowCompareException(OptionError,"MissingArgument",option);
857  type=ParseCommandOption(MagickMetricOptions,MagickTrue,argv[i]);
858  if (type < 0)
859  ThrowCompareException(OptionError,"UnrecognizedMetricType",
860  argv[i]);
861  metric=(MetricType) type;
862  break;
863  }
864  if (LocaleCompare("monitor",option+1) == 0)
865  break;
866  ThrowCompareException(OptionError,"UnrecognizedOption",option)
867  }
868  case 'p':
869  {
870  if (LocaleCompare("precision",option+1) == 0)
871  {
872  if (*option == '+')
873  break;
874  i++;
875  if (i == (ssize_t) argc)
876  ThrowCompareException(OptionError,"MissingArgument",option);
877  if (IsGeometry(argv[i]) == MagickFalse)
878  ThrowCompareInvalidArgumentException(option,argv[i]);
879  break;
880  }
881  if (LocaleCompare("passphrase",option+1) == 0)
882  {
883  if (*option == '+')
884  break;
885  i++;
886  if (i == (ssize_t) argc)
887  ThrowCompareException(OptionError,"MissingArgument",option);
888  break;
889  }
890  if (LocaleCompare("profile",option+1) == 0)
891  {
892  i++;
893  if (i == (ssize_t) argc)
894  ThrowCompareException(OptionError,"MissingArgument",option);
895  break;
896  }
897  ThrowCompareException(OptionError,"UnrecognizedOption",option)
898  }
899  case 'q':
900  {
901  if (LocaleCompare("quality",option+1) == 0)
902  {
903  if (*option == '+')
904  break;
905  i++;
906  if (i == (ssize_t) argc)
907  ThrowCompareException(OptionError,"MissingArgument",option);
908  if (IsGeometry(argv[i]) == MagickFalse)
909  ThrowCompareInvalidArgumentException(option,argv[i]);
910  break;
911  }
912  if (LocaleCompare("quantize",option+1) == 0)
913  {
914  ssize_t
915  colorspace;
916 
917  if (*option == '+')
918  break;
919  i++;
920  if (i == (ssize_t) argc)
921  ThrowCompareException(OptionError,"MissingArgument",option);
922  colorspace=ParseCommandOption(MagickColorspaceOptions,
923  MagickFalse,argv[i]);
924  if (colorspace < 0)
925  ThrowCompareException(OptionError,"UnrecognizedColorspace",
926  argv[i]);
927  break;
928  }
929  if (LocaleCompare("quiet",option+1) == 0)
930  break;
931  ThrowCompareException(OptionError,"UnrecognizedOption",option)
932  }
933  case 'r':
934  {
935  if (LocaleCompare("regard-warnings",option+1) == 0)
936  break;
937  if (LocaleCompare("repage",option+1) == 0)
938  {
939  if (*option == '+')
940  break;
941  i++;
942  if (i == (ssize_t) argc)
943  ThrowCompareException(OptionError,"MissingArgument",option);
944  if (IsGeometry(argv[i]) == MagickFalse)
945  ThrowCompareInvalidArgumentException(option,argv[i]);
946  break;
947  }
948  if (LocaleCompare("resize",option+1) == 0)
949  {
950  if (*option == '+')
951  break;
952  i++;
953  if (i == (ssize_t) argc)
954  ThrowCompareException(OptionError,"MissingArgument",option);
955  if (IsGeometry(argv[i]) == MagickFalse)
956  ThrowCompareInvalidArgumentException(option,argv[i]);
957  break;
958  }
959  if (LocaleCompare("rotate",option+1) == 0)
960  {
961  i++;
962  if (i == (ssize_t) argc)
963  ThrowCompareException(OptionError,"MissingArgument",option);
964  if (IsGeometry(argv[i]) == MagickFalse)
965  ThrowCompareInvalidArgumentException(option,argv[i]);
966  break;
967  }
968  if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
969  {
970  respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
971  break;
972  }
973  ThrowCompareException(OptionError,"UnrecognizedOption",option)
974  }
975  case 's':
976  {
977  if (LocaleCompare("sampling-factor",option+1) == 0)
978  {
979  if (*option == '+')
980  break;
981  i++;
982  if (i == (ssize_t) argc)
983  ThrowCompareException(OptionError,"MissingArgument",option);
984  if (IsGeometry(argv[i]) == MagickFalse)
985  ThrowCompareInvalidArgumentException(option,argv[i]);
986  break;
987  }
988  if (LocaleCompare("seed",option+1) == 0)
989  {
990  if (*option == '+')
991  break;
992  i++;
993  if (i == (ssize_t) argc)
994  ThrowCompareException(OptionError,"MissingArgument",option);
995  if (IsGeometry(argv[i]) == MagickFalse)
996  ThrowCompareInvalidArgumentException(option,argv[i]);
997  break;
998  }
999  if (LocaleCompare("separate",option+1) == 0)
1000  break;
1001  if (LocaleCompare("set",option+1) == 0)
1002  {
1003  i++;
1004  if (i == (ssize_t) argc)
1005  ThrowCompareException(OptionError,"MissingArgument",option);
1006  if (*option == '+')
1007  break;
1008  i++;
1009  if (i == (ssize_t) argc)
1010  ThrowCompareException(OptionError,"MissingArgument",option);
1011  break;
1012  }
1013  if (LocaleCompare("sigmoidal-contrast",option+1) == 0)
1014  {
1015  i++;
1016  if (i == (ssize_t) argc)
1017  ThrowCompareException(OptionError,"MissingArgument",option);
1018  if (IsGeometry(argv[i]) == MagickFalse)
1019  ThrowCompareInvalidArgumentException(option,argv[i]);
1020  break;
1021  }
1022  if (LocaleCompare("similarity-threshold",option+1) == 0)
1023  {
1024  if (*option == '+')
1025  break;
1026  i++;
1027  if (i == (ssize_t) argc)
1028  ThrowCompareException(OptionError,"MissingArgument",option);
1029  if (IsGeometry(argv[i]) == MagickFalse)
1030  ThrowCompareInvalidArgumentException(option,argv[i]);
1031  if (*option == '+')
1032  similarity_threshold=DefaultSimilarityThreshold;
1033  else
1034  similarity_threshold=StringToDouble(argv[i],(char **) NULL);
1035  break;
1036  }
1037  if (LocaleCompare("size",option+1) == 0)
1038  {
1039  if (*option == '+')
1040  break;
1041  i++;
1042  if (i == (ssize_t) argc)
1043  ThrowCompareException(OptionError,"MissingArgument",option);
1044  if (IsGeometry(argv[i]) == MagickFalse)
1045  ThrowCompareInvalidArgumentException(option,argv[i]);
1046  break;
1047  }
1048  if (LocaleCompare("subimage-search",option+1) == 0)
1049  {
1050  if (*option == '+')
1051  {
1052  subimage_search=MagickFalse;
1053  break;
1054  }
1055  subimage_search=MagickTrue;
1056  break;
1057  }
1058  if (LocaleCompare("synchronize",option+1) == 0)
1059  break;
1060  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1061  }
1062  case 't':
1063  {
1064  if (LocaleCompare("taint",option+1) == 0)
1065  break;
1066  if (LocaleCompare("transparent-color",option+1) == 0)
1067  {
1068  if (*option == '+')
1069  break;
1070  i++;
1071  if (i == (ssize_t) argc)
1072  ThrowCompareException(OptionError,"MissingArgument",option);
1073  break;
1074  }
1075  if (LocaleCompare("trim",option+1) == 0)
1076  break;
1077  if (LocaleCompare("type",option+1) == 0)
1078  {
1079  ssize_t
1080  type;
1081 
1082  if (*option == '+')
1083  break;
1084  i++;
1085  if (i == (ssize_t) argc)
1086  ThrowCompareException(OptionError,"MissingArgument",option);
1087  type=ParseCommandOption(MagickTypeOptions,MagickFalse,argv[i]);
1088  if (type < 0)
1089  ThrowCompareException(OptionError,"UnrecognizedImageType",
1090  argv[i]);
1091  break;
1092  }
1093  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1094  }
1095  case 'v':
1096  {
1097  if (LocaleCompare("verbose",option+1) == 0)
1098  break;
1099  if ((LocaleCompare("version",option+1) == 0) ||
1100  (LocaleCompare("-version",option+1) == 0))
1101  {
1102  ListMagickVersion(stdout);
1103  break;
1104  }
1105  if (LocaleCompare("virtual-pixel",option+1) == 0)
1106  {
1107  ssize_t
1108  method;
1109 
1110  if (*option == '+')
1111  break;
1112  i++;
1113  if (i == (ssize_t) argc)
1114  ThrowCompareException(OptionError,"MissingArgument",option);
1115  method=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
1116  argv[i]);
1117  if (method < 0)
1118  ThrowCompareException(OptionError,
1119  "UnrecognizedVirtualPixelMethod",argv[i]);
1120  break;
1121  }
1122  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1123  }
1124  case 'w':
1125  {
1126  if (LocaleCompare("write",option+1) == 0)
1127  {
1128  i++;
1129  if (i == (ssize_t) argc)
1130  ThrowCompareException(OptionError,"MissingArgument",option);
1131  break;
1132  }
1133  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1134  }
1135  case '?':
1136  break;
1137  default:
1138  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1139  }
1140  fire=(GetCommandOptionFlags(MagickCommandOptions,MagickFalse,option) &
1141  FireOptionFlag) == 0 ? MagickFalse : MagickTrue;
1142  if (fire != MagickFalse)
1143  FireImageStack(MagickTrue,MagickTrue,MagickTrue);
1144  }
1145  if (k != 0)
1146  ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
1147  if (i-- != (ssize_t) (argc-1))
1148  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1149  if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1150  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1151  FinalizeImageSettings(image_info,image,MagickTrue);
1152  if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1153  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1154  image=GetImageFromList(image,0);
1155  reconstruct_image=GetImageFromList(image,1);
1156  offset.x=0;
1157  offset.y=0;
1158  if (subimage_search != MagickFalse)
1159  {
1160  char
1161  artifact[MaxTextExtent];
1162 
1163  (void) FormatLocaleString(artifact,MaxTextExtent,"%g",
1164  similarity_threshold);
1165  (void) SetImageArtifact(image,"compare:similarity-threshold",artifact);
1166  similarity_image=SimilarityMetricImage(image,reconstruct_image,metric,
1167  &offset,&similarity_metric,exception);
1168  if (similarity_metric > dissimilarity_threshold)
1169  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1170  "ImagesTooDissimilar","`%s'",image->filename);
1171  }
1172  if (similarity_image == (Image *) NULL)
1173  difference_image=CompareImageChannels(image,reconstruct_image,channels,
1174  metric,&distortion,exception);
1175  else
1176  {
1177  Image
1178  *composite_image;
1179 
1180  /*
1181  Determine if reconstructed image is a subimage of the image.
1182  */
1183  composite_image=CloneImage(image,0,0,MagickTrue,exception);
1184  if (composite_image == (Image *) NULL)
1185  difference_image=CompareImageChannels(image,reconstruct_image,
1186  channels,metric,&distortion,exception);
1187  else
1188  {
1189  Image
1190  *distort_image;
1191 
1192  RectangleInfo
1193  page;
1194 
1195  (void) CompositeImage(composite_image,CopyCompositeOp,
1196  reconstruct_image,offset.x,offset.y);
1197  difference_image=CompareImageChannels(image,composite_image,
1198  channels,metric,&distortion,exception);
1199  if (difference_image != (Image *) NULL)
1200  {
1201  difference_image->page.x=offset.x;
1202  difference_image->page.y=offset.y;
1203  }
1204  composite_image=DestroyImage(composite_image);
1205  page.width=reconstruct_image->columns;
1206  page.height=reconstruct_image->rows;
1207  page.x=offset.x;
1208  page.y=offset.y;
1209  distort_image=CropImage(image,&page,exception);
1210  if (distort_image != (Image *) NULL)
1211  {
1212  Image
1213  *sans_image;
1214 
1215  sans_image=CompareImageChannels(distort_image,reconstruct_image,
1216  channels,metric,&distortion,exception);
1217  distort_image=DestroyImage(distort_image);
1218  if (sans_image != (Image *) NULL)
1219  sans_image=DestroyImage(sans_image);
1220  }
1221  }
1222  if (difference_image != (Image *) NULL)
1223  {
1224  AppendImageToList(&difference_image,similarity_image);
1225  similarity_image=(Image *) NULL;
1226  }
1227  }
1228  if (fabs(distortion) > CompareEpsilon)
1229  similar=MagickFalse;
1230  if (metric == NormalizedCrossCorrelationErrorMetric)
1231  {
1232  distortion=1.0-distortion;
1233  similarity_metric=1.0-similarity_metric;
1234  }
1235  if (difference_image == (Image *) NULL)
1236  status=0;
1237  else
1238  {
1239  if (image_info->verbose != MagickFalse)
1240  (void) IsImagesEqual(image,reconstruct_image);
1241  if (*difference_image->magick == '\0')
1242  (void) CopyMagickString(difference_image->magick,image->magick,
1243  MaxTextExtent);
1244  if (image_info->verbose == MagickFalse)
1245  {
1246  switch (metric)
1247  {
1248  case FuzzErrorMetric:
1249  case MeanAbsoluteErrorMetric:
1250  case MeanSquaredErrorMetric:
1251  case PeakAbsoluteErrorMetric:
1252  case RootMeanSquaredErrorMetric:
1253  case AbsoluteErrorMetric:
1254  case NormalizedCrossCorrelationErrorMetric:
1255  case PerceptualHashErrorMetric:
1256  {
1257  (void) FormatLocaleFile(stderr,"%.*g (%.*g)",GetMagickPrecision(),
1258  (double) QuantumRange*distortion,GetMagickPrecision(),
1259  distortion);
1260  break;
1261  }
1262  case PeakSignalToNoiseRatioMetric:
1263  {
1264  (void) FormatLocaleFile(stderr,"%.*g (%.*g)",GetMagickPrecision(),
1265  (double) QuantumRange*distortion,GetMagickPrecision(),
1266  0.01*distortion);
1267  break;
1268  }
1269  case MeanErrorPerPixelMetric:
1270  {
1271  (void) FormatLocaleFile(stderr,"%.*g (%.*g, %.*g)",
1272  GetMagickPrecision(),distortion,
1273  GetMagickPrecision(),image->error.normalized_mean_error,
1274  GetMagickPrecision(),image->error.normalized_maximum_error);
1275  break;
1276  }
1277  case UndefinedErrorMetric:
1278  break;
1279  }
1280  if (subimage_search != MagickFalse)
1281  (void) FormatLocaleFile(stderr," @ %.20g,%.20g [%.*g]",(double)
1282  difference_image->page.x,(double) difference_image->page.y,
1283  GetMagickPrecision(),similarity_metric);
1284  }
1285  else
1286  {
1287  double
1288  *channel_distortion;
1289 
1290  channel_distortion=GetImageChannelDistortions(image,reconstruct_image,
1291  metric,&image->exception);
1292  (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename);
1293  if ((reconstruct_image->columns != image->columns) ||
1294  (reconstruct_image->rows != image->rows))
1295  (void) FormatLocaleFile(stderr,"Offset: %.20g,%.20g\n",(double)
1296  difference_image->page.x,(double) difference_image->page.y);
1297  (void) FormatLocaleFile(stderr," Channel distortion: %s\n",
1298  CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1299  switch (metric)
1300  {
1301  case FuzzErrorMetric:
1302  case MeanAbsoluteErrorMetric:
1303  case MeanSquaredErrorMetric:
1304  case PeakAbsoluteErrorMetric:
1305  case RootMeanSquaredErrorMetric:
1306  {
1307  switch (image->colorspace)
1308  {
1309  case RGBColorspace:
1310  default:
1311  {
1312  (void) FormatLocaleFile(stderr," red: %.*g (%.*g)\n",
1313  GetMagickPrecision(),(double) QuantumRange*
1314  channel_distortion[RedChannel],GetMagickPrecision(),
1315  channel_distortion[RedChannel]);
1316  (void) FormatLocaleFile(stderr," green: %.*g (%.*g)\n",
1317  GetMagickPrecision(),(double) QuantumRange*
1318  channel_distortion[GreenChannel],GetMagickPrecision(),
1319  channel_distortion[GreenChannel]);
1320  (void) FormatLocaleFile(stderr," blue: %.*g (%.*g)\n",
1321  GetMagickPrecision(),(double) QuantumRange*
1322  channel_distortion[BlueChannel],GetMagickPrecision(),
1323  channel_distortion[BlueChannel]);
1324  if (image->matte != MagickFalse)
1325  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1326  GetMagickPrecision(),(double) QuantumRange*
1327  channel_distortion[OpacityChannel],GetMagickPrecision(),
1328  channel_distortion[OpacityChannel]);
1329  break;
1330  }
1331  case CMYKColorspace:
1332  {
1333  (void) FormatLocaleFile(stderr," cyan: %.*g (%.*g)\n",
1334  GetMagickPrecision(),(double) QuantumRange*
1335  channel_distortion[CyanChannel],GetMagickPrecision(),
1336  channel_distortion[CyanChannel]);
1337  (void) FormatLocaleFile(stderr," magenta: %.*g (%.*g)\n",
1338  GetMagickPrecision(),(double) QuantumRange*
1339  channel_distortion[MagentaChannel],GetMagickPrecision(),
1340  channel_distortion[MagentaChannel]);
1341  (void) FormatLocaleFile(stderr," yellow: %.*g (%.*g)\n",
1342  GetMagickPrecision(),(double) QuantumRange*
1343  channel_distortion[YellowChannel],GetMagickPrecision(),
1344  channel_distortion[YellowChannel]);
1345  (void) FormatLocaleFile(stderr," black: %.*g (%.*g)\n",
1346  GetMagickPrecision(),(double) QuantumRange*
1347  channel_distortion[BlackChannel],GetMagickPrecision(),
1348  channel_distortion[BlackChannel]);
1349  if (image->matte != MagickFalse)
1350  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1351  GetMagickPrecision(),(double) QuantumRange*
1352  channel_distortion[OpacityChannel],GetMagickPrecision(),
1353  channel_distortion[OpacityChannel]);
1354  break;
1355  }
1356  case LinearGRAYColorspace:
1357  case GRAYColorspace:
1358  {
1359  (void) FormatLocaleFile(stderr," gray: %.*g (%.*g)\n",
1360  GetMagickPrecision(),(double) QuantumRange*
1361  channel_distortion[GrayChannel],GetMagickPrecision(),
1362  channel_distortion[GrayChannel]);
1363  if (image->matte != MagickFalse)
1364  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1365  GetMagickPrecision(),(double) QuantumRange*
1366  channel_distortion[OpacityChannel],GetMagickPrecision(),
1367  channel_distortion[OpacityChannel]);
1368  break;
1369  }
1370  }
1371  (void) FormatLocaleFile(stderr," all: %.*g (%.*g)\n",
1372  GetMagickPrecision(),(double) QuantumRange*
1373  channel_distortion[CompositeChannels],GetMagickPrecision(),
1374  channel_distortion[CompositeChannels]);
1375  break;
1376  }
1377  case AbsoluteErrorMetric:
1378  case NormalizedCrossCorrelationErrorMetric:
1379  case PeakSignalToNoiseRatioMetric:
1380  case PerceptualHashErrorMetric:
1381  {
1382  switch (image->colorspace)
1383  {
1384  case RGBColorspace:
1385  default:
1386  {
1387  (void) FormatLocaleFile(stderr," red: %.*g\n",
1388  GetMagickPrecision(),channel_distortion[RedChannel]);
1389  (void) FormatLocaleFile(stderr," green: %.*g\n",
1390  GetMagickPrecision(),channel_distortion[GreenChannel]);
1391  (void) FormatLocaleFile(stderr," blue: %.*g\n",
1392  GetMagickPrecision(),channel_distortion[BlueChannel]);
1393  if (image->matte != MagickFalse)
1394  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1395  GetMagickPrecision(),channel_distortion[OpacityChannel]);
1396  break;
1397  }
1398  case CMYKColorspace:
1399  {
1400  (void) FormatLocaleFile(stderr," cyan: %.*g\n",
1401  GetMagickPrecision(),channel_distortion[CyanChannel]);
1402  (void) FormatLocaleFile(stderr," magenta: %.*g\n",
1403  GetMagickPrecision(),channel_distortion[MagentaChannel]);
1404  (void) FormatLocaleFile(stderr," yellow: %.*g\n",
1405  GetMagickPrecision(),channel_distortion[YellowChannel]);
1406  (void) FormatLocaleFile(stderr," black: %.*g\n",
1407  GetMagickPrecision(),channel_distortion[BlackChannel]);
1408  if (image->matte != MagickFalse)
1409  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1410  GetMagickPrecision(),channel_distortion[OpacityChannel]);
1411  break;
1412  }
1413  case LinearGRAYColorspace:
1414  case GRAYColorspace:
1415  {
1416  (void) FormatLocaleFile(stderr," gray: %.*g\n",
1417  GetMagickPrecision(),channel_distortion[GrayChannel]);
1418  if (image->matte != MagickFalse)
1419  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1420  GetMagickPrecision(),channel_distortion[OpacityChannel]);
1421  break;
1422  }
1423  }
1424  (void) FormatLocaleFile(stderr," all: %.*g\n",
1425  GetMagickPrecision(),channel_distortion[CompositeChannels]);
1426  break;
1427  }
1428  case MeanErrorPerPixelMetric:
1429  {
1430  (void) FormatLocaleFile(stderr," %.*g (%.*g, %.*g)\n",
1431  GetMagickPrecision(),channel_distortion[CompositeChannels],
1432  GetMagickPrecision(),image->error.normalized_mean_error,
1433  GetMagickPrecision(),image->error.normalized_maximum_error);
1434  break;
1435  }
1436  case UndefinedErrorMetric:
1437  break;
1438  }
1439  channel_distortion=(double *) RelinquishMagickMemory(
1440  channel_distortion);
1441  if (subimage_search != MagickFalse)
1442  {
1443  (void) FormatLocaleFile(stderr," Offset: %.20g,%.20g\n",
1444  (double) difference_image->page.x,(double)
1445  difference_image->page.y);
1446  (void) FormatLocaleFile(stderr," Similarity metric: %.*g\n",
1447  GetMagickPrecision(),similarity_metric);
1448  }
1449  }
1450  (void) ResetImagePage(difference_image,"0x0+0+0");
1451  if (difference_image->next != (Image *) NULL)
1452  (void) ResetImagePage(difference_image->next,"0x0+0+0");
1453  status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
1454  if ((metadata != (char **) NULL) && (format != (char *) NULL))
1455  {
1456  char
1457  *text;
1458 
1459  text=InterpretImageProperties(image_info,difference_image,format);
1460  InheritException(exception,&image->exception);
1461  if (text == (char *) NULL)
1462  ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1463  GetExceptionMessage(errno));
1464  (void) ConcatenateString(&(*metadata),text);
1465  text=DestroyString(text);
1466  }
1467  difference_image=DestroyImageList(difference_image);
1468  }
1469  DestroyCompare();
1470  image_info=restore_info;
1471  if (similar == MagickFalse)
1472  (void) SetImageOption(image_info,"compare:dissimilar","true");
1473  return(status != 0 ? MagickTrue : MagickFalse);
1474 }