MagickWand 7.1.2-28
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 return(MagickFalse);
1183 if (similarity_metric >= dissimilarity_threshold)
1184 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1185 "ImagesTooDissimilar","`%s'",image->filename);
1186 }
1187 if (similarity_image == (Image *) NULL)
1188 difference_image=CompareImages(image,reconstruct_image,metric,&distortion,
1189 exception);
1190 else
1191 {
1192 Image
1193 *composite_image;
1194
1195 /*
1196 Determine if reconstructed image is a subimage of the image.
1197 */
1198 composite_image=CloneImage(image,0,0,MagickTrue,exception);
1199 if (composite_image == (Image *) NULL)
1200 difference_image=CompareImages(image,reconstruct_image,metric,
1201 &distortion,exception);
1202 else
1203 {
1204 Image
1205 *distort_image;
1206
1207 RectangleInfo
1208 page;
1209
1210 (void) CompositeImage(composite_image,reconstruct_image,
1211 CopyCompositeOp,MagickTrue,offset.x,offset.y,exception);
1212 difference_image=CompareImages(image,composite_image,metric,
1213 &distortion,exception);
1214 if (difference_image != (Image *) NULL)
1215 {
1216 difference_image->page.x=offset.x;
1217 difference_image->page.y=offset.y;
1218 }
1219 composite_image=DestroyImage(composite_image);
1220 page.width=reconstruct_image->columns;
1221 page.height=reconstruct_image->rows;
1222 page.x=offset.x;
1223 page.y=offset.y;
1224 distort_image=CropImage(image,&page,exception);
1225 if (distort_image != (Image *) NULL)
1226 {
1227 Image
1228 *sans_image;
1229
1230 (void) SetImageArtifact(distort_image,"compare:virtual-pixels",
1231 "false");
1232 sans_image=CompareImages(distort_image,reconstruct_image,metric,
1233 &distortion,exception);
1234 if (sans_image != (Image *) NULL)
1235 sans_image=DestroyImage(sans_image);
1236 distort_image=DestroyImage(distort_image);
1237 }
1238 }
1239 if (difference_image != (Image *) NULL)
1240 {
1241 AppendImageToList(&difference_image,similarity_image);
1242 similarity_image=(Image *) NULL;
1243 }
1244 }
1245 switch (metric)
1246 {
1247 case AbsoluteErrorMetric:
1248 {
1249 size_t
1250 columns,
1251 rows;
1252
1253 SetImageCompareBounds(image,reconstruct_image,&columns,&rows);
1254 scale=(double) columns*rows;
1255 break;
1256 }
1257 case DotProductCorrelationErrorMetric:
1258 case PhaseCorrelationErrorMetric:
1259 case NormalizedCrossCorrelationErrorMetric:
1260 {
1261 double
1262 maxima = 0.0,
1263 minima = 0.0;
1264
1265 (void) GetImageRange(reconstruct_image,&minima,&maxima,exception);
1266 if (fabs(maxima-minima) < MagickEpsilon)
1267 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1268 CompareConstantColorException,"(%s)",CommandOptionToMnemonic(
1269 MagickMetricOptions,(ssize_t) metric));
1270 break;
1271 }
1272 case PeakAbsoluteErrorMetric:
1273 {
1274 if ((subimage_search != MagickFalse) &&
1275 (image->columns == reconstruct_image->columns) &&
1276 (image->rows == reconstruct_image->rows))
1277 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1278 CompareEqualSizedException,"(%s)",CommandOptionToMnemonic(
1279 MagickMetricOptions,(ssize_t) metric));
1280 break;
1281 }
1282 case PerceptualHashErrorMetric:
1283 {
1284 scale=1.0;
1285 if (subimage_search == MagickFalse)
1286 {
1287 double
1288 maxima = 0.0,
1289 minima = 0.0;
1290
1291 (void) GetImageRange(reconstruct_image,&minima,&maxima,exception);
1292 if (fabs(maxima-minima) < MagickEpsilon)
1293 (void) ThrowMagickException(exception,GetMagickModule(),
1294 ImageWarning,CompareConstantColorException,"(%s)",
1295 CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1296 }
1297 if ((subimage_search != MagickFalse) &&
1298 (image->columns == reconstruct_image->columns) &&
1299 (image->rows == reconstruct_image->rows))
1300 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1301 CompareEqualSizedException,"(%s)",CommandOptionToMnemonic(
1302 MagickMetricOptions,(ssize_t) metric));
1303 break;
1304 }
1305 case PeakSignalToNoiseRatioErrorMetric:
1306 {
1307 scale=MagickSafePSNRRecipicol(10.0);
1308 break;
1309 }
1310 case PixelDifferenceCountErrorMetric:
1311 {
1312 scale=1.0;
1313 normal_scale=1.0/(double) image->columns/(double) image->rows;
1314 break;
1315 }
1316 default:
1317 break;
1318 }
1319 if (fabs(distortion) > CompareEpsilon)
1320 similar=MagickFalse;
1321 if (difference_image == (Image *) NULL)
1322 status=0;
1323 else
1324 {
1325 if (image_info->verbose != MagickFalse)
1326 (void) SetImageColorMetric(image,reconstruct_image,exception);
1327 if (*difference_image->magick == '\0')
1328 (void) CopyMagickString(difference_image->magick,image->magick,
1329 MagickPathExtent);
1330 if (image_info->verbose == MagickFalse)
1331 {
1332 switch (metric)
1333 {
1334 case MeanErrorPerPixelErrorMetric:
1335 {
1336 if (subimage_search == MagickFalse)
1337 {
1338 (void) FormatLocaleFile(stderr,"%.*g (%.*g, %.*g)",
1339 GetMagickPrecision(),scale*distortion,
1340 GetMagickPrecision(),distortion,GetMagickPrecision(),
1341 image->error.normalized_maximum_error);
1342 break;
1343 }
1344 magick_fallthrough;
1345 }
1346 default:
1347 {
1348 (void) FormatLocaleFile(stderr,"%.*g (%.*g)",GetMagickPrecision(),
1349 scale*distortion,GetMagickPrecision(),normal_scale*distortion);
1350 break;
1351 }
1352 }
1353 if (subimage_search != MagickFalse)
1354 (void) FormatLocaleFile(stderr," @ %.17g,%.17g [%.*g]",
1355 (double) offset.x,(double) offset.y,GetMagickPrecision(),
1356 similarity_metric);
1357 }
1358 else
1359 {
1360 double
1361 *channel_distortion;
1362
1363 channel_distortion=GetImageDistortions(image,reconstruct_image,
1364 metric,exception);
1365 (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename);
1366 if ((reconstruct_image->columns != image->columns) ||
1367 (reconstruct_image->rows != image->rows))
1368 (void) FormatLocaleFile(stderr,"Offset: %.17g,%.17g\n",(double)
1369 difference_image->page.x,(double) difference_image->page.y);
1370 (void) FormatLocaleFile(stderr," Channel distortion: %s\n",
1371 CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric));
1372 switch (metric)
1373 {
1374 case FuzzErrorMetric:
1375 case MeanAbsoluteErrorMetric:
1376 case MeanSquaredErrorMetric:
1377 case PeakAbsoluteErrorMetric:
1378 case RootMeanSquaredErrorMetric:
1379 {
1380 switch (image->colorspace)
1381 {
1382 case RGBColorspace:
1383 default:
1384 {
1385 (void) FormatLocaleFile(stderr," red: %.*g (%.*g)\n",
1386 GetMagickPrecision(),scale*
1387 channel_distortion[RedPixelChannel],GetMagickPrecision(),
1388 normal_scale*channel_distortion[RedPixelChannel]);
1389 (void) FormatLocaleFile(stderr," green: %.*g (%.*g)\n",
1390 GetMagickPrecision(),scale*
1391 channel_distortion[GreenPixelChannel],GetMagickPrecision(),
1392 normal_scale*channel_distortion[GreenPixelChannel]);
1393 (void) FormatLocaleFile(stderr," blue: %.*g (%.*g)\n",
1394 GetMagickPrecision(),scale*
1395 channel_distortion[BluePixelChannel],GetMagickPrecision(),
1396 normal_scale*channel_distortion[BluePixelChannel]);
1397 if (image->alpha_trait != UndefinedPixelTrait)
1398 (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1399 GetMagickPrecision(),scale*
1400 channel_distortion[AlphaPixelChannel],GetMagickPrecision(),
1401 normal_scale*channel_distortion[AlphaPixelChannel]);
1402 break;
1403 }
1404 case CMYKColorspace:
1405 {
1406 (void) FormatLocaleFile(stderr," cyan: %.*g (%.*g)\n",
1407 GetMagickPrecision(),scale*
1408 channel_distortion[CyanPixelChannel],GetMagickPrecision(),
1409 normal_scale*channel_distortion[CyanPixelChannel]);
1410 (void) FormatLocaleFile(stderr," magenta: %.*g (%.*g)\n",
1411 GetMagickPrecision(),scale*
1412 channel_distortion[MagentaPixelChannel],
1413 GetMagickPrecision(),
1414 normal_scale*channel_distortion[MagentaPixelChannel]);
1415 (void) FormatLocaleFile(stderr," yellow: %.*g (%.*g)\n",
1416 GetMagickPrecision(),scale*
1417 channel_distortion[YellowPixelChannel],GetMagickPrecision(),
1418 normal_scale*channel_distortion[YellowPixelChannel]);
1419 (void) FormatLocaleFile(stderr," black: %.*g (%.*g)\n",
1420 GetMagickPrecision(),scale*
1421 channel_distortion[BlackPixelChannel],GetMagickPrecision(),
1422 normal_scale*channel_distortion[BlackPixelChannel]);
1423 if (image->alpha_trait != UndefinedPixelTrait)
1424 (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1425 GetMagickPrecision(),scale*
1426 channel_distortion[AlphaPixelChannel],
1427 GetMagickPrecision(),
1428 normal_scale*channel_distortion[AlphaPixelChannel]);
1429 break;
1430 }
1431 case LinearGRAYColorspace:
1432 case GRAYColorspace:
1433 {
1434 (void) FormatLocaleFile(stderr," gray: %.*g (%.*g)\n",
1435 GetMagickPrecision(),scale*
1436 channel_distortion[GrayPixelChannel],GetMagickPrecision(),
1437 normal_scale*channel_distortion[GrayPixelChannel]);
1438 if (image->alpha_trait != UndefinedPixelTrait)
1439 (void) FormatLocaleFile(stderr," alpha: %.*g (%.*g)\n",
1440 GetMagickPrecision(),scale*
1441 channel_distortion[AlphaPixelChannel],
1442 GetMagickPrecision(),
1443 normal_scale*channel_distortion[AlphaPixelChannel]);
1444 break;
1445 }
1446 }
1447 (void) FormatLocaleFile(stderr," all: %.*g (%.*g)\n",
1448 GetMagickPrecision(),scale*channel_distortion[MaxPixelChannels],
1449 GetMagickPrecision(),normal_scale*channel_distortion[MaxPixelChannels]);
1450 break;
1451 }
1452 case DotProductCorrelationErrorMetric:
1453 case NormalizedCrossCorrelationErrorMetric:
1454 case PeakSignalToNoiseRatioErrorMetric:
1455 case PerceptualHashErrorMetric:
1456 case PhaseCorrelationErrorMetric:
1457 case StructuralSimilarityErrorMetric:
1458 case StructuralDissimilarityErrorMetric:
1459 {
1460 switch (image->colorspace)
1461 {
1462 case RGBColorspace:
1463 default:
1464 {
1465 (void) FormatLocaleFile(stderr," red: %.*g\n",
1466 GetMagickPrecision(),channel_distortion[RedPixelChannel]);
1467 (void) FormatLocaleFile(stderr," green: %.*g\n",
1468 GetMagickPrecision(),channel_distortion[GreenPixelChannel]);
1469 (void) FormatLocaleFile(stderr," blue: %.*g\n",
1470 GetMagickPrecision(),channel_distortion[BluePixelChannel]);
1471 if (image->alpha_trait != UndefinedPixelTrait)
1472 (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1473 GetMagickPrecision(),
1474 channel_distortion[AlphaPixelChannel]);
1475 break;
1476 }
1477 case CMYKColorspace:
1478 {
1479 (void) FormatLocaleFile(stderr," cyan: %.*g\n",
1480 GetMagickPrecision(),channel_distortion[CyanPixelChannel]);
1481 (void) FormatLocaleFile(stderr," magenta: %.*g\n",
1482 GetMagickPrecision(),
1483 channel_distortion[MagentaPixelChannel]);
1484 (void) FormatLocaleFile(stderr," yellow: %.*g\n",
1485 GetMagickPrecision(),
1486 channel_distortion[YellowPixelChannel]);
1487 (void) FormatLocaleFile(stderr," black: %.*g\n",
1488 GetMagickPrecision(),
1489 channel_distortion[BlackPixelChannel]);
1490 if (image->alpha_trait != UndefinedPixelTrait)
1491 (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1492 GetMagickPrecision(),
1493 channel_distortion[AlphaPixelChannel]);
1494 break;
1495 }
1496 case LinearGRAYColorspace:
1497 case GRAYColorspace:
1498 {
1499 (void) FormatLocaleFile(stderr," gray: %.*g\n",
1500 GetMagickPrecision(),channel_distortion[GrayPixelChannel]);
1501 if (image->alpha_trait != UndefinedPixelTrait)
1502 (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1503 GetMagickPrecision(),
1504 channel_distortion[AlphaPixelChannel]);
1505 break;
1506 }
1507 }
1508 (void) FormatLocaleFile(stderr," all: %.*g\n",
1509 GetMagickPrecision(),channel_distortion[MaxPixelChannels]);
1510 break;
1511 }
1512 case MeanErrorPerPixelErrorMetric:
1513 {
1514 (void) FormatLocaleFile(stderr," %.*g (%.*g, %.*g)\n",
1515 GetMagickPrecision(),channel_distortion[MaxPixelChannels],
1516 GetMagickPrecision(),channel_distortion[MaxPixelChannels],
1517 GetMagickPrecision(),image->error.normalized_maximum_error);
1518 break;
1519 }
1520 case AbsoluteErrorMetric:
1521 case PixelDifferenceCountErrorMetric:
1522 {
1523 switch (image->colorspace)
1524 {
1525 case RGBColorspace:
1526 default:
1527 {
1528 (void) FormatLocaleFile(stderr," red: %.*g\n",
1529 GetMagickPrecision(),scale*
1530 channel_distortion[RedPixelChannel]);
1531 (void) FormatLocaleFile(stderr," green: %.*g\n",
1532 GetMagickPrecision(),scale*
1533 channel_distortion[GreenPixelChannel]);
1534 (void) FormatLocaleFile(stderr," blue: %.*g\n",
1535 GetMagickPrecision(),scale*
1536 channel_distortion[BluePixelChannel]);
1537 if (image->alpha_trait != UndefinedPixelTrait)
1538 (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1539 GetMagickPrecision(),scale*
1540 channel_distortion[AlphaPixelChannel]);
1541 break;
1542 }
1543 case CMYKColorspace:
1544 {
1545 (void) FormatLocaleFile(stderr," cyan: %.*g\n",
1546 GetMagickPrecision(),channel_distortion[CyanPixelChannel]);
1547 (void) FormatLocaleFile(stderr," magenta: %.*g\n",
1548 GetMagickPrecision(),scale*
1549 channel_distortion[MagentaPixelChannel]);
1550 (void) FormatLocaleFile(stderr," yellow: %.*g\n",
1551 GetMagickPrecision(),scale*
1552 channel_distortion[YellowPixelChannel]);
1553 (void) FormatLocaleFile(stderr," black: %.*g\n",
1554 GetMagickPrecision(),scale*
1555 channel_distortion[BlackPixelChannel]);
1556 if (image->alpha_trait != UndefinedPixelTrait)
1557 (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1558 GetMagickPrecision(),scale*
1559 channel_distortion[AlphaPixelChannel]);
1560 break;
1561 }
1562 case LinearGRAYColorspace:
1563 case GRAYColorspace:
1564 {
1565 (void) FormatLocaleFile(stderr," gray: %.*g\n",
1566 GetMagickPrecision(),scale*
1567 channel_distortion[GrayPixelChannel]);
1568 if (image->alpha_trait != UndefinedPixelTrait)
1569 (void) FormatLocaleFile(stderr," alpha: %.*g\n",
1570 GetMagickPrecision(),scale*
1571 channel_distortion[AlphaPixelChannel]);
1572 break;
1573 }
1574 }
1575 (void) FormatLocaleFile(stderr," all: %.*g\n",
1576 GetMagickPrecision(),scale*
1577 channel_distortion[MaxPixelChannels]);
1578 break;
1579 }
1580 case UndefinedErrorMetric:
1581 break;
1582 }
1583 channel_distortion=(double *) RelinquishMagickMemory(
1584 channel_distortion);
1585 if (subimage_search != MagickFalse)
1586 {
1587 (void) FormatLocaleFile(stderr," Offset: %.17g,%.17g\n",
1588 (double) difference_image->page.x,(double)
1589 difference_image->page.y);
1590 (void) FormatLocaleFile(stderr," Similarity metric: %*g\n",
1591 GetMagickPrecision(),similarity_metric);
1592 (void) FormatLocaleFile(stderr," Similarity threshold: %*g\n",
1593 GetMagickPrecision(),similarity_threshold);
1594 (void) FormatLocaleFile(stderr,
1595 " Dissimilarity threshold: %*g\n",GetMagickPrecision(),
1596 dissimilarity_threshold);
1597 }
1598 }
1599 (void) ResetImagePage(difference_image,"0x0+0+0");
1600 if (difference_image->next != (Image *) NULL)
1601 (void) ResetImagePage(difference_image->next,"0x0+0+0");
1602 status&=(MagickStatusType) WriteImages(image_info,difference_image,
1603 argv[argc-1],exception);
1604 if ((metadata != (char **) NULL) && (format != (char *) NULL))
1605 {
1606 char
1607 *text;
1608
1609 text=InterpretImageProperties(image_info,difference_image,format,
1610 exception);
1611 if (text == (char *) NULL)
1612 ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1613 (char *) NULL);
1614 (void) ConcatenateString(&(*metadata),text);
1615 text=DestroyString(text);
1616 }
1617 difference_image=DestroyImageList(difference_image);
1618 }
1619 DestroyCompare();
1620 if (similar == MagickFalse)
1621 (void) SetImageOption(image_info,"compare:dissimilar","true");
1622 return(status != 0 ? MagickTrue : MagickFalse);
1623}