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