PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/wand/compare.c

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