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-2016 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% http://www.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 83static MagickBooleanType CompareUsage(void) 84{ 85 const char 86 **p; 87 88 static const char 89 *miscellaneous[]= 90 { 91 "-channel mask set the image channel mask", 92 "-debug events display copious debugging information", 93 "-help print program options", 94 "-list type print a list of supported option arguments", 95 "-log format format of debugging information", 96 (char *) NULL 97 }, 98 *settings[]= 99 { 100 "-alpha option on, activate, off, deactivate, set, opaque, copy", 101 " transparent, extract, background, or shape", 102 "-authenticate password", 103 " decipher image with this password", 104 "-colorspace type alternate image colorspace", 105 "-compose operator set image composite operator", 106 "-compress type type of pixel compression when writing the image", 107 "-decipher filename convert cipher pixels to plain pixels", 108 "-define format:option", 109 " define one or more image format options", 110 "-density geometry horizontal and vertical density of the image", 111 "-depth value image depth", 112 "-dissimilarity-threshold value", 113 " maximum distortion for (sub)image match", 114 "-encipher filename convert plain pixels to cipher pixels", 115 "-extract geometry extract area from image", 116 "-format \"string\" output formatted image characteristics", 117 "-fuzz distance colors within this distance are considered equal", 118 "-highlight-color color", 119 " empasize pixel differences with this color", 120 "-identify identify the format and characteristics of the image", 121 "-interlace type type of image interlacing scheme", 122 "-limit type value pixel cache resource limit", 123 "-lowlight-color color", 124 " de-emphasize pixel differences with this color", 125 "-metric type measure differences between images with this metric", 126 "-monitor monitor progress", 127 "-profile filename add, delete, or apply an image profile", 128 "-quality value JPEG/MIFF/PNG compression level", 129 "-quiet suppress all warning messages", 130 "-quantize colorspace reduce colors in this colorspace", 131 "-regard-warnings pay attention to warning messages", 132 "-respect-parentheses settings remain in effect until parenthesis boundary", 133 "-sampling-factor geometry", 134 " horizontal and vertical sampling factor", 135 "-seed value seed a new sequence of pseudo-random numbers", 136 "-set attribute value set an image attribute", 137 "-quality value JPEG/MIFF/PNG compression level", 138 "-similarity-threshold value", 139 " minimum distortion for (sub)image match", 140 "-size geometry width and height of image", 141 "-subimage-search search for subimage", 142 "-synchronize synchronize image to storage device", 143 "-taint declare the image as modified", 144 "-transparent-color color", 145 " transparent color", 146 "-type type image type", 147 "-verbose print detailed information about the image", 148 "-version print version information", 149 "-virtual-pixel method", 150 " virtual pixel access method", 151 (char *) NULL 152 }; 153 154 ListMagickVersion(stdout); 155 (void) printf("Usage: %s [options ...] image reconstruct difference\n", 156 GetClientName()); 157 (void) printf("\nImage Settings:\n"); 158 for (p=settings; *p != (char *) NULL; p++) 159 (void) printf(" %s\n",*p); 160 (void) printf("\nMiscellaneous Options:\n"); 161 for (p=miscellaneous; *p != (char *) NULL; p++) 162 (void) printf(" %s\n",*p); 163 (void) printf( 164 "\nBy default, the image format of 'file' is determined by its magic\n"); 165 (void) printf( 166 "number. To specify a particular image format, precede the filename\n"); 167 (void) printf( 168 "with an image format name and a colon (i.e. ps:image) or specify the\n"); 169 (void) printf( 170 "image type as the filename suffix (i.e. image.ps). Specify 'file' as\n"); 171 (void) printf("'-' for standard input or output.\n"); 172 return(MagickFalse); 173} 174 175WandExport MagickBooleanType CompareImagesCommand(ImageInfo *image_info, 176 int argc,char **argv,char **metadata,ExceptionInfo *exception) 177{ 178#define CompareEpsilon (1.0e-06) 179#define DefaultDissimilarityThreshold 0.31830988618379067154 180#define DefaultSimilarityThreshold (-1.0) 181#define DestroyCompare() \ 182{ \ 183 if (similarity_image != (Image *) NULL) \ 184 similarity_image=DestroyImageList(similarity_image); \ 185 if (difference_image != (Image *) NULL) \ 186 difference_image=DestroyImageList(difference_image); \ 187 DestroyImageStack(); \ 188 for (i=0; i < (ssize_t) argc; i++) \ 189 argv[i]=DestroyString(argv[i]); \ 190 argv=(char **) RelinquishMagickMemory(argv); \ 191} 192#define ThrowCompareException(asperity,tag,option) \ 193{ \ 194 if (exception->severity < (asperity)) \ 195 (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \ 196 "`%s'",option); \ 197 DestroyCompare(); \ 198 return(MagickFalse); \ 199} 200#define ThrowCompareInvalidArgumentException(option,argument) \ 201{ \ 202 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \ 203 "InvalidArgument","'%s': %s",option,argument); \ 204 DestroyCompare(); \ 205 return(MagickFalse); \ 206} 207 208 char 209 *filename, 210 *option; 211 212 const char 213 *format; 214 215 double 216 dissimilarity_threshold, 217 distortion, 218 similarity_metric, 219 similarity_threshold; 220 221 Image 222 *difference_image, 223 *image, 224 *reconstruct_image, 225 *similarity_image; 226 227 ImageStack 228 image_stack[MaxImageStackDepth+1]; 229 230 MagickBooleanType 231 fire, 232 pend, 233 respect_parenthesis, 234 subimage_search; 235 236 MagickStatusType 237 status; 238 239 MetricType 240 metric; 241 242 RectangleInfo 243 offset; 244 245 register ssize_t 246 i; 247 248 ssize_t 249 j, 250 k; 251 252 /* 253 Set defaults. 254 */ 255 assert(image_info != (ImageInfo *) NULL); 256 assert(image_info->signature == MagickCoreSignature); 257 if (image_info->debug != MagickFalse) 258 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 259 assert(exception != (ExceptionInfo *) NULL); 260 if (argc == 2) 261 { 262 option=argv[1]; 263 if ((LocaleCompare("version",option+1) == 0) || 264 (LocaleCompare("-version",option+1) == 0)) 265 { 266 ListMagickVersion(stdout); 267 return(MagickTrue); 268 } 269 } 270 if (argc < 3) 271 return(CompareUsage()); 272 difference_image=NewImageList(); 273 similarity_image=NewImageList(); 274 dissimilarity_threshold=DefaultDissimilarityThreshold; 275 similarity_threshold=DefaultSimilarityThreshold; 276 distortion=0.0; 277 format=(char *) NULL; 278 j=1; 279 k=0; 280 metric=UndefinedErrorMetric; 281 NewImageStack(); 282 option=(char *) NULL; 283 pend=MagickFalse; 284 reconstruct_image=NewImageList(); 285 respect_parenthesis=MagickFalse; 286 status=MagickTrue; 287 subimage_search=MagickFalse; 288 /* 289 Compare an image. 290 */ 291 ReadCommandlLine(argc,&argv); 292 status=ExpandFilenames(&argc,&argv); 293 if (status == MagickFalse) 294 ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed", 295 GetExceptionMessage(errno)); 296 for (i=1; i < (ssize_t) (argc-1); i++) 297 { 298 option=argv[i]; 299 if (LocaleCompare(option,"(") == 0) 300 { 301 FireImageStack(MagickTrue,MagickTrue,pend); 302 if (k == MaxImageStackDepth) 303 ThrowCompareException(OptionError,"ParenthesisNestedTooDeeply", 304 option); 305 PushImageStack(); 306 continue; 307 } 308 if (LocaleCompare(option,")") == 0) 309 { 310 FireImageStack(MagickTrue,MagickTrue,MagickTrue); 311 if (k == 0) 312 ThrowCompareException(OptionError,"UnableToParseExpression",option); 313 PopImageStack(); 314 continue; 315 } 316 if (IsCommandOption(option) == MagickFalse) 317 { 318 Image 319 *images; 320 321 /* 322 Read input image. 323 */ 324 FireImageStack(MagickFalse,MagickFalse,pend); 325 filename=argv[i]; 326 if ((LocaleCompare(filename,"--") == 0) && (i < (ssize_t) (argc-1))) 327 filename=argv[++i]; 328 images=ReadImages(image_info,filename,exception); 329 status&=(images != (Image *) NULL) && 330 (exception->severity < ErrorException); 331 if (images == (Image *) NULL) 332 continue; 333 AppendImageStack(images); 334 continue; 335 } 336 pend=image != (Image *) NULL ? MagickTrue : MagickFalse; 337 switch (*(option+1)) 338 { 339 case 'a': 340 { 341 if (LocaleCompare("alpha",option+1) == 0) 342 { 343 ssize_t 344 type; 345 346 if (*option == '+') 347 break; 348 i++; 349 if (i == (ssize_t) argc) 350 ThrowCompareException(OptionError,"MissingArgument",option); 351 type=ParseCommandOption(MagickAlphaChannelOptions,MagickFalse,argv[i]); 352 if (type < 0) 353 ThrowCompareException(OptionError,"UnrecognizedAlphaChannelOption", 354 argv[i]); 355 break; 356 } 357 if (LocaleCompare("authenticate",option+1) == 0) 358 { 359 if (*option == '+') 360 break; 361 i++; 362 if (i == (ssize_t) argc) 363 ThrowCompareException(OptionError,"MissingArgument",option); 364 break; 365 } 366 ThrowCompareException(OptionError,"UnrecognizedOption",option); 367 } 368 case 'c': 369 { 370 if (LocaleCompare("cache",option+1) == 0) 371 { 372 if (*option == '+') 373 break; 374 i++; 375 if (i == (ssize_t) argc) 376 ThrowCompareException(OptionError,"MissingArgument",option); 377 if (IsGeometry(argv[i]) == MagickFalse) 378 ThrowCompareInvalidArgumentException(option,argv[i]); 379 break; 380 } 381 if (LocaleCompare("channel",option+1) == 0) 382 { 383 ssize_t 384 channel; 385 386 if (*option == '+') 387 break; 388 i++; 389 if (i == (ssize_t) argc) 390 ThrowCompareException(OptionError,"MissingArgument",option); 391 channel=ParseChannelOption(argv[i]); 392 if (channel < 0) 393 ThrowCompareException(OptionError,"UnrecognizedChannelType", 394 argv[i]); 395 (void) SetPixelChannelMask(image,(ChannelType) channel); 396 break; 397 } 398 if (LocaleCompare("colorspace",option+1) == 0) 399 { 400 ssize_t 401 colorspace; 402 403 if (*option == '+') 404 break; 405 i++; 406 if (i == (ssize_t) argc) 407 ThrowCompareException(OptionError,"MissingArgument",option); 408 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse, 409 argv[i]); 410 if (colorspace < 0) 411 ThrowCompareException(OptionError,"UnrecognizedColorspace", 412 argv[i]); 413 break; 414 } 415 if (LocaleCompare("compose",option+1) == 0) 416 { 417 ssize_t 418 compose; 419 420 if (*option == '+') 421 break; 422 i++; 423 if (i == (ssize_t) argc) 424 ThrowCompareException(OptionError,"MissingArgument",option); 425 compose=ParseCommandOption(MagickComposeOptions,MagickFalse, 426 argv[i]); 427 if (compose < 0) 428 ThrowCompareException(OptionError,"UnrecognizedComposeOperator", 429 argv[i]); 430 break; 431 } 432 if (LocaleCompare("compress",option+1) == 0) 433 { 434 ssize_t 435 compress; 436 437 if (*option == '+') 438 break; 439 i++; 440 if (i == (ssize_t) argc) 441 ThrowCompareException(OptionError,"MissingArgument",option); 442 compress=ParseCommandOption(MagickCompressOptions,MagickFalse, 443 argv[i]); 444 if (compress < 0) 445 ThrowCompareException(OptionError,"UnrecognizedImageCompression", 446 argv[i]); 447 break; 448 } 449 if (LocaleCompare("concurrent",option+1) == 0) 450 break; 451 ThrowCompareException(OptionError,"UnrecognizedOption",option) 452 } 453 case 'd': 454 { 455 if (LocaleCompare("debug",option+1) == 0) 456 { 457 LogEventType 458 event_mask; 459 460 if (*option == '+') 461 break; 462 i++; 463 if (i == (ssize_t) argc) 464 ThrowCompareException(OptionError,"MissingArgument",option); 465 event_mask=SetLogEventMask(argv[i]); 466 if (event_mask == UndefinedEvents) 467 ThrowCompareException(OptionError,"UnrecognizedEventType", 468 argv[i]); 469 break; 470 } 471 if (LocaleCompare("decipher",option+1) == 0) 472 { 473 if (*option == '+') 474 break; 475 i++; 476 if (i == (ssize_t) argc) 477 ThrowCompareException(OptionError,"MissingArgument",option); 478 break; 479 } 480 if (LocaleCompare("define",option+1) == 0) 481 { 482 i++; 483 if (i == (ssize_t) argc) 484 ThrowCompareException(OptionError,"MissingArgument",option); 485 if (*option == '+') 486 { 487 const char 488 *define; 489 490 define=GetImageOption(image_info,argv[i]); 491 if (define == (const char *) NULL) 492 ThrowCompareException(OptionError,"NoSuchOption",argv[i]); 493 break; 494 } 495 break; 496 } 497 if (LocaleCompare("density",option+1) == 0) 498 { 499 if (*option == '+') 500 break; 501 i++; 502 if (i == (ssize_t) argc) 503 ThrowCompareException(OptionError,"MissingArgument",option); 504 if (IsGeometry(argv[i]) == MagickFalse) 505 ThrowCompareInvalidArgumentException(option,argv[i]); 506 break; 507 } 508 if (LocaleCompare("depth",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 if (LocaleCompare("dissimilarity-threshold",option+1) == 0) 520 { 521 if (*option == '+') 522 break; 523 i++; 524 if (i == (ssize_t) argc) 525 ThrowCompareException(OptionError,"MissingArgument",option); 526 if (IsGeometry(argv[i]) == MagickFalse) 527 ThrowCompareInvalidArgumentException(option,argv[i]); 528 if (*option == '+') 529 dissimilarity_threshold=DefaultDissimilarityThreshold; 530 else 531 dissimilarity_threshold=StringToDouble(argv[i],(char **) NULL); 532 break; 533 } 534 if (LocaleCompare("duration",option+1) == 0) 535 { 536 if (*option == '+') 537 break; 538 i++; 539 if (i == (ssize_t) argc) 540 ThrowCompareException(OptionError,"MissingArgument",option); 541 if (IsGeometry(argv[i]) == MagickFalse) 542 ThrowCompareInvalidArgumentException(option,argv[i]); 543 break; 544 } 545 ThrowCompareException(OptionError,"UnrecognizedOption",option) 546 } 547 case 'e': 548 { 549 if (LocaleCompare("encipher",option+1) == 0) 550 { 551 if (*option == '+') 552 break; 553 i++; 554 if (i == (ssize_t) argc) 555 ThrowCompareException(OptionError,"MissingArgument",option); 556 break; 557 } 558 if (LocaleCompare("extract",option+1) == 0) 559 { 560 if (*option == '+') 561 break; 562 i++; 563 if (i == (ssize_t) argc) 564 ThrowCompareException(OptionError,"MissingArgument",option); 565 if (IsGeometry(argv[i]) == MagickFalse) 566 ThrowCompareInvalidArgumentException(option,argv[i]); 567 break; 568 } 569 ThrowCompareException(OptionError,"UnrecognizedOption",option) 570 } 571 case 'f': 572 { 573 if (LocaleCompare("format",option+1) == 0) 574 { 575 if (*option == '+') 576 break; 577 i++; 578 if (i == (ssize_t) argc) 579 ThrowCompareException(OptionError,"MissingArgument",option); 580 format=argv[i]; 581 break; 582 } 583 if (LocaleCompare("fuzz",option+1) == 0) 584 { 585 if (*option == '+') 586 break; 587 i++; 588 if (i == (ssize_t) argc) 589 ThrowCompareException(OptionError,"MissingArgument",option); 590 if (IsGeometry(argv[i]) == MagickFalse) 591 ThrowCompareInvalidArgumentException(option,argv[i]); 592 break; 593 } 594 ThrowCompareException(OptionError,"UnrecognizedOption",option) 595 } 596 case 'h': 597 { 598 if ((LocaleCompare("help",option+1) == 0) || 599 (LocaleCompare("-help",option+1) == 0)) 600 return(CompareUsage()); 601 if (LocaleCompare("highlight-color",option+1) == 0) 602 { 603 if (*option == '+') 604 break; 605 i++; 606 if (i == (ssize_t) argc) 607 ThrowCompareException(OptionError,"MissingArgument",option); 608 break; 609 } 610 ThrowCompareException(OptionError,"UnrecognizedOption",option) 611 } 612 case 'i': 613 { 614 if (LocaleCompare("identify",option+1) == 0) 615 break; 616 if (LocaleCompare("interlace",option+1) == 0) 617 { 618 ssize_t 619 interlace; 620 621 if (*option == '+') 622 break; 623 i++; 624 if (i == (ssize_t) argc) 625 ThrowCompareException(OptionError,"MissingArgument",option); 626 interlace=ParseCommandOption(MagickInterlaceOptions,MagickFalse, 627 argv[i]); 628 if (interlace < 0) 629 ThrowCompareException(OptionError,"UnrecognizedInterlaceType", 630 argv[i]); 631 break; 632 } 633 ThrowCompareException(OptionError,"UnrecognizedOption",option) 634 } 635 case 'l': 636 { 637 if (LocaleCompare("limit",option+1) == 0) 638 { 639 char 640 *p; 641 642 double 643 value; 644 645 ssize_t 646 resource; 647 648 if (*option == '+') 649 break; 650 i++; 651 if (i == (ssize_t) argc) 652 ThrowCompareException(OptionError,"MissingArgument",option); 653 resource=ParseCommandOption(MagickResourceOptions,MagickFalse, 654 argv[i]); 655 if (resource < 0) 656 ThrowCompareException(OptionError,"UnrecognizedResourceType", 657 argv[i]); 658 i++; 659 if (i == (ssize_t) argc) 660 ThrowCompareException(OptionError,"MissingArgument",option); 661 value=StringToDouble(argv[i],&p); 662 (void) value; 663 if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0)) 664 ThrowCompareInvalidArgumentException(option,argv[i]); 665 break; 666 } 667 if (LocaleCompare("list",option+1) == 0) 668 { 669 ssize_t 670 list; 671 672 if (*option == '+') 673 break; 674 i++; 675 if (i == (ssize_t) argc) 676 ThrowCompareException(OptionError,"MissingArgument",option); 677 list=ParseCommandOption(MagickListOptions,MagickFalse,argv[i]); 678 if (list < 0) 679 ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]); 680 status=MogrifyImageInfo(image_info,(int) (i-j+1),(const char **) 681 argv+j,exception); 682 DestroyCompare(); 683 return(status == 0 ? MagickTrue : MagickFalse); 684 } 685 if (LocaleCompare("log",option+1) == 0) 686 { 687 if (*option == '+') 688 break; 689 i++; 690 if ((i == (ssize_t) argc) || (strchr(argv[i],'%') == (char *) NULL)) 691 ThrowCompareException(OptionError,"MissingArgument",option); 692 break; 693 } 694 if (LocaleCompare("lowlight-color",option+1) == 0) 695 { 696 if (*option == '+') 697 break; 698 i++; 699 if (i == (ssize_t) argc) 700 ThrowCompareException(OptionError,"MissingArgument",option); 701 break; 702 } 703 ThrowCompareException(OptionError,"UnrecognizedOption",option) 704 } 705 case 'm': 706 { 707 if (LocaleCompare("matte",option+1) == 0) 708 break; 709 if (LocaleCompare("metric",option+1) == 0) 710 { 711 ssize_t 712 type; 713 714 if (*option == '+') 715 break; 716 i++; 717 if (i == (ssize_t) argc) 718 ThrowCompareException(OptionError,"MissingArgument",option); 719 type=ParseCommandOption(MagickMetricOptions,MagickTrue,argv[i]); 720 if (type < 0) 721 ThrowCompareException(OptionError,"UnrecognizedMetricType", 722 argv[i]); 723 metric=(MetricType) type; 724 break; 725 } 726 if (LocaleCompare("monitor",option+1) == 0) 727 break; 728 ThrowCompareException(OptionError,"UnrecognizedOption",option) 729 } 730 case 'p': 731 { 732 if (LocaleCompare("profile",option+1) == 0) 733 { 734 i++; 735 if (i == (ssize_t) argc) 736 ThrowCompareException(OptionError,"MissingArgument",option); 737 break; 738 } 739 ThrowCompareException(OptionError,"UnrecognizedOption",option) 740 } 741 case 'q': 742 { 743 if (LocaleCompare("quality",option+1) == 0) 744 { 745 if (*option == '+') 746 break; 747 i++; 748 if (i == (ssize_t) argc) 749 ThrowCompareException(OptionError,"MissingArgument",option); 750 if (IsGeometry(argv[i]) == MagickFalse) 751 ThrowCompareInvalidArgumentException(option,argv[i]); 752 break; 753 } 754 if (LocaleCompare("quantize",option+1) == 0) 755 { 756 ssize_t 757 colorspace; 758 759 if (*option == '+') 760 break; 761 i++; 762 if (i == (ssize_t) argc) 763 ThrowCompareException(OptionError,"MissingArgument",option); 764 colorspace=ParseCommandOption(MagickColorspaceOptions, 765 MagickFalse,argv[i]); 766 if (colorspace < 0) 767 ThrowCompareException(OptionError,"UnrecognizedColorspace", 768 argv[i]); 769 break; 770 } 771 if (LocaleCompare("quiet",option+1) == 0) 772 break; 773 ThrowCompareException(OptionError,"UnrecognizedOption",option) 774 } 775 case 'r': 776 { 777 if (LocaleCompare("regard-warnings",option+1) == 0) 778 break; 779 if (LocaleNCompare("respect-parentheses",option+1,17) == 0) 780 { 781 respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse; 782 break; 783 } 784 ThrowCompareException(OptionError,"UnrecognizedOption",option) 785 } 786 case 's': 787 { 788 if (LocaleCompare("sampling-factor",option+1) == 0) 789 { 790 if (*option == '+') 791 break; 792 i++; 793 if (i == (ssize_t) argc) 794 ThrowCompareException(OptionError,"MissingArgument",option); 795 if (IsGeometry(argv[i]) == MagickFalse) 796 ThrowCompareInvalidArgumentException(option,argv[i]); 797 break; 798 } 799 if (LocaleCompare("seed",option+1) == 0) 800 { 801 if (*option == '+') 802 break; 803 i++; 804 if (i == (ssize_t) argc) 805 ThrowCompareException(OptionError,"MissingArgument",option); 806 if (IsGeometry(argv[i]) == MagickFalse) 807 ThrowCompareInvalidArgumentException(option,argv[i]); 808 break; 809 } 810 if (LocaleCompare("set",option+1) == 0) 811 { 812 i++; 813 if (i == (ssize_t) argc) 814 ThrowCompareException(OptionError,"MissingArgument",option); 815 if (*option == '+') 816 break; 817 i++; 818 if (i == (ssize_t) argc) 819 ThrowCompareException(OptionError,"MissingArgument",option); 820 break; 821 } 822 if (LocaleCompare("similarity-threshold",option+1) == 0) 823 { 824 if (*option == '+') 825 break; 826 i++; 827 if (i == (ssize_t) argc) 828 ThrowCompareException(OptionError,"MissingArgument",option); 829 if (IsGeometry(argv[i]) == MagickFalse) 830 ThrowCompareInvalidArgumentException(option,argv[i]); 831 if (*option == '+') 832 similarity_threshold=DefaultSimilarityThreshold; 833 else 834 similarity_threshold=StringToDouble(argv[i],(char **) NULL); 835 break; 836 } 837 if (LocaleCompare("size",option+1) == 0) 838 { 839 if (*option == '+') 840 break; 841 i++; 842 if (i == (ssize_t) argc) 843 ThrowCompareException(OptionError,"MissingArgument",option); 844 if (IsGeometry(argv[i]) == MagickFalse) 845 ThrowCompareInvalidArgumentException(option,argv[i]); 846 break; 847 } 848 if (LocaleCompare("subimage-search",option+1) == 0) 849 { 850 if (*option == '+') 851 { 852 subimage_search=MagickFalse; 853 break; 854 } 855 subimage_search=MagickTrue; 856 break; 857 } 858 if (LocaleCompare("synchronize",option+1) == 0) 859 break; 860 ThrowCompareException(OptionError,"UnrecognizedOption",option) 861 } 862 case 't': 863 { 864 if (LocaleCompare("taint",option+1) == 0) 865 break; 866 if (LocaleCompare("transparent-color",option+1) == 0) 867 { 868 if (*option == '+') 869 break; 870 i++; 871 if (i == (ssize_t) argc) 872 ThrowCompareException(OptionError,"MissingArgument",option); 873 break; 874 } 875 if (LocaleCompare("type",option+1) == 0) 876 { 877 ssize_t 878 type; 879 880 if (*option == '+') 881 break; 882 i++; 883 if (i == (ssize_t) argc) 884 ThrowCompareException(OptionError,"MissingArgument",option); 885 type=ParseCommandOption(MagickTypeOptions,MagickFalse,argv[i]); 886 if (type < 0) 887 ThrowCompareException(OptionError,"UnrecognizedImageType", 888 argv[i]); 889 break; 890 } 891 ThrowCompareException(OptionError,"UnrecognizedOption",option) 892 } 893 case 'v': 894 { 895 if (LocaleCompare("verbose",option+1) == 0) 896 break; 897 if ((LocaleCompare("version",option+1) == 0) || 898 (LocaleCompare("-version",option+1) == 0)) 899 { 900 ListMagickVersion(stdout); 901 break; 902 } 903 if (LocaleCompare("virtual-pixel",option+1) == 0) 904 { 905 ssize_t 906 method; 907 908 if (*option == '+') 909 break; 910 i++; 911 if (i == (ssize_t) argc) 912 ThrowCompareException(OptionError,"MissingArgument",option); 913 method=ParseCommandOption(MagickVirtualPixelOptions,MagickFalse, 914 argv[i]); 915 if (method < 0) 916 ThrowCompareException(OptionError, 917 "UnrecognizedVirtualPixelMethod",argv[i]); 918 break; 919 } 920 ThrowCompareException(OptionError,"UnrecognizedOption",option) 921 } 922 case '?': 923 break; 924 default: 925 ThrowCompareException(OptionError,"UnrecognizedOption",option) 926 } 927 fire=(GetCommandOptionFlags(MagickCommandOptions,MagickFalse,option) & 928 FireOptionFlag) == 0 ? MagickFalse : MagickTrue; 929 if (fire != MagickFalse) 930 FireImageStack(MagickTrue,MagickTrue,MagickTrue); 931 } 932 if (k != 0) 933 ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]); 934 if (i-- != (ssize_t) (argc-1)) 935 ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]); 936 if ((image == (Image *) NULL) || (GetImageListLength(image) < 2)) 937 ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]); 938 FinalizeImageSettings(image_info,image,MagickTrue); 939 if ((image == (Image *) NULL) || (GetImageListLength(image) < 2)) 940 ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]); 941 image=GetImageFromList(image,0); 942 reconstruct_image=GetImageFromList(image,1); 943 offset.x=0; 944 offset.y=0; 945 if (subimage_search != MagickFalse) 946 { 947 similarity_image=SimilarityImage(image,reconstruct_image,metric, 948 similarity_threshold,&offset,&similarity_metric,exception); 949 if (similarity_metric > dissimilarity_threshold) 950 ThrowCompareException(ImageError,"ImagesTooDissimilar",image->filename); 951 } 952 if ((reconstruct_image->columns == image->columns) && 953 (reconstruct_image->rows == image->rows)) 954 difference_image=CompareImages(image,reconstruct_image,metric,&distortion, 955 exception); 956 else 957 if (similarity_image == (Image *) NULL) 958 { 959 if (metric == PerceptualHashErrorMetric) 960 difference_image=CompareImages(image,reconstruct_image,metric, 961 &distortion,exception); 962 else 963 ThrowCompareException(OptionError,"ImageWidthsOrHeightsDiffer", 964 image->filename); 965 } 966 else 967 { 968 Image 969 *composite_image; 970 971 /* 972 Determine if reconstructed image is a subimage of the image. 973 */ 974 composite_image=CloneImage(image,0,0,MagickTrue,exception); 975 if (composite_image == (Image *) NULL) 976 difference_image=CompareImages(image,reconstruct_image,metric, 977 &distortion,exception); 978 else 979 { 980 Image 981 *distort_image; 982 983 RectangleInfo 984 page; 985 986 (void) CompositeImage(composite_image,reconstruct_image, 987 CopyCompositeOp,MagickTrue,offset.x,offset.y,exception); 988 difference_image=CompareImages(image,composite_image,metric, 989 &distortion,exception); 990 if (difference_image != (Image *) NULL) 991 { 992 difference_image->page.x=offset.x; 993 difference_image->page.y=offset.y; 994 } 995 composite_image=DestroyImage(composite_image); 996 page.width=reconstruct_image->columns; 997 page.height=reconstruct_image->rows; 998 page.x=offset.x; 999 page.y=offset.y; 1000 distort_image=CropImage(image,&page,exception); 1001 if (distort_image != (Image *) NULL) 1002 { 1003 Image 1004 *sans_image; 1005 1006 sans_image=CompareImages(distort_image,reconstruct_image,metric, 1007 &distortion,exception); 1008 distort_image=DestroyImage(distort_image); 1009 if (sans_image != (Image *) NULL) 1010 sans_image=DestroyImage(sans_image); 1011 } 1012 } 1013 if (difference_image != (Image *) NULL) 1014 { 1015 AppendImageToList(&difference_image,similarity_image); 1016 similarity_image=(Image *) NULL; 1017 } 1018 } 1019 if (difference_image == (Image *) NULL) 1020 status=0; 1021 else 1022 { 1023 if (image_info->verbose != MagickFalse) 1024 (void) SetImageColorMetric(image,reconstruct_image,exception); 1025 if (*difference_image->magick == '\0') 1026 (void) CopyMagickString(difference_image->magick,image->magick, 1027 MagickPathExtent); 1028 if (image_info->verbose == MagickFalse) 1029 { 1030 switch (metric) 1031 { 1032 case FuzzErrorMetric: 1033 case MeanAbsoluteErrorMetric: 1034 case MeanSquaredErrorMetric: 1035 case PeakAbsoluteErrorMetric: 1036 case RootMeanSquaredErrorMetric: 1037 { 1038 (void) FormatLocaleFile(stderr,"%g (%g)",QuantumRange*distortion, 1039 (double) distortion); 1040 break; 1041 } 1042 case AbsoluteErrorMetric: 1043 case NormalizedCrossCorrelationErrorMetric: 1044 case PeakSignalToNoiseRatioErrorMetric: 1045 case PerceptualHashErrorMetric: 1046 { 1047 (void) FormatLocaleFile(stderr,"%g",distortion); 1048 break; 1049 } 1050 case MeanErrorPerPixelErrorMetric: 1051 { 1052 (void) FormatLocaleFile(stderr,"%g (%g, %g)",distortion, 1053 image->error.normalized_mean_error, 1054 image->error.normalized_maximum_error); 1055 break; 1056 } 1057 case UndefinedErrorMetric: 1058 break; 1059 } 1060 if (subimage_search != MagickFalse) 1061 (void) FormatLocaleFile(stderr," @ %.20g,%.20g",(double) 1062 difference_image->page.x,(double) difference_image->page.y); 1063 } 1064 else 1065 { 1066 double 1067 *channel_distortion; 1068 1069 channel_distortion=GetImageDistortions(image,reconstruct_image, 1070 metric,exception); 1071 (void) FormatLocaleFile(stderr,"Image: %s\n",image->filename); 1072 if ((reconstruct_image->columns != image->columns) || 1073 (reconstruct_image->rows != image->rows)) 1074 (void) FormatLocaleFile(stderr,"Offset: %.20g,%.20g\n",(double) 1075 difference_image->page.x,(double) difference_image->page.y); 1076 (void) FormatLocaleFile(stderr," Channel distortion: %s\n", 1077 CommandOptionToMnemonic(MagickMetricOptions,(ssize_t) metric)); 1078 switch (metric) 1079 { 1080 case FuzzErrorMetric: 1081 case MeanAbsoluteErrorMetric: 1082 case MeanSquaredErrorMetric: 1083 case PeakAbsoluteErrorMetric: 1084 case RootMeanSquaredErrorMetric: 1085 { 1086 switch (image->colorspace) 1087 { 1088 case RGBColorspace: 1089 default: 1090 { 1091 (void) FormatLocaleFile(stderr," red: %g (%g)\n", 1092 QuantumRange*channel_distortion[RedPixelChannel], 1093 channel_distortion[RedPixelChannel]); 1094 (void) FormatLocaleFile(stderr," green: %g (%g)\n", 1095 QuantumRange*channel_distortion[GreenPixelChannel], 1096 channel_distortion[GreenPixelChannel]); 1097 (void) FormatLocaleFile(stderr," blue: %g (%g)\n", 1098 QuantumRange*channel_distortion[BluePixelChannel], 1099 channel_distortion[BluePixelChannel]); 1100 if (image->alpha_trait != UndefinedPixelTrait) 1101 (void) FormatLocaleFile(stderr," alpha: %g (%g)\n", 1102 QuantumRange*channel_distortion[AlphaPixelChannel], 1103 channel_distortion[AlphaPixelChannel]); 1104 break; 1105 } 1106 case CMYKColorspace: 1107 { 1108 (void) FormatLocaleFile(stderr," cyan: %g (%g)\n", 1109 QuantumRange*channel_distortion[CyanPixelChannel], 1110 channel_distortion[CyanPixelChannel]); 1111 (void) FormatLocaleFile(stderr," magenta: %g (%g)\n", 1112 QuantumRange*channel_distortion[MagentaPixelChannel], 1113 channel_distortion[MagentaPixelChannel]); 1114 (void) FormatLocaleFile(stderr," yellow: %g (%g)\n", 1115 QuantumRange*channel_distortion[YellowPixelChannel], 1116 channel_distortion[YellowPixelChannel]); 1117 (void) FormatLocaleFile(stderr," black: %g (%g)\n", 1118 QuantumRange*channel_distortion[BlackPixelChannel], 1119 channel_distortion[BlackPixelChannel]); 1120 if (image->alpha_trait != UndefinedPixelTrait) 1121 (void) FormatLocaleFile(stderr," alpha: %g (%g)\n", 1122 QuantumRange*channel_distortion[AlphaPixelChannel], 1123 channel_distortion[AlphaPixelChannel]); 1124 break; 1125 } 1126 case GRAYColorspace: 1127 { 1128 (void) FormatLocaleFile(stderr," gray: %g (%g)\n", 1129 QuantumRange*channel_distortion[GrayPixelChannel], 1130 channel_distortion[GrayPixelChannel]); 1131 if (image->alpha_trait != UndefinedPixelTrait) 1132 (void) FormatLocaleFile(stderr," alpha: %g (%g)\n", 1133 QuantumRange*channel_distortion[AlphaPixelChannel], 1134 channel_distortion[AlphaPixelChannel]); 1135 break; 1136 } 1137 } 1138 (void) FormatLocaleFile(stderr," all: %g (%g)\n", 1139 QuantumRange*channel_distortion[MaxPixelChannels], 1140 channel_distortion[MaxPixelChannels]); 1141 break; 1142 } 1143 case AbsoluteErrorMetric: 1144 case NormalizedCrossCorrelationErrorMetric: 1145 case PeakSignalToNoiseRatioErrorMetric: 1146 case PerceptualHashErrorMetric: 1147 { 1148 switch (image->colorspace) 1149 { 1150 case RGBColorspace: 1151 default: 1152 { 1153 (void) FormatLocaleFile(stderr," red: %g\n", 1154 channel_distortion[RedPixelChannel]); 1155 (void) FormatLocaleFile(stderr," green: %g\n", 1156 channel_distortion[GreenPixelChannel]); 1157 (void) FormatLocaleFile(stderr," blue: %g\n", 1158 channel_distortion[BluePixelChannel]); 1159 if (image->alpha_trait != UndefinedPixelTrait) 1160 (void) FormatLocaleFile(stderr," alpha: %g\n", 1161 channel_distortion[AlphaPixelChannel]); 1162 break; 1163 } 1164 case CMYKColorspace: 1165 { 1166 (void) FormatLocaleFile(stderr," cyan: %g\n", 1167 channel_distortion[CyanPixelChannel]); 1168 (void) FormatLocaleFile(stderr," magenta: %g\n", 1169 channel_distortion[MagentaPixelChannel]); 1170 (void) FormatLocaleFile(stderr," yellow: %g\n", 1171 channel_distortion[YellowPixelChannel]); 1172 (void) FormatLocaleFile(stderr," black: %g\n", 1173 channel_distortion[BlackPixelChannel]); 1174 if (image->alpha_trait != UndefinedPixelTrait) 1175 (void) FormatLocaleFile(stderr," alpha: %g\n", 1176 channel_distortion[AlphaPixelChannel]); 1177 break; 1178 } 1179 case GRAYColorspace: 1180 { 1181 (void) FormatLocaleFile(stderr," gray: %g\n", 1182 channel_distortion[GrayPixelChannel]); 1183 if (image->alpha_trait != UndefinedPixelTrait) 1184 (void) FormatLocaleFile(stderr," alpha: %g\n", 1185 channel_distortion[AlphaPixelChannel]); 1186 break; 1187 } 1188 } 1189 (void) FormatLocaleFile(stderr," all: %g\n", 1190 channel_distortion[MaxPixelChannels]); 1191 break; 1192 } 1193 case MeanErrorPerPixelErrorMetric: 1194 { 1195 (void) FormatLocaleFile(stderr," %g (%g, %g)\n", 1196 channel_distortion[MaxPixelChannels], 1197 image->error.normalized_mean_error, 1198 image->error.normalized_maximum_error); 1199 break; 1200 } 1201 case UndefinedErrorMetric: 1202 break; 1203 } 1204 channel_distortion=(double *) RelinquishMagickMemory( 1205 channel_distortion); 1206 if (subimage_search != MagickFalse) 1207 (void) FormatLocaleFile(stderr," Offset: %.20g,%.20g\n",(double) 1208 difference_image->page.x,(double) difference_image->page.y); 1209 } 1210 status&=WriteImages(image_info,difference_image,argv[argc-1],exception); 1211 if ((metadata != (char **) NULL) && (format != (char *) NULL)) 1212 { 1213 char 1214 *text; 1215 1216 text=InterpretImageProperties(image_info,difference_image,format, 1217 exception); 1218 if (text == (char *) NULL) 1219 ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed", 1220 GetExceptionMessage(errno)); 1221 (void) ConcatenateString(&(*metadata),text); 1222 text=DestroyString(text); 1223 } 1224 difference_image=DestroyImageList(difference_image); 1225 } 1226 DestroyCompare(); 1227 if ((metric == NormalizedCrossCorrelationErrorMetric) || 1228 (metric == UndefinedErrorMetric)) 1229 { 1230 if (fabs(distortion-1.0) > CompareEpsilon) 1231 (void) SetImageOption(image_info,"compare:dissimilar","true"); 1232 } 1233 else 1234 if (fabs(distortion) > CompareEpsilon) 1235 (void) SetImageOption(image_info,"compare:dissimilar","true"); 1236 return(status != 0 ? MagickTrue : MagickFalse); 1237} 1238