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