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