MagickWand  7.1.2-29
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 "MagickWand/studio.h"
45 #include "MagickWand/MagickWand.h"
46 #include "MagickWand/mogrify-private.h"
47 #include "MagickCore/compare-private.h"
48 #include "MagickCore/image-private.h"
49 #include "MagickCore/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 % CompareImagesCommand() 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 CompareImagesCommand method is:
67 %
68 % MagickBooleanType CompareImagesCommand(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  channel_operators[] =
89  " -separate separate an image channel into a grayscale image",
90  miscellaneous[] =
91  " -channel mask set the image channel mask\n"
92  " -debug events display copious debugging information\n"
93  " -help print program options\n"
94  " -list type print a list of supported option arguments\n"
95  " -log format format of debugging information",
96  operators[] =
97  " -auto-orient automagically orient (rotate) image\n"
98  " -brightness-contrast geometry\n"
99  " improve brightness / contrast of the image\n"
100  " -distort method args\n"
101  " distort images according to given method and args\n"
102  " -level value adjust the level of image contrast\n"
103  " -resize geometry resize the image\n"
104  " -rotate degrees apply Paeth rotation to the image\n"
105  " -sigmoidal-contrast geometry\n"
106  " increase the contrast without saturating highlights or\n"
107  " -trim trim image edges\n"
108  " -write filename write images to this file",
109  sequence_operators[] =
110  " -crop geometry cut out a rectangular region of the image",
111  settings[] =
112  " -adjoin join images into a single multi-image file\n"
113  " -alpha option on, activate, off, deactivate, set, opaque, copy\n"
114  " transparent, extract, background, or shape\n"
115  " -authenticate password\n"
116  " decipher image with this password\n"
117  " -background color background color\n"
118  " -colorspace type alternate image colorspace\n"
119  " -compose operator set image composite operator\n"
120  " -compress type type of pixel compression when writing the image\n"
121  " -decipher filename convert cipher pixels to plain pixels\n"
122  " -define format:option\n"
123  " define one or more image format options\n"
124  " -density geometry horizontal and vertical density of the image\n"
125  " -depth value image depth\n"
126  " -dissimilarity-threshold value\n"
127  " maximum distortion for (sub)image match\n"
128  " -encipher filename convert plain pixels to cipher pixels\n"
129  " -extract geometry extract area from image\n"
130  " -format \"string\" output formatted image characteristics\n"
131  " -fuzz distance colors within this distance are considered equal\n"
132  " -gravity type horizontal and vertical text placement\n"
133  " -highlight-color color\n"
134  " emphasize pixel differences with this color\n"
135  " -identify identify the format and characteristics of the image\n"
136  " -interlace type type of image interlacing scheme\n"
137  " -limit type value pixel cache resource limit\n"
138  " -lowlight-color color\n"
139  " de-emphasize pixel differences with this color\n"
140  " -metric type measure differences between images with this metric\n"
141  " -monitor monitor progress\n"
142  " -negate replace every pixel with its complementary color \n"
143  " -passphrase filename get the passphrase from this file\n"
144  " -precision value maximum number of significant digits to print\n"
145  " -profile filename add, delete, or apply an image profile\n"
146  " -quality value JPEG/MIFF/PNG compression level\n"
147  " -quiet suppress all warning messages\n"
148  " -quantize colorspace reduce colors in this colorspace\n"
149  " -read-mask filename associate a read mask with the image\n"
150  " -regard-warnings pay attention to warning messages\n"
151  " -respect-parentheses settings remain in effect until parenthesis boundary\n"
152  " -sampling-factor geometry\n"
153  " horizontal and vertical sampling factor\n"
154  " -seed value seed a new sequence of pseudo-random numbers\n"
155  " -set attribute value set an image attribute\n"
156  " -quality value JPEG/MIFF/PNG compression level\n"
157  " -repage geometry size and location of an image canvas\n"
158  " -similarity-threshold value\n"
159  " minimum distortion for (sub)image match\n"
160  " -size geometry width and height of image\n"
161  " -subimage-search search for subimage\n"
162  " -synchronize synchronize image to storage device\n"
163  " -taint declare the image as modified\n"
164  " -transparent-color color\n"
165  " transparent color\n"
166  " -type type image type\n"
167  " -verbose print detailed information about the image\n"
168  " -version print version information\n"
169  " -virtual-pixel method\n"
170  " virtual pixel access method\n"
171  " -write-mask filename associate a write mask with the image",
172  stack_operators[] =
173  " -delete indexes delete the image from the image sequence";
174 
175  ListMagickVersion(stdout);
176  (void) printf("Usage: %s [options ...] image reconstruct difference\n",
177  GetClientName());
178  (void) printf("\nImage Settings:\n");
179  (void) puts(settings);
180  (void) printf("\nImage Operators:\n");
181  (void) puts(operators);
182  (void) printf("\nImage Channel Operators:\n");
183  (void) puts(channel_operators);
184  (void) printf("\nImage Sequence Operators:\n");
185  (void) puts(sequence_operators);
186  (void) printf("\nImage Stack Operators:\n");
187  (void) puts(stack_operators);
188  (void) printf("\nMiscellaneous Options:\n");
189  (void) puts(miscellaneous);
190  (void) printf(
191  "\nBy default, the image format of 'file' is determined by its magic\n");
192  (void) printf(
193  "number. To specify a particular image format, precede the filename\n");
194  (void) printf(
195  "with an image format name and a colon (i.e. ps:image) or specify the\n");
196  (void) printf(
197  "image type as the filename suffix (i.e. image.ps). Specify 'file' as\n");
198  (void) printf("'-' for standard input or output.\n");
199  return(MagickTrue);
200 }
201 
202 WandExport MagickBooleanType CompareImagesCommand(ImageInfo *image_info,
203  int argc,char **argv,char **metadata,ExceptionInfo *exception)
204 {
205 #define CompareEpsilon (1.0e-06)
206 #define CompareConstantColorException \
207  "search metric is unreliable for constant-color images"
208 #define CompareEqualSizedException \
209  "subimage search metric is unreliable for equal-sized images"
210 #define DefaultDissimilarityThreshold (1.0/MagickPI)
211 #define DestroyCompare() \
212 { \
213  if (similarity_image != (Image *) NULL) \
214  similarity_image=DestroyImageList(similarity_image); \
215  if (difference_image != (Image *) NULL) \
216  difference_image=DestroyImageList(difference_image); \
217  DestroyImageStack(); \
218  for (i=0; i < (ssize_t) argc; i++) \
219  argv[i]=DestroyString(argv[i]); \
220  argv=(char **) RelinquishMagickMemory(argv); \
221 }
222 #define ThrowCompareException(asperity,tag,option) \
223 { \
224  char *message = GetExceptionMessage(errno); \
225  (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \
226  "`%s'",option == (char *) NULL ? message : option); \
227  message=DestroyString(message); \
228  DestroyCompare(); \
229  return(MagickFalse); \
230 }
231 #define ThrowCompareInvalidArgumentException(option,argument) \
232 { \
233  (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \
234  "InvalidArgument","'%s': %s",option,argument); \
235  DestroyCompare(); \
236  return(MagickFalse); \
237 }
238 
239  char
240  *filename,
241  *option;
242 
243  const char
244  *format;
245 
246  double
247  dissimilarity_threshold = DefaultDissimilarityThreshold,
248  distortion = 0.0,
249  normal_scale = 1.0,
250  scale = (double) QuantumRange,
251  similarity_metric = 0.0,
252  similarity_threshold = DefaultSimilarityThreshold;
253 
254  Image
255  *difference_image,
256  *image = (Image *) NULL,
257  *reconstruct_image,
258  *similarity_image;
259 
260  ImageStack
261  image_stack[MaxImageStackDepth+1];
262 
263  MagickBooleanType
264  fire,
265  pend,
266  respect_parentheses,
267  similar = MagickTrue,
268  subimage_search;
269 
270  MagickStatusType
271  status;
272 
273  MetricType
274  metric = UndefinedErrorMetric;
275 
276  RectangleInfo
277  offset;
278 
279  ssize_t
280  i;
281 
282  ssize_t
283  j,
284  k;
285 
286  /*
287  Set defaults.
288  */
289  assert(image_info != (ImageInfo *) NULL);
290  assert(image_info->signature == MagickCoreSignature);
291  assert(exception != (ExceptionInfo *) NULL);
292  if (IsEventLogging() != MagickFalse)
293  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
294  if (argc == 2)
295  {
296  option=argv[1];
297  if ((LocaleCompare("version",option+1) == 0) ||
298  (LocaleCompare("-version",option+1) == 0))
299  {
300  ListMagickVersion(stdout);
301  return(MagickTrue);
302  }
303  }
304  if (argc < 3)
305  {
306  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
307  "MissingArgument","%s","");
308  (void) CompareUsage();
309  return(MagickFalse);
310  }
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_parentheses=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  images=ReadImages(image_info,filename,exception);
364  status&=(MagickStatusType) ((images != (Image *) NULL) &&
365  (exception->severity < ErrorException));
366  if (images == (Image *) NULL)
367  continue;
368  AppendImageStack(images);
369  continue;
370  }
371  pend=image != (Image *) NULL ? MagickTrue : MagickFalse;
372  switch (*(option+1))
373  {
374  case 'a':
375  {
376  if (LocaleCompare("adjoin",option+1) == 0)
377  break;
378  if (LocaleCompare("alpha",option+1) == 0)
379  {
380  ssize_t
381  type;
382 
383  if (*option == '+')
384  break;
385  i++;
386  if (i == (ssize_t) argc)
387  ThrowCompareException(OptionError,"MissingArgument",option);
388  type=ParseCommandOption(MagickAlphaChannelOptions,MagickFalse,
389  argv[i]);
390  if (type < 0)
391  ThrowCompareException(OptionError,
392  "UnrecognizedAlphaChannelOption",argv[i]);
393  break;
394  }
395  if (LocaleCompare("auto-orient",option+1) == 0)
396  break;
397  if (LocaleCompare("authenticate",option+1) == 0)
398  {
399  if (*option == '+')
400  break;
401  i++;
402  if (i == (ssize_t) argc)
403  ThrowCompareException(OptionError,"MissingArgument",option);
404  break;
405  }
406  ThrowCompareException(OptionError,"UnrecognizedOption",option);
407  }
408  case 'b':
409  {
410  if (LocaleCompare("background",option+1) == 0)
411  {
412  if (*option == '+')
413  break;
414  i++;
415  if (i == (ssize_t) argc)
416  ThrowCompareException(OptionError,"MissingArgument",option);
417  break;
418  }
419  if (LocaleCompare("brightness-contrast",option+1) == 0)
420  {
421  i++;
422  if (i == (ssize_t) argc)
423  ThrowCompareException(OptionError,"MissingArgument",option);
424  if (IsGeometry(argv[i]) == MagickFalse)
425  ThrowCompareInvalidArgumentException(option,argv[i]);
426  break;
427  }
428  ThrowCompareException(OptionError,"UnrecognizedOption",option);
429  }
430  case 'c':
431  {
432  if (LocaleCompare("cache",option+1) == 0)
433  {
434  if (*option == '+')
435  break;
436  i++;
437  if (i == (ssize_t) argc)
438  ThrowCompareException(OptionError,"MissingArgument",option);
439  if (IsGeometry(argv[i]) == MagickFalse)
440  ThrowCompareInvalidArgumentException(option,argv[i]);
441  break;
442  }
443  if (LocaleCompare("channel",option+1) == 0)
444  {
445  ssize_t
446  channel;
447 
448  if (*option == '+')
449  break;
450  i++;
451  if (i == (ssize_t) argc)
452  ThrowCompareException(OptionError,"MissingArgument",option);
453  channel=ParseChannelOption(argv[i]);
454  if (channel < 0)
455  ThrowCompareException(OptionError,"UnrecognizedChannelType",
456  argv[i]);
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("matte",option+1) == 0)
841  break;
842  if (LocaleCompare("metric",option+1) == 0)
843  {
844  ssize_t
845  type;
846 
847  if (*option == '+')
848  break;
849  i++;
850  if (i == (ssize_t) argc)
851  ThrowCompareException(OptionError,"MissingArgument",option);
852  type=ParseCommandOption(MagickMetricOptions,MagickTrue,argv[i]);
853  if (type < 0)
854  ThrowCompareException(OptionError,"UnrecognizedMetricType",
855  argv[i]);
856  metric=(MetricType) type;
857  break;
858  }
859  if (LocaleCompare("monitor",option+1) == 0)
860  break;
861  ThrowCompareException(OptionError,"UnrecognizedOption",option)
862  }
863  case 'n':
864  {
865  if (LocaleCompare("negate",option+1) == 0)
866  break;
867  ThrowCompareException(OptionError,"UnrecognizedOption",option)
868  }
869  case 'p':
870  {
871  if (LocaleCompare("passphrase",option+1) == 0)
872  {
873  if (*option == '+')
874  break;
875  i++;
876  if (i == (ssize_t) argc)
877  ThrowCompareException(OptionError,"MissingArgument",option);
878  break;
879  }
880  if (LocaleCompare("precision",option+1) == 0)
881  {
882  if (*option == '+')
883  break;
884  i++;
885  if (i == (ssize_t) argc)
886  ThrowCompareException(OptionError,"MissingArgument",option);
887  if (IsGeometry(argv[i]) == MagickFalse)
888  ThrowCompareInvalidArgumentException(option,argv[i]);
889  break;
890  }
891  if (LocaleCompare("profile",option+1) == 0)
892  {
893  i++;
894  if (i == (ssize_t) argc)
895  ThrowCompareException(OptionError,"MissingArgument",option);
896  break;
897  }
898  ThrowCompareException(OptionError,"UnrecognizedOption",option)
899  }
900  case 'q':
901  {
902  if (LocaleCompare("quality",option+1) == 0)
903  {
904  if (*option == '+')
905  break;
906  i++;
907  if (i == (ssize_t) argc)
908  ThrowCompareException(OptionError,"MissingArgument",option);
909  if (IsGeometry(argv[i]) == MagickFalse)
910  ThrowCompareInvalidArgumentException(option,argv[i]);
911  break;
912  }
913  if (LocaleCompare("quantize",option+1) == 0)
914  {
915  ssize_t
916  colorspace;
917 
918  if (*option == '+')
919  break;
920  i++;
921  if (i == (ssize_t) argc)
922  ThrowCompareException(OptionError,"MissingArgument",option);
923  colorspace=ParseCommandOption(MagickColorspaceOptions,
924  MagickFalse,argv[i]);
925  if (colorspace < 0)
926  ThrowCompareException(OptionError,"UnrecognizedColorspace",
927  argv[i]);
928  break;
929  }
930  if (LocaleCompare("quiet",option+1) == 0)
931  break;
932  ThrowCompareException(OptionError,"UnrecognizedOption",option)
933  }
934  case 'r':
935  {
936  if (LocaleCompare("read-mask",option+1) == 0)
937  {
938  if (*option == '+')
939  break;
940  i++;
941  if (i == (ssize_t) argc)
942  ThrowCompareException(OptionError,"MissingArgument",option);
943  break;
944  }
945  if (LocaleCompare("regard-warnings",option+1) == 0)
946  break;
947  if (LocaleCompare("repage",option+1) == 0)
948  {
949  if (*option == '+')
950  break;
951  i++;
952  if (i == (ssize_t) argc)
953  ThrowCompareException(OptionError,"MissingArgument",option);
954  if (IsGeometry(argv[i]) == MagickFalse)
955  ThrowCompareInvalidArgumentException(option,argv[i]);
956  break;
957  }
958  if (LocaleCompare("resize",option+1) == 0)
959  {
960  if (*option == '+')
961  break;
962  i++;
963  if (i == (ssize_t) argc)
964  ThrowCompareException(OptionError,"MissingArgument",option);
965  if (IsGeometry(argv[i]) == MagickFalse)
966  ThrowCompareInvalidArgumentException(option,argv[i]);
967  break;
968  }
969  if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
970  {
971  respect_parentheses=(*option == '-') ? MagickTrue : MagickFalse;
972  break;
973  }
974  if (LocaleCompare("rotate",option+1) == 0)
975  {
976  i++;
977  if (i == (ssize_t) argc)
978  ThrowCompareException(OptionError,"MissingArgument",option);
979  if (IsGeometry(argv[i]) == MagickFalse)
980  ThrowCompareInvalidArgumentException(option,argv[i]);
981  break;
982  }
983  ThrowCompareException(OptionError,"UnrecognizedOption",option)
984  }
985  case 's':
986  {
987  if (LocaleCompare("sampling-factor",option+1) == 0)
988  {
989  if (*option == '+')
990  break;
991  i++;
992  if (i == (ssize_t) argc)
993  ThrowCompareException(OptionError,"MissingArgument",option);
994  if (IsGeometry(argv[i]) == MagickFalse)
995  ThrowCompareInvalidArgumentException(option,argv[i]);
996  break;
997  }
998  if (LocaleCompare("seed",option+1) == 0)
999  {
1000  if (*option == '+')
1001  break;
1002  i++;
1003  if (i == (ssize_t) argc)
1004  ThrowCompareException(OptionError,"MissingArgument",option);
1005  if (IsGeometry(argv[i]) == MagickFalse)
1006  ThrowCompareInvalidArgumentException(option,argv[i]);
1007  break;
1008  }
1009  if (LocaleCompare("separate",option+1) == 0)
1010  break;
1011  if (LocaleCompare("set",option+1) == 0)
1012  {
1013  i++;
1014  if (i == (ssize_t) argc)
1015  ThrowCompareException(OptionError,"MissingArgument",option);
1016  if (*option == '+')
1017  break;
1018  i++;
1019  if (i == (ssize_t) argc)
1020  ThrowCompareException(OptionError,"MissingArgument",option);
1021  break;
1022  }
1023  if (LocaleCompare("sigmoidal-contrast",option+1) == 0)
1024  {
1025  i++;
1026  if (i == (ssize_t) argc)
1027  ThrowCompareException(OptionError,"MissingArgument",option);
1028  if (IsGeometry(argv[i]) == MagickFalse)
1029  ThrowCompareInvalidArgumentException(option,argv[i]);
1030  break;
1031  }
1032  if (LocaleCompare("similarity-threshold",option+1) == 0)
1033  {
1034  if (*option == '+')
1035  break;
1036  i++;
1037  if (i == (ssize_t) argc)
1038  ThrowCompareException(OptionError,"MissingArgument",option);
1039  if (IsGeometry(argv[i]) == MagickFalse)
1040  ThrowCompareInvalidArgumentException(option,argv[i]);
1041  if (*option == '+')
1042  similarity_threshold=DefaultSimilarityThreshold;
1043  else
1044  similarity_threshold=StringToDouble(argv[i],(char **) NULL);
1045  break;
1046  }
1047  if (LocaleCompare("size",option+1) == 0)
1048  {
1049  if (*option == '+')
1050  break;
1051  i++;
1052  if (i == (ssize_t) argc)
1053  ThrowCompareException(OptionError,"MissingArgument",option);
1054  if (IsGeometry(argv[i]) == MagickFalse)
1055  ThrowCompareInvalidArgumentException(option,argv[i]);
1056  break;
1057  }
1058  if (LocaleCompare("subimage-search",option+1) == 0)
1059  {
1060  if (*option == '+')
1061  {
1062  subimage_search=MagickFalse;
1063  break;
1064  }
1065  subimage_search=MagickTrue;
1066  break;
1067  }
1068  if (LocaleCompare("synchronize",option+1) == 0)
1069  break;
1070  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1071  }
1072  case 't':
1073  {
1074  if (LocaleCompare("taint",option+1) == 0)
1075  break;
1076  if (LocaleCompare("transparent-color",option+1) == 0)
1077  {
1078  if (*option == '+')
1079  break;
1080  i++;
1081  if (i == (ssize_t) argc)
1082  ThrowCompareException(OptionError,"MissingArgument",option);
1083  break;
1084  }
1085  if (LocaleCompare("trim",option+1) == 0)
1086  break;
1087  if (LocaleCompare("type",option+1) == 0)
1088  {
1089  ssize_t
1090  type;
1091 
1092  if (*option == '+')
1093  break;
1094  i++;
1095  if (i == (ssize_t) argc)
1096  ThrowCompareException(OptionError,"MissingArgument",option);
1097  type=ParseCommandOption(MagickTypeOptions,MagickFalse,argv[i]);
1098  if (type < 0)
1099  ThrowCompareException(OptionError,"UnrecognizedImageType",
1100  argv[i]);
1101  break;
1102  }
1103  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1104  }
1105  case 'v':
1106  {
1107  if (LocaleCompare("verbose",option+1) == 0)
1108  break;
1109  if ((LocaleCompare("version",option+1) == 0) ||
1110  (LocaleCompare("-version",option+1) == 0))
1111  {
1112  ListMagickVersion(stdout);
1113  break;
1114  }
1115  if (LocaleCompare("virtual-pixel",option+1) == 0)
1116  {
1117  ssize_t
1118  method;
1119 
1120  if (*option == '+')
1121  break;
1122  i++;
1123  if (i == (ssize_t) argc)
1124  ThrowCompareException(OptionError,"MissingArgument",option);
1125  method=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse,
1126  argv[i]);
1127  if (method < 0)
1128  ThrowCompareException(OptionError,
1129  "UnrecognizedVirtualPixelMethod",argv[i]);
1130  break;
1131  }
1132  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1133  }
1134  case 'w':
1135  {
1136  if (LocaleCompare("write",option+1) == 0)
1137  {
1138  i++;
1139  if (i == (ssize_t) argc)
1140  ThrowCompareException(OptionError,"MissingArgument",option);
1141  break;
1142  }
1143  if (LocaleCompare("write-mask",option+1) == 0)
1144  {
1145  if (*option == '+')
1146  break;
1147  i++;
1148  if (i == (ssize_t) argc)
1149  ThrowCompareException(OptionError,"MissingArgument",option);
1150  break;
1151  }
1152  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1153  }
1154  case '?':
1155  break;
1156  default:
1157  ThrowCompareException(OptionError,"UnrecognizedOption",option)
1158  }
1159  fire=(GetCommandOptionFlags(MagickCommandOptions,MagickFalse,option) &
1160  FireOptionFlag) == 0 ? MagickFalse : MagickTrue;
1161  if (fire != MagickFalse)
1162  FireImageStack(MagickTrue,MagickTrue,MagickTrue);
1163  }
1164  if (k != 0)
1165  ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
1166  if (i-- != ((ssize_t) argc-1))
1167  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1168  if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1169  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1170  FinalizeImageSettings(image_info,image,MagickTrue);
1171  if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
1172  ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
1173  image=GetImageFromList(image,0);
1174  reconstruct_image=GetImageFromList(image,1);
1175  offset.x=0;
1176  offset.y=0;
1177  if (subimage_search != MagickFalse)
1178  {
1179  similarity_image=SimilarityImage(image,reconstruct_image,metric,
1180  similarity_threshold,&offset,&similarity_metric,exception);
1181  if (similarity_image == (Image *) NULL)
1182  return(MagickFalse);
1183  if (similarity_metric >= dissimilarity_threshold)
1184  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1185  "ImagesTooDissimilar","`%s'",image->filename);
1186  }
1187  if (similarity_image == (Image *) NULL)
1188  difference_image=CompareImages(image,reconstruct_image,metric,&distortion,
1189  exception);
1190  else
1191  {
1192  Image
1193  *composite_image;
1194 
1195  /*
1196  Determine if reconstructed image is a subimage of the image.
1197  */
1198  composite_image=CloneImage(image,0,0,MagickTrue,exception);
1199  if (composite_image == (Image *) NULL)
1200  difference_image=CompareImages(image,reconstruct_image,metric,
1201  &distortion,exception);
1202  else
1203  {
1204  Image
1205  *distort_image;
1206 
1207  RectangleInfo
1208  page;
1209 
1210  (void) CompositeImage(composite_image,reconstruct_image,
1211  CopyCompositeOp,MagickTrue,offset.x,offset.y,exception);
1212  difference_image=CompareImages(image,composite_image,metric,
1213  &distortion,exception);
1214  if (difference_image != (Image *) NULL)
1215  {
1216  difference_image->page.x=offset.x;
1217  difference_image->page.y=offset.y;
1218  }
1219  composite_image=DestroyImage(composite_image);
1220  page.width=reconstruct_image->columns;
1221  page.height=reconstruct_image->rows;
1222  page.x=offset.x;
1223  page.y=offset.y;
1224  distort_image=CropImage(image,&page,exception);
1225  if (distort_image != (Image *) NULL)
1226  {
1227  Image
1228  *sans_image;
1229 
1230  (void) SetImageArtifact(distort_image,"compare:virtual-pixels",
1231  "false");
1232  sans_image=CompareImages(distort_image,reconstruct_image,metric,
1233  &distortion,exception);
1234  if (sans_image != (Image *) NULL)
1235  sans_image=DestroyImage(sans_image);
1236  distort_image=DestroyImage(distort_image);
1237  }
1238  }
1239  if (difference_image != (Image *) NULL)
1240  {
1241  AppendImageToList(&difference_image,similarity_image);
1242  similarity_image=(Image *) NULL;
1243  }
1244  }
1245  switch (metric)
1246  {
1247  case AbsoluteErrorMetric:
1248  {
1249  size_t
1250  columns,
1251  rows;
1252 
1253  SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1254  scale=(double) columns*rows;
1255  break;
1256  }
1257  case DotProductCorrelationErrorMetric:
1258  case PhaseCorrelationErrorMetric:
1259  case NormalizedCrossCorrelationErrorMetric:
1260  {
1261  double
1262  maxima = 0.0,
1263  minima = 0.0;
1264 
1265  (void) GetImageRange(reconstruct_image,&minima,&maxima,exception);
1266  if (fabs(maxima-minima) < MagickEpsilon)
1267  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1268  CompareConstantColorException,"(%s)",CommandOptionToMnemonic(
1269  MagickMetricOptions,(ssize_t) metric));
1270  break;
1271  }
1272  case PeakAbsoluteErrorMetric:
1273  {
1274  if ((subimage_search != MagickFalse) &&
1275  (image->columns == reconstruct_image->columns) &&
1276  (image->rows == reconstruct_image->rows))
1277  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1278  CompareEqualSizedException,"(%s)",CommandOptionToMnemonic(
1279  MagickMetricOptions,(ssize_t) metric));
1280  break;
1281  }
1282  case PerceptualHashErrorMetric:
1283  {
1284  scale=1.0;
1285  if (subimage_search == MagickFalse)
1286  {
1287  double
1288  maxima = 0.0,
1289  minima = 0.0;
1290 
1291  (void) GetImageRange(reconstruct_image,&minima,&maxima,exception);
1292  if (fabs(maxima-minima) < MagickEpsilon)
1293  (void) ThrowMagickException(exception,GetMagickModule(),
1294  ImageWarning,CompareConstantColorException,"(%s)",
1295  CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1296  }
1297  if ((subimage_search != MagickFalse) &&
1298  (image->columns == reconstruct_image->columns) &&
1299  (image->rows == reconstruct_image->rows))
1300  (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1301  CompareEqualSizedException,"(%s)",CommandOptionToMnemonic(
1302  MagickMetricOptions,(ssize_t) metric));
1303  break;
1304  }
1305  case PeakSignalToNoiseRatioErrorMetric:
1306  {
1307  scale=MagickSafePSNRRecipicol(10.0);
1308  break;
1309  }
1310  case PixelDifferenceCountErrorMetric:
1311  {
1312  scale=1.0;
1313  normal_scale=1.0/(double) image->columns/(double) image->rows;
1314  break;
1315  }
1316  default:
1317  break;
1318  }
1319  if (fabs(distortion) > CompareEpsilon)
1320  similar=MagickFalse;
1321  if (difference_image == (Image *) NULL)
1322  status=0;
1323  else
1324  {
1325  if (image_info->verbose != MagickFalse)
1326  (void) SetImageColorMetric(image,reconstruct_image,exception);
1327  if (*difference_image->magick == '\0')
1328  (void) CopyMagickString(difference_image->magick,image->magick,
1329  MagickPathExtent);
1330  if (image_info->verbose == MagickFalse)
1331  {
1332  switch (metric)
1333  {
1334  case MeanErrorPerPixelErrorMetric:
1335  {
1336  if (subimage_search == MagickFalse)
1337  {
1338  (void) FormatLocaleFile(stderr,"%.*g (%.*g, %.*g)",
1339  GetMagickPrecision(),scale*distortion,
1340  GetMagickPrecision(),distortion,GetMagickPrecision(),
1341  image->error.normalized_maximum_error);
1342  break;
1343  }
1344  magick_fallthrough;
1345  }
1346  default:
1347  {
1348  (void) FormatLocaleFile(stderr,"%.*g (%.*g)",GetMagickPrecision(),
1349  scale*distortion,GetMagickPrecision(),normal_scale*distortion);
1350  break;
1351  }
1352  }
1353  if (subimage_search != MagickFalse)
1354  (void) FormatLocaleFile(stderr," @ %.17g,%.17g [%.*g]",
1355  (double) offset.x,(double) offset.y,GetMagickPrecision(),
1356  similarity_metric);
1357  }
1358  else
1359  {
1360  double
1361  *channel_distortion;
1362 
1363  channel_distortion=GetImageDistortions(image,reconstruct_image,
1364  metric,exception);
1365  (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename);
1366  if ((reconstruct_image->columns != image->columns) ||
1367  (reconstruct_image->rows != image->rows))
1368  (void) FormatLocaleFile(stderr,"Offset: %.17g,%.17g\n",(double)
1369  difference_image->page.x,(double) difference_image->page.y);
1370  (void) FormatLocaleFile(stderr," Channel distortion: %s\n",
1371  CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1372  switch (metric)
1373  {
1374  case FuzzErrorMetric:
1375  case MeanAbsoluteErrorMetric:
1376  case MeanSquaredErrorMetric:
1377  case PeakAbsoluteErrorMetric:
1378  case RootMeanSquaredErrorMetric:
1379  {
1380  switch (image->colorspace)
1381  {
1382  case RGBColorspace:
1383  default:
1384  {
1385  (void) FormatLocaleFile(stderr," red: %.*g (%.*g)\n",
1386  GetMagickPrecision(),scale*
1387  channel_distortion[RedPixelChannel],GetMagickPrecision(),
1388  normal_scale*channel_distortion[RedPixelChannel]);
1389  (void) FormatLocaleFile(stderr," green: %.*g (%.*g)\n",
1390  GetMagickPrecision(),scale*
1391  channel_distortion[GreenPixelChannel],GetMagickPrecision(),
1392  normal_scale*channel_distortion[GreenPixelChannel]);
1393  (void) FormatLocaleFile(stderr," blue: %.*g (%.*g)\n",
1394  GetMagickPrecision(),scale*
1395  channel_distortion[BluePixelChannel],GetMagickPrecision(),
1396  normal_scale*channel_distortion[BluePixelChannel]);
1397  if (image->alpha_trait != UndefinedPixelTrait)
1398  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1399  GetMagickPrecision(),scale*
1400  channel_distortion[AlphaPixelChannel],GetMagickPrecision(),
1401  normal_scale*channel_distortion[AlphaPixelChannel]);
1402  break;
1403  }
1404  case CMYKColorspace:
1405  {
1406  (void) FormatLocaleFile(stderr," cyan: %.*g (%.*g)\n",
1407  GetMagickPrecision(),scale*
1408  channel_distortion[CyanPixelChannel],GetMagickPrecision(),
1409  normal_scale*channel_distortion[CyanPixelChannel]);
1410  (void) FormatLocaleFile(stderr," magenta: %.*g (%.*g)\n",
1411  GetMagickPrecision(),scale*
1412  channel_distortion[MagentaPixelChannel],
1413  GetMagickPrecision(),
1414  normal_scale*channel_distortion[MagentaPixelChannel]);
1415  (void) FormatLocaleFile(stderr," yellow: %.*g (%.*g)\n",
1416  GetMagickPrecision(),scale*
1417  channel_distortion[YellowPixelChannel],GetMagickPrecision(),
1418  normal_scale*channel_distortion[YellowPixelChannel]);
1419  (void) FormatLocaleFile(stderr," black: %.*g (%.*g)\n",
1420  GetMagickPrecision(),scale*
1421  channel_distortion[BlackPixelChannel],GetMagickPrecision(),
1422  normal_scale*channel_distortion[BlackPixelChannel]);
1423  if (image->alpha_trait != UndefinedPixelTrait)
1424  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1425  GetMagickPrecision(),scale*
1426  channel_distortion[AlphaPixelChannel],
1427  GetMagickPrecision(),
1428  normal_scale*channel_distortion[AlphaPixelChannel]);
1429  break;
1430  }
1431  case LinearGRAYColorspace:
1432  case GRAYColorspace:
1433  {
1434  (void) FormatLocaleFile(stderr," gray: %.*g (%.*g)\n",
1435  GetMagickPrecision(),scale*
1436  channel_distortion[GrayPixelChannel],GetMagickPrecision(),
1437  normal_scale*channel_distortion[GrayPixelChannel]);
1438  if (image->alpha_trait != UndefinedPixelTrait)
1439  (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1440  GetMagickPrecision(),scale*
1441  channel_distortion[AlphaPixelChannel],
1442  GetMagickPrecision(),
1443  normal_scale*channel_distortion[AlphaPixelChannel]);
1444  break;
1445  }
1446  }
1447  (void) FormatLocaleFile(stderr," all: %.*g (%.*g)\n",
1448  GetMagickPrecision(),scale*channel_distortion[MaxPixelChannels],
1449  GetMagickPrecision(),normal_scale*channel_distortion[MaxPixelChannels]);
1450  break;
1451  }
1452  case DotProductCorrelationErrorMetric:
1453  case NormalizedCrossCorrelationErrorMetric:
1454  case PeakSignalToNoiseRatioErrorMetric:
1455  case PerceptualHashErrorMetric:
1456  case PhaseCorrelationErrorMetric:
1457  case StructuralSimilarityErrorMetric:
1458  case StructuralDissimilarityErrorMetric:
1459  {
1460  switch (image->colorspace)
1461  {
1462  case RGBColorspace:
1463  default:
1464  {
1465  (void) FormatLocaleFile(stderr," red: %.*g\n",
1466  GetMagickPrecision(),channel_distortion[RedPixelChannel]);
1467  (void) FormatLocaleFile(stderr," green: %.*g\n",
1468  GetMagickPrecision(),channel_distortion[GreenPixelChannel]);
1469  (void) FormatLocaleFile(stderr," blue: %.*g\n",
1470  GetMagickPrecision(),channel_distortion[BluePixelChannel]);
1471  if (image->alpha_trait != UndefinedPixelTrait)
1472  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1473  GetMagickPrecision(),
1474  channel_distortion[AlphaPixelChannel]);
1475  break;
1476  }
1477  case CMYKColorspace:
1478  {
1479  (void) FormatLocaleFile(stderr," cyan: %.*g\n",
1480  GetMagickPrecision(),channel_distortion[CyanPixelChannel]);
1481  (void) FormatLocaleFile(stderr," magenta: %.*g\n",
1482  GetMagickPrecision(),
1483  channel_distortion[MagentaPixelChannel]);
1484  (void) FormatLocaleFile(stderr," yellow: %.*g\n",
1485  GetMagickPrecision(),
1486  channel_distortion[YellowPixelChannel]);
1487  (void) FormatLocaleFile(stderr," black: %.*g\n",
1488  GetMagickPrecision(),
1489  channel_distortion[BlackPixelChannel]);
1490  if (image->alpha_trait != UndefinedPixelTrait)
1491  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1492  GetMagickPrecision(),
1493  channel_distortion[AlphaPixelChannel]);
1494  break;
1495  }
1496  case LinearGRAYColorspace:
1497  case GRAYColorspace:
1498  {
1499  (void) FormatLocaleFile(stderr," gray: %.*g\n",
1500  GetMagickPrecision(),channel_distortion[GrayPixelChannel]);
1501  if (image->alpha_trait != UndefinedPixelTrait)
1502  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1503  GetMagickPrecision(),
1504  channel_distortion[AlphaPixelChannel]);
1505  break;
1506  }
1507  }
1508  (void) FormatLocaleFile(stderr," all: %.*g\n",
1509  GetMagickPrecision(),channel_distortion[MaxPixelChannels]);
1510  break;
1511  }
1512  case MeanErrorPerPixelErrorMetric:
1513  {
1514  (void) FormatLocaleFile(stderr," %.*g (%.*g, %.*g)\n",
1515  GetMagickPrecision(),channel_distortion[MaxPixelChannels],
1516  GetMagickPrecision(),channel_distortion[MaxPixelChannels],
1517  GetMagickPrecision(),image->error.normalized_maximum_error);
1518  break;
1519  }
1520  case AbsoluteErrorMetric:
1521  case PixelDifferenceCountErrorMetric:
1522  {
1523  switch (image->colorspace)
1524  {
1525  case RGBColorspace:
1526  default:
1527  {
1528  (void) FormatLocaleFile(stderr," red: %.*g\n",
1529  GetMagickPrecision(),scale*
1530  channel_distortion[RedPixelChannel]);
1531  (void) FormatLocaleFile(stderr," green: %.*g\n",
1532  GetMagickPrecision(),scale*
1533  channel_distortion[GreenPixelChannel]);
1534  (void) FormatLocaleFile(stderr," blue: %.*g\n",
1535  GetMagickPrecision(),scale*
1536  channel_distortion[BluePixelChannel]);
1537  if (image->alpha_trait != UndefinedPixelTrait)
1538  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1539  GetMagickPrecision(),scale*
1540  channel_distortion[AlphaPixelChannel]);
1541  break;
1542  }
1543  case CMYKColorspace:
1544  {
1545  (void) FormatLocaleFile(stderr," cyan: %.*g\n",
1546  GetMagickPrecision(),channel_distortion[CyanPixelChannel]);
1547  (void) FormatLocaleFile(stderr," magenta: %.*g\n",
1548  GetMagickPrecision(),scale*
1549  channel_distortion[MagentaPixelChannel]);
1550  (void) FormatLocaleFile(stderr," yellow: %.*g\n",
1551  GetMagickPrecision(),scale*
1552  channel_distortion[YellowPixelChannel]);
1553  (void) FormatLocaleFile(stderr," black: %.*g\n",
1554  GetMagickPrecision(),scale*
1555  channel_distortion[BlackPixelChannel]);
1556  if (image->alpha_trait != UndefinedPixelTrait)
1557  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1558  GetMagickPrecision(),scale*
1559  channel_distortion[AlphaPixelChannel]);
1560  break;
1561  }
1562  case LinearGRAYColorspace:
1563  case GRAYColorspace:
1564  {
1565  (void) FormatLocaleFile(stderr," gray: %.*g\n",
1566  GetMagickPrecision(),scale*
1567  channel_distortion[GrayPixelChannel]);
1568  if (image->alpha_trait != UndefinedPixelTrait)
1569  (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1570  GetMagickPrecision(),scale*
1571  channel_distortion[AlphaPixelChannel]);
1572  break;
1573  }
1574  }
1575  (void) FormatLocaleFile(stderr," all: %.*g\n",
1576  GetMagickPrecision(),scale*
1577  channel_distortion[MaxPixelChannels]);
1578  break;
1579  }
1580  case UndefinedErrorMetric:
1581  break;
1582  }
1583  channel_distortion=(double *) RelinquishMagickMemory(
1584  channel_distortion);
1585  if (subimage_search != MagickFalse)
1586  {
1587  (void) FormatLocaleFile(stderr," Offset: %.17g,%.17g\n",
1588  (double) difference_image->page.x,(double)
1589  difference_image->page.y);
1590  (void) FormatLocaleFile(stderr," Similarity metric: %*g\n",
1591  GetMagickPrecision(),similarity_metric);
1592  (void) FormatLocaleFile(stderr," Similarity threshold: %*g\n",
1593  GetMagickPrecision(),similarity_threshold);
1594  (void) FormatLocaleFile(stderr,
1595  " Dissimilarity threshold: %*g\n",GetMagickPrecision(),
1596  dissimilarity_threshold);
1597  }
1598  }
1599  (void) ResetImagePage(difference_image,"0x0+0+0");
1600  if (difference_image->next != (Image *) NULL)
1601  (void) ResetImagePage(difference_image->next,"0x0+0+0");
1602  status&=(MagickStatusType) WriteImages(image_info,difference_image,
1603  argv[argc-1],exception);
1604  if ((metadata != (char **) NULL) && (format != (char *) NULL))
1605  {
1606  char
1607  *text;
1608 
1609  text=InterpretImageProperties(image_info,difference_image,format,
1610  exception);
1611  if (text == (char *) NULL)
1612  ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1613  (char *) NULL);
1614  (void) ConcatenateString(&(*metadata),text);
1615  text=DestroyString(text);
1616  }
1617  difference_image=DestroyImageList(difference_image);
1618  }
1619  DestroyCompare();
1620  if (similar == MagickFalse)
1621  (void) SetImageOption(image_info,"compare:dissimilar","true");
1622  return(status != 0 ? MagickTrue : MagickFalse);
1623 }