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