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