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