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