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