MagickWand  6.9.13-26
Convert, Edit, Or Compose Bitmap Images
compare.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % CCCC OOO M M PPPP AAA RRRR EEEEE %
7 % C O O MM MM P P A A R R E %
8 % C O O M M M PPPP AAAAA RRRR EEE %
9 % C O O M M P A A R R E %
10 % CCCC OOO M M P A A R R EEEEE %
11 % %
12 % %
13 % Image Comparison Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % December 2003 %
18 % %
19 % %
20 % Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % Use the compare program to mathematically and visually annotate the
37 % difference between an image and its reconstruction.
38 %
39 */
40 
41 /*
42  Include declarations.
43 */
44 #include "wand/studio.h"
45 #include "wand/MagickWand.h"
46 #include "wand/mogrify-private.h"
47 #include "magick/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  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  ChannelType
233  channels = DefaultChannels;
234 
235  char
236  *filename,
237  *option;
238 
239  const char
240  *format;
241 
242  double
243  dissimilarity_threshold = DefaultDissimilarityThreshold,
244  distortion = 0.0,
245  scale = (double) QuantumRange,
246  similarity_metric = 0.0,
247  similarity_threshold = DefaultSimilarityThreshold;
248 
249  Image
250  *difference_image,
251  *image = (Image *) NULL,
252  *reconstruct_image,
253  *similarity_image;
254 
255  ImageInfo
256  *restore_info;
257 
258  ImageStack
259  image_stack[MaxImageStackDepth+1];
260 
261  MagickBooleanType
262  fire,
263  pend,
264  respect_parenthesis,
265  similar = MagickTrue,
266  subimage_search;
267 
268  MagickStatusType
269  status;
270 
271  MetricType
272  metric = UndefinedErrorMetric;
273 
274  RectangleInfo
275  offset;
276 
277  ssize_t
278  i;
279 
280  ssize_t
281  j,
282  k;
283 
284  /*
285  Set defaults.
286  */
287  assert(image_info != (ImageInfo *) NULL);
288  assert(image_info->signature == MagickCoreSignature);
289  assert(exception != (ExceptionInfo *) NULL);
290  if (IsEventLogging() != MagickFalse)
291  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
292  if (argc == 2)
293  {
294  option=argv[1];
295  if ((LocaleCompare("version",option+1) == 0) ||
296  (LocaleCompare("-version",option+1) == 0))
297  {
298  ListMagickVersion(stdout);
299  return(MagickTrue);
300  }
301  }
302  if (argc < 3)
303  {
304  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
305  "MissingArgument","%s","");
306  (void) CompareUsage();
307  return(MagickFalse);
308  }
309  restore_info=image_info;
310  difference_image=NewImageList();
311  similarity_image=NewImageList();
312  format=(char *) NULL;
313  j=1;
314  k=0;
315  NewImageStack();
316  option=(char *) NULL;
317  pend=MagickFalse;
318  reconstruct_image=NewImageList();
319  respect_parenthesis=MagickFalse;
320  status=MagickTrue;
321  subimage_search=MagickFalse;
322  /*
323  Compare an image.
324  */
325  ReadCommandlLine(argc,&argv);
326  status=ExpandFilenames(&argc,&argv);
327  if (status == MagickFalse)
328  ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
329  GetExceptionMessage(errno));
330  for (i=1; i < (ssize_t) (argc-1); i++)
331  {
332  option=argv[i];
333  if (LocaleCompare(option,"(") == 0)
334  {
335  FireImageStack(MagickTrue,MagickTrue,pend);
336  if (k == MaxImageStackDepth)
337  ThrowCompareException(OptionError,"ParenthesisNestedTooDeeply",
338  option);
339  PushImageStack();
340  continue;
341  }
342  if (LocaleCompare(option,")") == 0)
343  {
344  FireImageStack(MagickTrue,MagickTrue,MagickTrue);
345  if (k == 0)
346  ThrowCompareException(OptionError,"UnableToParseExpression",option);
347  PopImageStack();
348  continue;
349  }
350  if (IsCommandOption(option) == MagickFalse)
351  {
352  Image
353  *images;
354 
355  /*
356  Read input image.
357  */
358  FireImageStack(MagickFalse,MagickFalse,pend);
359  filename=argv[i];
360  if ((LocaleCompare(filename,"--") == 0) && (i < (ssize_t) (argc-1)))
361  filename=argv[++i];
362  (void) SetImageOption(image_info,"filename",filename);
363  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
364  images=ReadImages(image_info,exception);
365  status&=(images != (Image *) NULL) &&
366  (exception->severity < ErrorException);
367  if (images == (Image *) NULL)
368  continue;
369  AppendImageStack(images);
370  continue;
371  }
372  pend=image != (Image *) NULL ? MagickTrue : MagickFalse;
373  switch (*(option+1))
374  {
375  case 'a':
376  {
377  if (LocaleCompare("adjoin",option+1) == 0)
378  break;
379  if (LocaleCompare("alpha",option+1) == 0)
380  {
381  ssize_t
382  type;
383 
384  if (*option == '+')
385  break;
386  i++;
387  if (i == (ssize_t) argc)
388  ThrowCompareException(OptionError,"MissingArgument",option);
389  type=ParseCommandOption(MagickAlphaOptions,MagickFalse,argv[i]);
390  if (type < 0)
391  ThrowCompareException(OptionError,"UnrecognizedAlphaChannelType",
392  argv[i]);
393  break;
394  }
395  if (LocaleCompare("authenticate",option+1) == 0)
396  {
397  if (*option == '+')
398  break;
399  i++;
400  if (i == (ssize_t) argc)
401  ThrowCompareException(OptionError,"MissingArgument",option);
402  break;
403  }
404  ThrowCompareException(OptionError,"UnrecognizedOption",option);
405  }
406  case 'b':
407  {
408  if (LocaleCompare("background",option+1) == 0)
409  {
410  if (*option == '+')
411  break;
412  i++;
413  if (i == (ssize_t) argc)
414  ThrowCompareException(OptionError,"MissingArgument",option);
415  break;
416  }
417  if (LocaleCompare("brightness-contrast",option+1) == 0)
418  {
419  i++;
420  if (i == (ssize_t) argc)
421  ThrowCompareException(OptionError,"MissingArgument",option);
422  if (IsGeometry(argv[i]) == MagickFalse)
423  ThrowCompareInvalidArgumentException(option,argv[i]);
424  break;
425  }
426  ThrowCompareException(OptionError,"UnrecognizedOption",option);
427  }
428  case 'c':
429  {
430  if (LocaleCompare("cache",option+1) == 0)
431  {
432  if (*option == '+')
433  break;
434  i++;
435  if (i == (ssize_t) argc)
436  ThrowCompareException(OptionError,"MissingArgument",option);
437  if (IsGeometry(argv[i]) == MagickFalse)
438  ThrowCompareInvalidArgumentException(option,argv[i]);
439  break;
440  }
441  if (LocaleCompare("channel",option+1) == 0)
442  {
443  ssize_t
444  channel;
445 
446  if (*option == '+')
447  break;
448  i++;
449  if (i == (ssize_t) argc)
450  ThrowCompareException(OptionError,"MissingArgument",option);
451  channel=ParseChannelOption(argv[i]);
452  if (channel < 0)
453  ThrowCompareException(OptionError,"UnrecognizedChannelType",
454  argv[i]);
455  channels=(ChannelType) channel;
456  break;
457  }
458  if (LocaleCompare("colorspace",option+1) == 0)
459  {
460  ssize_t
461  colorspace;
462 
463  if (*option == '+')
464  break;
465  i++;
466  if (i == (ssize_t) argc)
467  ThrowCompareException(OptionError,"MissingArgument",option);
468  colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
469  argv[i]);
470  if (colorspace < 0)
471  ThrowCompareException(OptionError,"UnrecognizedColorspace",
472  argv[i]);
473  break;
474  }
475  if (LocaleCompare("compose",option+1) == 0)
476  {
477  ssize_t
478  compose;
479 
480  if (*option == '+')
481  break;
482  i++;
483  if (i == (ssize_t) argc)
484  ThrowCompareException(OptionError,"MissingArgument",option);
485  compose=ParseCommandOption(MagickComposeOptions,MagickFalse,
486  argv[i]);
487  if (compose < 0)
488  ThrowCompareException(OptionError,"UnrecognizedComposeOperator",
489  argv[i]);
490  break;
491  }
492  if (LocaleCompare("compress",option+1) == 0)
493  {
494  ssize_t
495  compress;
496 
497  if (*option == '+')
498  break;
499  i++;
500  if (i == (ssize_t) argc)
501  ThrowCompareException(OptionError,"MissingArgument",option);
502  compress=ParseCommandOption(MagickCompressOptions,MagickFalse,
503  argv[i]);
504  if (compress < 0)
505  ThrowCompareException(OptionError,"UnrecognizedImageCompression",
506  argv[i]);
507  break;
508  }
509  if (LocaleCompare("concurrent",option+1) == 0)
510  break;
511  if (LocaleCompare("crop",option+1) == 0)
512  {
513  if (*option == '+')
514  break;
515  i++;
516  if (i == (ssize_t) argc)
517  ThrowCompareException(OptionError,"MissingArgument",option);
518  if (IsGeometry(argv[i]) == MagickFalse)
519  ThrowCompareInvalidArgumentException(option,argv[i]);
520  break;
521  }
522  ThrowCompareException(OptionError,"UnrecognizedOption",option)
523  }
524  case 'd':
525  {
526  if (LocaleCompare("debug",option+1) == 0)
527  {
528  LogEventType
529  event_mask;
530 
531  if (*option == '+')
532  break;
533  i++;
534  if (i == (ssize_t) argc)
535  ThrowCompareException(OptionError,"MissingArgument",option);
536  event_mask=SetLogEventMask(argv[i]);
537  if (event_mask == UndefinedEvents)
538  ThrowCompareException(OptionError,"UnrecognizedEventType",
539  argv[i]);
540  break;
541  }
542  if (LocaleCompare("decipher",option+1) == 0)
543  {
544  if (*option == '+')
545  break;
546  i++;
547  if (i == (ssize_t) argc)
548  ThrowCompareException(OptionError,"MissingArgument",option);
549  break;
550  }
551  if (LocaleCompare("define",option+1) == 0)
552  {
553  i++;
554  if (i == (ssize_t) argc)
555  ThrowCompareException(OptionError,"MissingArgument",option);
556  if (*option == '+')
557  {
558  const char
559  *define;
560 
561  define=GetImageOption(image_info,argv[i]);
562  if (define == (const char *) NULL)
563  ThrowCompareException(OptionError,"NoSuchOption",argv[i]);
564  break;
565  }
566  break;
567  }
568  if (LocaleCompare("delete",option+1) == 0)
569  {
570  if (*option == '+')
571  break;
572  i++;
573  if (i == (ssize_t) argc)
574  ThrowCompareException(OptionError,"MissingArgument",option);
575  if (IsSceneGeometry(argv[i],MagickFalse) == MagickFalse)
576  ThrowCompareInvalidArgumentException(option,argv[i]);
577  break;
578  }
579  if (LocaleCompare("density",option+1) == 0)
580  {
581  if (*option == '+')
582  break;
583  i++;
584  if (i == (ssize_t) argc)
585  ThrowCompareException(OptionError,"MissingArgument",option);
586  if (IsGeometry(argv[i]) == MagickFalse)
587  ThrowCompareInvalidArgumentException(option,argv[i]);
588  break;
589  }
590  if (LocaleCompare("depth",option+1) == 0)
591  {
592  if (*option == '+')
593  break;
594  i++;
595  if (i == (ssize_t) argc)
596  ThrowCompareException(OptionError,"MissingArgument",option);
597  if (IsGeometry(argv[i]) == MagickFalse)
598  ThrowCompareInvalidArgumentException(option,argv[i]);
599  break;
600  }
601  if (LocaleCompare("dissimilarity-threshold",option+1) == 0)
602  {
603  if (*option == '+')
604  break;
605  i++;
606  if (i == (ssize_t) argc)
607  ThrowCompareException(OptionError,"MissingArgument",option);
608  if (IsGeometry(argv[i]) == MagickFalse)
609  ThrowCompareInvalidArgumentException(option,argv[i]);
610  if (*option == '+')
611  dissimilarity_threshold=DefaultDissimilarityThreshold;
612  else
613  dissimilarity_threshold=StringToDouble(argv[i],(char **) NULL);
614  break;
615  }
616  if (LocaleCompare("distort",option+1) == 0)
617  {
618  ssize_t
619  op;
620 
621  i++;
622  if (i == (ssize_t) argc)
623  ThrowCompareException(OptionError,"MissingArgument",option);
624  op=ParseCommandOption(MagickDistortOptions,MagickFalse,argv[i]);
625  if (op < 0)
626  ThrowCompareException(OptionError,"UnrecognizedDistortMethod",
627  argv[i]);
628  i++;
629  if (i == (ssize_t) argc)
630  ThrowCompareException(OptionError,"MissingArgument",option);
631  break;
632  }
633  if (LocaleCompare("duration",option+1) == 0)
634  {
635  if (*option == '+')
636  break;
637  i++;
638  if (i == (ssize_t) argc)
639  ThrowCompareException(OptionError,"MissingArgument",option);
640  if (IsGeometry(argv[i]) == MagickFalse)
641  ThrowCompareInvalidArgumentException(option,argv[i]);
642  break;
643  }
644  ThrowCompareException(OptionError,"UnrecognizedOption",option)
645  }
646  case 'e':
647  {
648  if (LocaleCompare("encipher",option+1) == 0)
649  {
650  if (*option == '+')
651  break;
652  i++;
653  if (i == (ssize_t) argc)
654  ThrowCompareException(OptionError,"MissingArgument",option);
655  break;
656  }
657  if (LocaleCompare("extract",option+1) == 0)
658  {
659  if (*option == '+')
660  break;
661  i++;
662  if (i == (ssize_t) argc)
663  ThrowCompareException(OptionError,"MissingArgument",option);
664  if (IsGeometry(argv[i]) == MagickFalse)
665  ThrowCompareInvalidArgumentException(option,argv[i]);
666  break;
667  }
668  ThrowCompareException(OptionError,"UnrecognizedOption",option)
669  }
670  case 'f':
671  {
672  if (LocaleCompare("format",option+1) == 0)
673  {
674  if (*option == '+')
675  break;
676  i++;
677  if (i == (ssize_t) argc)
678  ThrowCompareException(OptionError,"MissingArgument",option);
679  format=argv[i];
680  break;
681  }
682  if (LocaleCompare("fuzz",option+1) == 0)
683  {
684  if (*option == '+')
685  break;
686  i++;
687  if (i == (ssize_t) argc)
688  ThrowCompareException(OptionError,"MissingArgument",option);
689  if (IsGeometry(argv[i]) == MagickFalse)
690  ThrowCompareInvalidArgumentException(option,argv[i]);
691  break;
692  }
693  ThrowCompareException(OptionError,"UnrecognizedOption",option)
694  }
695  case 'g':
696  {
697  if (LocaleCompare("gravity",option+1) == 0)
698  {
699  ssize_t
700  gravity;
701 
702  if (*option == '+')
703  break;
704  i++;
705  if (i == (ssize_t) argc)
706  ThrowCompareException(OptionError,"MissingArgument",option);
707  gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,
708  argv[i]);
709  if (gravity < 0)
710  ThrowCompareException(OptionError,"UnrecognizedGravityType",
711  argv[i]);
712  break;
713  }
714  ThrowCompareException(OptionError,"UnrecognizedOption",option)
715  }
716  case 'h':
717  {
718  if ((LocaleCompare("help",option+1) == 0) ||
719  (LocaleCompare("-help",option+1) == 0))
720  {
721  DestroyCompare();
722  return(CompareUsage());
723  }
724  if (LocaleCompare("highlight-color",option+1) == 0)
725  {
726  if (*option == '+')
727  break;
728  i++;
729  if (i == (ssize_t) argc)
730  ThrowCompareException(OptionError,"MissingArgument",option);
731  break;
732  }
733  ThrowCompareException(OptionError,"UnrecognizedOption",option)
734  }
735  case 'i':
736  {
737  if (LocaleCompare("identify",option+1) == 0)
738  break;
739  if (LocaleCompare("interlace",option+1) == 0)
740  {
741  ssize_t
742  interlace;
743 
744  if (*option == '+')
745  break;
746  i++;
747  if (i == (ssize_t) argc)
748  ThrowCompareException(OptionError,"MissingArgument",option);
749  interlace=ParseCommandOption(MagickInterlaceOptions,MagickFalse,
750  argv[i]);
751  if (interlace < 0)
752  ThrowCompareException(OptionError,"UnrecognizedInterlaceType",
753  argv[i]);
754  break;
755  }
756  ThrowCompareException(OptionError,"UnrecognizedOption",option)
757  }
758  case 'l':
759  {
760  if (LocaleCompare("level",option+1) == 0)
761  {
762  i++;
763  if (i == (ssize_t) argc)
764  ThrowCompareException(OptionError,"MissingArgument",option);
765  if (IsGeometry(argv[i]) == MagickFalse)
766  ThrowCompareInvalidArgumentException(option,argv[i]);
767  break;
768  }
769  if (LocaleCompare("limit",option+1) == 0)
770  {
771  char
772  *p;
773 
774  double
775  value;
776 
777  ssize_t
778  resource;
779 
780  if (*option == '+')
781  break;
782  i++;
783  if (i == (ssize_t) argc)
784  ThrowCompareException(OptionError,"MissingArgument",option);
785  resource=ParseCommandOption(MagickResourceOptions,MagickFalse,
786  argv[i]);
787  if (resource < 0)
788  ThrowCompareException(OptionError,"UnrecognizedResourceType",
789  argv[i]);
790  i++;
791  if (i == (ssize_t) argc)
792  ThrowCompareException(OptionError,"MissingArgument",option);
793  value=StringToDouble(argv[i],&p);
794  (void) value;
795  if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
796  ThrowCompareInvalidArgumentException(option,argv[i]);
797  break;
798  }
799  if (LocaleCompare("list",option+1) == 0)
800  {
801  ssize_t
802  list;
803 
804  if (*option == '+')
805  break;
806  i++;
807  if (i == (ssize_t) argc)
808  ThrowCompareException(OptionError,"MissingArgument",option);
809  list=ParseCommandOption(MagickListOptions,MagickFalse,argv[i]);
810  if (list < 0)
811  ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
812  status=MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
813  argv+j,exception);
814  DestroyCompare();
815  return(status == 0 ? MagickFalse : MagickTrue);
816  }
817  if (LocaleCompare("log",option+1) == 0)
818  {
819  if (*option == '+')
820  break;
821  i++;
822  if ((i == (ssize_t) argc) || (strchr(argv[i],'%') == (char *) NULL))
823  ThrowCompareException(OptionError,"MissingArgument",option);
824  break;
825  }
826  if (LocaleCompare("lowlight-color",option+1) == 0)
827  {
828  if (*option == '+')
829  break;
830  i++;
831  if (i == (ssize_t) argc)
832  ThrowCompareException(OptionError,"MissingArgument",option);
833  break;
834  }
835  ThrowCompareException(OptionError,"UnrecognizedOption",option)
836  }
837  case 'm':
838  {
839  if (LocaleCompare("mask",option+1) == 0)
840  {
841  if (*option == '+')
842  break;
843  i++;
844  if (i == (ssize_t) argc)
845  ThrowCompareException(OptionError,"MissingArgument",option);
846  break;
847  }
848  if (LocaleCompare("matte",option+1) == 0)
849  break;
850  if (LocaleCompare("metric",option+1) == 0)
851  {
852  ssize_t
853  type;
854 
855  if (*option == '+')
856  break;
857  i++;
858  if (i == (ssize_t) argc)
859  ThrowCompareException(OptionError,"MissingArgument",option);
860  type=ParseCommandOption(MagickMetricOptions,MagickTrue,argv[i]);
861  if (type < 0)
862  ThrowCompareException(OptionError,"UnrecognizedMetricType",
863  argv[i]);
864  metric=(MetricType) type;
865  break;
866  }
867  if (LocaleCompare("monitor",option+1) == 0)
868  break;
869  ThrowCompareException(OptionError,"UnrecognizedOption",option)
870  }
871  case 'p':
872  {
873  if (LocaleCompare("precision",option+1) == 0)
874  {
875  if (*option == '+')
876  break;
877  i++;
878  if (i == (ssize_t) argc)
879  ThrowCompareException(OptionError,"MissingArgument",option);
880  if (IsGeometry(argv[i]) == MagickFalse)
881  ThrowCompareInvalidArgumentException(option,argv[i]);
882  break;
883  }
884  if (LocaleCompare("passphrase",option+1) == 0)
885  {
886  if (*option == '+')
887  break;
888  i++;
889  if (i == (ssize_t) argc)
890  ThrowCompareException(OptionError,"MissingArgument",option);
891  break;
892  }
893  if (LocaleCompare("profile",option+1) == 0)
894  {
895  i++;
896  if (i == (ssize_t) argc)
897  ThrowCompareException(OptionError,"MissingArgument",option);
898  break;
899  }
900  ThrowCompareException(OptionError,"UnrecognizedOption",option)
901  }
902  case 'q':
903  {
904  if (LocaleCompare("quality",option+1) == 0)
905  {
906  if (*option == '+')
907  break;
908  i++;
909  if (i == (ssize_t) argc)
910  ThrowCompareException(OptionError,"MissingArgument",option);
911  if (IsGeometry(argv[i]) == MagickFalse)
912  ThrowCompareInvalidArgumentException(option,argv[i]);
913  break;
914  }
915  if (LocaleCompare("quantize",option+1) == 0)
916  {
917  ssize_t
918  colorspace;
919 
920  if (*option == '+')
921  break;
922  i++;
923  if (i == (ssize_t) argc)
924  ThrowCompareException(OptionError,"MissingArgument",option);
925  colorspace=ParseCommandOption(MagickColorspaceOptions,
926  MagickFalse,argv[i]);
927  if (colorspace < 0)
928  ThrowCompareException(OptionError,"UnrecognizedColorspace",
929  argv[i]);
930  break;
931  }
932  if (LocaleCompare("quiet",option+1) == 0)
933  break;
934  ThrowCompareException(OptionError,"UnrecognizedOption",option)
935  }
936  case 'r':
937  {
938  if (LocaleCompare("regard-warnings",option+1) == 0)
939  break;
940  if (LocaleCompare("repage",option+1) == 0)
941  {
942  if (*option == '+')
943  break;
944  i++;
945  if (i == (ssize_t) argc)
946  ThrowCompareException(OptionError,"MissingArgument",option);
947  if (IsGeometry(argv[i]) == MagickFalse)
948  ThrowCompareInvalidArgumentException(option,argv[i]);
949  break;
950  }
951  if (LocaleCompare("resize",option+1) == 0)
952  {
953  if (*option == '+')
954  break;
955  i++;
956  if (i == (ssize_t) argc)
957  ThrowCompareException(OptionError,"MissingArgument",option);
958  if (IsGeometry(argv[i]) == MagickFalse)
959  ThrowCompareInvalidArgumentException(option,argv[i]);
960  break;
961  }
962  if (LocaleCompare("rotate",option+1) == 0)
963  {
964  i++;
965  if (i == (ssize_t) argc)
966  ThrowCompareException(OptionError,"MissingArgument",option);
967  if (IsGeometry(argv[i]) == MagickFalse)
968  ThrowCompareInvalidArgumentException(option,argv[i]);
969  break;
970  }
971  if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
972  {
973  respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
974  break;
975  }
976  ThrowCompareException(OptionError,"UnrecognizedOption",option)
977  }
978  case 's':
979  {
980  if (LocaleCompare("sampling-factor",option+1) == 0)
981  {
982  if (*option == '+')
983  break;
984  i++;
985  if (i == (ssize_t) argc)
986  ThrowCompareException(OptionError,"MissingArgument",option);
987  if (IsGeometry(argv[i]) == MagickFalse)
988  ThrowCompareInvalidArgumentException(option,argv[i]);
989  break;
990  }
991  if (LocaleCompare("seed",option+1) == 0)
992  {
993  if (*option == '+')
994  break;
995  i++;
996  if (i == (ssize_t) argc)
997  ThrowCompareException(OptionError,"MissingArgument",option);
998  if (IsGeometry(argv[i]) == MagickFalse)
999  ThrowCompareInvalidArgumentException(option,argv[i]);
1000  break;
1001  }
1002  if (LocaleCompare("separate",option+1) == 0)
1003  break;
1004  if (LocaleCompare("set",option+1) == 0)
1005  {
1006  i++;
1007  if (i == (ssize_t) argc)
1008  ThrowCompareException(OptionError,"MissingArgument",option);
1009  if (*option == '+')
1010  break;
1011  i++;
1012  if (i == (ssize_t) argc)
1013  ThrowCompareException(OptionError,"MissingArgument",option);
1014  break;
1015  }
1016  if (LocaleCompare("sigmoidal-contrast",option+1) == 0)
1017  {
1018  i++;
1019  if (i == (ssize_t) argc)
1020  ThrowCompareException(OptionError,"MissingArgument",option);
1021  if (IsGeometry(argv[i]) == MagickFalse)
1022  ThrowCompareInvalidArgumentException(option,argv[i]);
1023  break;
1024  }
1025  if (LocaleCompare("similarity-threshold",option+1) == 0)
1026  {
1027  if (*option == '+')
1028  break;
1029  i++;
1030  if (i == (ssize_t) argc)
1031  ThrowCompareException(OptionError,"MissingArgument",option);
1032  if (IsGeometry(argv[i]) == MagickFalse)
1033  ThrowCompareInvalidArgumentException(option,argv[i]);
1034  if (*option == '+')
1035  similarity_threshold=DefaultSimilarityThreshold;
1036  else
1037  similarity_threshold=StringToDouble(argv[i],(char **) NULL);
1038  break;
1039  }
1040  if (LocaleCompare("size",option+1) == 0)
1041  {
1042  if (*option == '+')
1043  break;
1044  i++;
1045  if (i == (ssize_t) argc)
1046  ThrowCompareException(OptionError,"MissingArgument",option);
1047  if (IsGeometry(argv[i]) == MagickFalse)
1048  ThrowCompareInvalidArgumentException(option,argv[i]);
1049  break;
1050  }
1051  if (LocaleCompare("subimage-search",option+1) == 0)
1052  {
1053  if (*option == '+')
1054  {
1055  subimage_search=MagickFalse;
1056  break;
1057  }
1058  subimage_search=MagickTrue;
1059  break;
1060  }
1061  if (LocaleCompare("synchronize",option+1) == 0)
1062  break;
1063  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1064  }
1065  case 't':
1066  {
1067  if (LocaleCompare("taint",option+1) == 0)
1068  break;
1069  if (LocaleCompare("transparent-color",option+1) == 0)
1070  {
1071  if (*option == '+')
1072  break;
1073  i++;
1074  if (i == (ssize_t) argc)
1075  ThrowCompareException(OptionError,"MissingArgument",option);
1076  break;
1077  }
1078  if (LocaleCompare("trim",option+1) == 0)
1079  break;
1080  if (LocaleCompare("type",option+1) == 0)
1081  {
1082  ssize_t
1083  type;
1084 
1085  if (*option == '+')
1086  break;
1087  i++;
1088  if (i == (ssize_t) argc)
1089  ThrowCompareException(OptionError,"MissingArgument",option);
1090  type=ParseCommandOption(MagickTypeOptions,MagickFalse,argv[i]);
1091  if (type < 0)
1092  ThrowCompareException(OptionError,"UnrecognizedImageType",
1093  argv[i]);
1094  break;
1095  }
1096  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1097  }
1098  case 'v':
1099  {
1100  if (LocaleCompare("verbose",option+1) == 0)
1101  break;
1102  if ((LocaleCompare("version",option+1) == 0) ||
1103  (LocaleCompare("-version",option+1) == 0))
1104  {
1105  ListMagickVersion(stdout);
1106  break;
1107  }
1108  if (LocaleCompare("virtual-pixel",option+1) == 0)
1109  {
1110  ssize_t
1111  method;
1112 
1113  if (*option == '+')
1114  break;
1115  i++;
1116  if (i == (ssize_t) argc)
1117  ThrowCompareException(OptionError,"MissingArgument",option);
1118  method=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
1119  argv[i]);
1120  if (method < 0)
1121  ThrowCompareException(OptionError,
1122  "UnrecognizedVirtualPixelMethod",argv[i]);
1123  break;
1124  }
1125  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1126  }
1127  case 'w':
1128  {
1129  if (LocaleCompare("write",option+1) == 0)
1130  {
1131  i++;
1132  if (i == (ssize_t) argc)
1133  ThrowCompareException(OptionError,"MissingArgument",option);
1134  break;
1135  }
1136  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1137  }
1138  case '?':
1139  break;
1140  default:
1141  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1142  }
1143  fire=(GetCommandOptionFlags(MagickCommandOptions,MagickFalse,option) &
1144  FireOptionFlag) == 0 ? MagickFalse : MagickTrue;
1145  if (fire != MagickFalse)
1146  FireImageStack(MagickTrue,MagickTrue,MagickTrue);
1147  }
1148  if (k != 0)
1149  ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
1150  if (i-- != (ssize_t) (argc-1))
1151  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1152  if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1153  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1154  FinalizeImageSettings(image_info,image,MagickTrue);
1155  if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1156  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1157  image=GetImageFromList(image,0);
1158  reconstruct_image=GetImageFromList(image,1);
1159  offset.x=0;
1160  offset.y=0;
1161  if (subimage_search != MagickFalse)
1162  {
1163  char
1164  artifact[MaxTextExtent];
1165 
1166  (void) FormatLocaleString(artifact,MaxTextExtent,"%g",
1167  similarity_threshold);
1168  (void) SetImageArtifact(image,"compare:similarity-threshold",artifact);
1169  similarity_image=SimilarityMetricImage(image,reconstruct_image,metric,
1170  &offset,&similarity_metric,exception);
1171  if (similarity_metric >= dissimilarity_threshold)
1172  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1173  "ImagesTooDissimilar","`%s'",image->filename);
1174  }
1175  if (similarity_image == (Image *) NULL)
1176  difference_image=CompareImageChannels(image,reconstruct_image,channels,metric,
1177  &distortion,exception);
1178  else
1179  {
1180  Image
1181  *composite_image;
1182 
1183  /*
1184  Determine if reconstructed image is a subimage of the image.
1185  */
1186  composite_image=CloneImage(image,0,0,MagickTrue,exception);
1187  if (composite_image == (Image *) NULL)
1188  difference_image=CompareImageChannels(image,reconstruct_image,channels,
1189  metric,&distortion,exception);
1190  else
1191  {
1192  Image
1193  *distort_image;
1194 
1195  RectangleInfo
1196  page;
1197 
1198  (void) CompositeImage(composite_image,CopyCompositeOp,
1199  reconstruct_image,offset.x,offset.y);
1200  difference_image=CompareImageChannels(image,composite_image,channels,
1201  metric,&distortion,exception);
1202  if (difference_image != (Image *) NULL)
1203  {
1204  difference_image->page.x=offset.x;
1205  difference_image->page.y=offset.y;
1206  }
1207  composite_image=DestroyImage(composite_image);
1208  page.width=reconstruct_image->columns;
1209  page.height=reconstruct_image->rows;
1210  page.x=offset.x;
1211  page.y=offset.y;
1212  distort_image=CropImage(image,&page,exception);
1213  if (distort_image != (Image *) NULL)
1214  {
1215  Image
1216  *sans_image;
1217 
1218  (void) SetImageArtifact(distort_image,"compare:virtual-pixels",
1219  "false");
1220  sans_image=CompareImages(distort_image,reconstruct_image,metric,
1221  &distortion,exception);
1222  if (sans_image != (Image *) NULL)
1223  sans_image=DestroyImage(sans_image);
1224  distort_image=DestroyImage(distort_image);
1225  }
1226  }
1227  if (difference_image != (Image *) NULL)
1228  {
1229  AppendImageToList(&difference_image,similarity_image);
1230  similarity_image=(Image *) NULL;
1231  }
1232  }
1233  switch (metric)
1234  {
1235  case AbsoluteErrorMetric:
1236  {
1237  size_t
1238  columns,
1239  rows;
1240 
1241  SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1242  scale=(double) columns*rows;
1243  break;
1244  }
1245  case NormalizedCrossCorrelationErrorMetric:
1246  {
1247  double
1248  maxima = 0.0,
1249  minima = 0.0;
1250 
1251  (void) GetImageRange(reconstruct_image,&minima,&maxima,exception);
1252  if (fabs(maxima-minima) < MagickEpsilon)
1253  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1254  CompareConstantColorException,"(%s)",CommandOptionToMnemonic(
1255  MagickMetricOptions,(ssize_t) metric));
1256  break;
1257  }
1258  case PeakAbsoluteErrorMetric:
1259  {
1260  if ((subimage_search != MagickFalse) &&
1261  (image->columns == reconstruct_image->columns) &&
1262  (image->rows == reconstruct_image->rows))
1263  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1264  CompareEqualSizedException,"(%s)",CommandOptionToMnemonic(
1265  MagickMetricOptions,(ssize_t) metric));
1266  break;
1267  }
1268  case PerceptualHashErrorMetric:
1269  {
1270  if (subimage_search == MagickFalse)
1271  {
1272  double
1273  maxima = 0.0,
1274  minima = 0.0;
1275 
1276  (void) GetImageRange(reconstruct_image,&minima,&maxima,exception);
1277  if (fabs(maxima-minima) < MagickEpsilon)
1278  (void) ThrowMagickException(exception,GetMagickModule(),
1279  ImageWarning,CompareConstantColorException,"(%s)",
1280  CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1281  }
1282  if ((subimage_search != MagickFalse) &&
1283  (image->columns == reconstruct_image->columns) &&
1284  (image->rows == reconstruct_image->rows))
1285  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1286  CompareEqualSizedException,"(%s)",CommandOptionToMnemonic(
1287  MagickMetricOptions,(ssize_t) metric));
1288  break;
1289  }
1290  case PeakSignalToNoiseRatioMetric:
1291  {
1292  scale=MagickSafePSNRRecipicol(10.0);
1293  break;
1294  }
1295  default:
1296  break;
1297  }
1298  if (fabs(distortion) > CompareEpsilon)
1299  similar=MagickFalse;
1300  if (difference_image == (Image *) NULL)
1301  status=0;
1302  else
1303  {
1304  if (image_info->verbose != MagickFalse)
1305  (void) IsImagesEqual(image,reconstruct_image);
1306  if (*difference_image->magick == '\0')
1307  (void) CopyMagickString(difference_image->magick,image->magick,
1308  MaxTextExtent);
1309  if (image_info->verbose == MagickFalse)
1310  {
1311  switch (metric)
1312  {
1313  case AbsoluteErrorMetric:
1314  {
1315  (void) FormatLocaleFile(stderr,"%.*g (%.*g)",GetMagickPrecision(),
1316  ceil(scale*distortion),GetMagickPrecision(),distortion);
1317  break;
1318  }
1319  case MeanErrorPerPixelMetric:
1320  {
1321  if (subimage_search == MagickFalse)
1322  {
1323  (void) FormatLocaleFile(stderr,"%.*g (%.*g, %.*g)",
1324  GetMagickPrecision(),scale*distortion,
1325  GetMagickPrecision(),distortion,GetMagickPrecision(),
1326  image->error.normalized_maximum_error);
1327  break;
1328  }
1329  magick_fallthrough;
1330  }
1331  default:
1332  {
1333  (void) FormatLocaleFile(stderr,"%.*g (%.*g)",GetMagickPrecision(),
1334  scale*distortion,GetMagickPrecision(),distortion);
1335  break;
1336  }
1337  }
1338  if (subimage_search != MagickFalse)
1339  (void) FormatLocaleFile(stderr," @ %.20g,%.20g [%.*g]",
1340  (double) offset.x,(double) offset.y,GetMagickPrecision(),
1341  similarity_metric);
1342  }
1343  else
1344  {
1345  double
1346  *channel_distortion;
1347 
1348  channel_distortion=GetImageChannelDistortions(image,reconstruct_image,
1349  metric,&image->exception);
1350  (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename);
1351  if ((reconstruct_image->columns != image->columns) ||
1352  (reconstruct_image->rows != image->rows))
1353  (void) FormatLocaleFile(stderr,"Offset: %.20g,%.20g\n",(double)
1354  difference_image->page.x,(double) difference_image->page.y);
1355  (void) FormatLocaleFile(stderr," Channel distortion: %s\n",
1356  CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1357  switch (metric)
1358  {
1359  case FuzzErrorMetric:
1360  case MeanAbsoluteErrorMetric:
1361  case MeanSquaredErrorMetric:
1362  case PeakAbsoluteErrorMetric:
1363  case RootMeanSquaredErrorMetric:
1364  case UndefinedErrorMetric:
1365  {
1366  switch (image->colorspace)
1367  {
1368  case RGBColorspace:
1369  default:
1370  {
1371  (void) FormatLocaleFile(stderr," red: %.*g (%.*g)\n",
1372  GetMagickPrecision(),scale*channel_distortion[RedChannel],
1373  GetMagickPrecision(),channel_distortion[RedChannel]);
1374  (void) FormatLocaleFile(stderr," green: %.*g (%.*g)\n",
1375  GetMagickPrecision(),scale*channel_distortion[GreenChannel],
1376  GetMagickPrecision(),channel_distortion[GreenChannel]);
1377  (void) FormatLocaleFile(stderr," blue: %.*g (%.*g)\n",
1378  GetMagickPrecision(),scale*channel_distortion[BlueChannel],
1379  GetMagickPrecision(),channel_distortion[BlueChannel]);
1380  if (image->matte != MagickFalse)
1381  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1382  GetMagickPrecision(),scale*
1383  channel_distortion[OpacityChannel],GetMagickPrecision(),
1384  channel_distortion[OpacityChannel]);
1385  break;
1386  }
1387  case CMYKColorspace:
1388  {
1389  (void) FormatLocaleFile(stderr," cyan: %.*g (%.*g)\n",
1390  GetMagickPrecision(),scale*channel_distortion[CyanChannel],
1391  GetMagickPrecision(),channel_distortion[CyanChannel]);
1392  (void) FormatLocaleFile(stderr," magenta: %.*g (%.*g)\n",
1393  GetMagickPrecision(),scale*
1394  channel_distortion[MagentaChannel],GetMagickPrecision(),
1395  channel_distortion[MagentaChannel]);
1396  (void) FormatLocaleFile(stderr," yellow: %.*g (%.*g)\n",
1397  GetMagickPrecision(),scale*
1398  channel_distortion[YellowChannel],GetMagickPrecision(),
1399  channel_distortion[YellowChannel]);
1400  (void) FormatLocaleFile(stderr," black: %.*g (%.*g)\n",
1401  GetMagickPrecision(),scale*channel_distortion[BlackChannel],
1402  GetMagickPrecision(),channel_distortion[BlackChannel]);
1403  if (image->matte != MagickFalse)
1404  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1405  GetMagickPrecision(),scale*
1406  channel_distortion[OpacityChannel],GetMagickPrecision(),
1407  channel_distortion[OpacityChannel]);
1408  break;
1409  }
1410  case LinearGRAYColorspace:
1411  case GRAYColorspace:
1412  {
1413  (void) FormatLocaleFile(stderr," gray: %.*g (%.*g)\n",
1414  GetMagickPrecision(),scale*channel_distortion[GrayChannel],
1415  GetMagickPrecision(),channel_distortion[GrayChannel]);
1416  if (image->matte != MagickFalse)
1417  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1418  GetMagickPrecision(),scale*
1419  channel_distortion[OpacityChannel],GetMagickPrecision(),
1420  channel_distortion[OpacityChannel]);
1421  break;
1422  }
1423  }
1424  (void) FormatLocaleFile(stderr," all: %.*g (%.*g)\n",
1425  GetMagickPrecision(),scale*
1426  channel_distortion[CompositeChannels],GetMagickPrecision(),
1427  channel_distortion[CompositeChannels]);
1428  break;
1429  }
1430  case AbsoluteErrorMetric:
1431  case NormalizedCrossCorrelationErrorMetric:
1432  case PeakSignalToNoiseRatioMetric:
1433  case PerceptualHashErrorMetric:
1434  {
1435  switch (image->colorspace)
1436  {
1437  case RGBColorspace:
1438  default:
1439  {
1440  (void) FormatLocaleFile(stderr," red: %.*g\n",
1441  GetMagickPrecision(),channel_distortion[RedChannel]);
1442  (void) FormatLocaleFile(stderr," green: %.*g\n",
1443  GetMagickPrecision(),channel_distortion[GreenChannel]);
1444  (void) FormatLocaleFile(stderr," blue: %.*g\n",
1445  GetMagickPrecision(),channel_distortion[BlueChannel]);
1446  if (image->matte != MagickFalse)
1447  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1448  GetMagickPrecision(),channel_distortion[OpacityChannel]);
1449  break;
1450  }
1451  case CMYKColorspace:
1452  {
1453  (void) FormatLocaleFile(stderr," cyan: %.*g\n",
1454  GetMagickPrecision(),channel_distortion[CyanChannel]);
1455  (void) FormatLocaleFile(stderr," magenta: %.*g\n",
1456  GetMagickPrecision(),channel_distortion[MagentaChannel]);
1457  (void) FormatLocaleFile(stderr," yellow: %.*g\n",
1458  GetMagickPrecision(),channel_distortion[YellowChannel]);
1459  (void) FormatLocaleFile(stderr," black: %.*g\n",
1460  GetMagickPrecision(),channel_distortion[BlackChannel]);
1461  if (image->matte != MagickFalse)
1462  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1463  GetMagickPrecision(),channel_distortion[OpacityChannel]);
1464  break;
1465  }
1466  case LinearGRAYColorspace:
1467  case GRAYColorspace:
1468  {
1469  (void) FormatLocaleFile(stderr," gray: %.*g\n",
1470  GetMagickPrecision(),channel_distortion[GrayChannel]);
1471  if (image->matte != MagickFalse)
1472  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1473  GetMagickPrecision(),channel_distortion[OpacityChannel]);
1474  break;
1475  }
1476  }
1477  (void) FormatLocaleFile(stderr," all: %.*g\n",
1478  GetMagickPrecision(),channel_distortion[CompositeChannels]);
1479  break;
1480  }
1481  case MeanErrorPerPixelMetric:
1482  {
1483  (void) FormatLocaleFile(stderr," %.*g (%.*g, %.*g)\n",
1484  GetMagickPrecision(),channel_distortion[CompositeChannels],
1485  GetMagickPrecision(),channel_distortion[CompositeChannels],
1486  GetMagickPrecision(),image->error.normalized_maximum_error);
1487  break;
1488  }
1489  }
1490  channel_distortion=(double *) RelinquishMagickMemory(
1491  channel_distortion);
1492  if (subimage_search != MagickFalse)
1493  {
1494  (void) FormatLocaleFile(stderr," Offset: %.20g,%.20g\n",
1495  (double) difference_image->page.x,(double)
1496  difference_image->page.y);
1497  (void) FormatLocaleFile(stderr," Similarity metric: %.*g\n",
1498  GetMagickPrecision(),similarity_metric);
1499  (void) FormatLocaleFile(stderr," Similarity threshold: %*g\n",
1500  GetMagickPrecision(),similarity_threshold);
1501  (void) FormatLocaleFile(stderr,
1502  " Dissimilarity threshold: %*g\n",GetMagickPrecision(),
1503  dissimilarity_threshold);
1504  }
1505  }
1506  (void) ResetImagePage(difference_image,"0x0+0+0");
1507  if (difference_image->next != (Image *) NULL)
1508  (void) ResetImagePage(difference_image->next,"0x0+0+0");
1509  status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
1510  if ((metadata != (char **) NULL) && (format != (char *) NULL))
1511  {
1512  char
1513  *text;
1514 
1515  text=InterpretImageProperties(image_info,difference_image,format);
1516  InheritException(exception,&image->exception);
1517  if (text == (char *) NULL)
1518  ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1519  GetExceptionMessage(errno));
1520  (void) ConcatenateString(&(*metadata),text);
1521  text=DestroyString(text);
1522  }
1523  difference_image=DestroyImageList(difference_image);
1524  }
1525  DestroyCompare();
1526  image_info=restore_info;
1527  if (similar == MagickFalse)
1528  (void) SetImageOption(image_info,"compare:dissimilar","true");
1529  return(status != 0 ? MagickTrue : MagickFalse);
1530 }