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