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