blob: e5299488b8441da0659794fee83fd870bb577fa2 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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-2009 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
cristy6a917d92009-10-06 19:23:54 +000037% difference between an image and its reconstruction.
cristy3ed852e2009-09-05 21:47:34 +000038%
39*/
40
41/*
42 Include declarations.
43*/
44#include "wand/studio.h"
45#include "wand/MagickWand.h"
46#include "wand/mogrify-private.h"
47
48/*
49%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50% %
51% %
52% %
53% C o m p a r e I m a g e C o m m a n d %
54% %
55% %
56% %
57%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58%
59% CompareImageCommand() compares two images and returns the difference between
60% them as a distortion metric and as a new image visually annotating their
61% differences.
62%
63% The format of the CompareImageCommand method is:
64%
65% MagickBooleanType CompareImageCommand(ImageInfo *image_info,int argc,
66% char **argv,char **metadata,ExceptionInfo *exception)
67%
68% A description of each parameter follows:
69%
70% o image_info: the image info.
71%
72% o argc: the number of elements in the argument vector.
73%
74% o argv: A text array containing the command line arguments.
75%
76% o metadata: any metadata is returned here.
77%
78% o exception: return any errors or warnings in this structure.
79%
80*/
81
82static MagickBooleanType CompareUsage(void)
83{
84 const char
85 **p;
86
87 static const char
88 *miscellaneous[]=
89 {
90 "-debug events display copious debugging information",
91 "-help print program options",
92 "-list type print a list of supported option arguments",
93 "-log format format of debugging information",
94 (char *) NULL
95 },
96 *settings[]=
97 {
98 "-alpha option on, activate, off, deactivate, set, opaque, copy",
99 " transparent, extract, background, or shape",
100 "-authenticate password",
101 " decipher image with this password",
102 "-channel type apply option to select image channels",
103 "-colorspace type alternate image colorspace",
104 "-compose operator set image composite operator",
105 "-compress type type of pixel compression when writing the image",
106 "-decipher filename convert cipher pixels to plain pixels",
107 "-define format:option",
108 " define one or more image format options",
109 "-density geometry horizontal and vertical density of the image",
110 "-depth value image depth",
111 "-dissimilarity-threshold value",
112 " maximum RMSE for (sub)image match",
113 "-encipher filename convert plain pixels to cipher pixels",
114 "-extract geometry extract area from image",
115 "-format \"string\" output formatted image characteristics",
116 "-fuzz distance colors within this distance are considered equal",
117 "-highlight-color color",
118 " empasize pixel differences with this color",
119 "-identify identify the format and characteristics of the image",
120 "-interlace type type of image interlacing scheme",
121 "-limit type value pixel cache resource limit",
122 "-lowlight-color color",
123 " de-emphasize pixel differences with this color",
124 "-metric type measure differences between images with this metric",
125 "-monitor monitor progress",
126 "-passphrase filename get the passphrase from this file",
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 "-size geometry width and height of image",
139 "-transparent-color color",
140 " transparent color",
141 "-type type image type",
142 "-verbose print detailed information about the image",
143 "-version print version information",
144 "-virtual-pixel method",
145 " virtual pixel access method",
146 (char *) NULL
147 };
148
149 (void) printf("Version: %s\n",GetMagickVersion((unsigned long *) NULL));
cristy610b2e22009-10-22 14:59:43 +0000150 (void) printf("Copyright: %s\n",GetMagickCopyright());
151 (void) printf("Features: %s\n\n",GetMagickFeatures());
cristy3ed852e2009-09-05 21:47:34 +0000152 (void) printf("Usage: %s [options ...] image reconstruct difference\n",
153 GetClientName());
154 (void) printf("\nImage Settings:\n");
155 for (p=settings; *p != (char *) NULL; p++)
156 (void) printf(" %s\n",*p);
157 (void) printf("\nMiscellaneous Options:\n");
158 for (p=miscellaneous; *p != (char *) NULL; p++)
159 (void) printf(" %s\n",*p);
160 (void) printf(
161 "\nBy default, the image format of `file' is determined by its magic\n");
162 (void) printf(
163 "number. To specify a particular image format, precede the filename\n");
164 (void) printf(
165 "with an image format name and a colon (i.e. ps:image) or specify the\n");
166 (void) printf(
167 "image type as the filename suffix (i.e. image.ps). Specify 'file' as\n");
168 (void) printf("'-' for standard input or output.\n");
169 return(MagickFalse);
170}
171
172WandExport MagickBooleanType CompareImageCommand(ImageInfo *image_info,
173 int argc,char **argv,char **metadata,ExceptionInfo *exception)
174{
175#define DefaultDissimilarityThreshold 0.2
176#define DestroyCompare() \
177{ \
178 if (similarity_image != (Image *) NULL) \
179 similarity_image=DestroyImageList(similarity_image); \
180 if (difference_image != (Image *) NULL) \
181 difference_image=DestroyImageList(difference_image); \
182 DestroyImageStack(); \
183 for (i=0; i < (long) argc; i++) \
184 argv[i]=DestroyString(argv[i]); \
185 argv=(char **) RelinquishMagickMemory(argv); \
186}
187#define ThrowCompareException(asperity,tag,option) \
188{ \
189 if (exception->severity < (asperity)) \
190 (void) ThrowMagickException(exception,GetMagickModule(),asperity,tag, \
191 "`%s'",option); \
192 DestroyCompare(); \
193 return(MagickFalse); \
194}
195#define ThrowCompareInvalidArgumentException(option,argument) \
196{ \
197 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \
198 "InvalidArgument","`%s': %s",option,argument); \
199 DestroyCompare(); \
200 return(MagickFalse); \
201}
202
203 char
204 *filename,
205 *option;
206
207 const char
208 *format;
209
210 ChannelType
211 channels;
212
213 double
214 dissimilarity_threshold,
215 distortion,
216 similarity_metric;
217
218 Image
219 *difference_image,
220 *image,
221 *reconstruct_image,
222 *similarity_image;
223
224 ImageStack
225 image_stack[MaxImageStackDepth+1];
226
227 long
228 j,
229 k;
230
231 MagickBooleanType
232 fire,
233 pend;
234
235 MagickStatusType
236 status;
237
238 MetricType
239 metric;
240
241 RectangleInfo
242 offset;
243
244 register long
245 i;
246
247 /*
248 Set defaults.
249 */
250 assert(image_info != (ImageInfo *) NULL);
251 assert(image_info->signature == MagickSignature);
252 if (image_info->debug != MagickFalse)
253 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
254 assert(exception != (ExceptionInfo *) NULL);
255 if (argc == 2)
256 {
257 option=argv[1];
258 if ((LocaleCompare("version",option+1) == 0) ||
259 (LocaleCompare("-version",option+1) == 0))
260 {
261 (void) fprintf(stdout,"Version: %s\n",
262 GetMagickVersion((unsigned long *) NULL));
cristy3980b0d2009-10-25 14:37:13 +0000263 (void) fprintf(stdout,"Copyright: %s\n",GetMagickCopyright());
cristy104cea82009-10-25 02:26:51 +0000264 (void) fprintf(stdout,"Features: %s\n\n",GetMagickFeatures());
cristy3ed852e2009-09-05 21:47:34 +0000265 return(MagickFalse);
266 }
267 }
268 if (argc < 3)
269 {
270 (void) CompareUsage();
271 return(MagickTrue);
272 }
273 channels=AllChannels;
274 difference_image=NewImageList();
275 similarity_image=NewImageList();
276 dissimilarity_threshold=DefaultDissimilarityThreshold;
277 distortion=0.0;
278 format=(char *) NULL;
279 j=1;
280 k=0;
281 metric=UndefinedMetric;
282 NewImageStack();
283 option=(char *) NULL;
284 pend=MagickFalse;
285 reconstruct_image=NewImageList();
286 status=MagickTrue;
287 /*
288 Compare an image.
289 */
290 ReadCommandlLine(argc,&argv);
291 status=ExpandFilenames(&argc,&argv);
292 if (status == MagickFalse)
293 ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
294 GetExceptionMessage(errno));
295 for (i=1; i < (long) (argc-1); i++)
296 {
297 option=argv[i];
298 if (LocaleCompare(option,"(") == 0)
299 {
300 FireImageStack(MagickTrue,MagickTrue,pend);
301 if (k == MaxImageStackDepth)
302 ThrowCompareException(OptionError,"ParenthesisNestedTooDeeply",
303 option);
304 PushImageStack();
305 continue;
306 }
307 if (LocaleCompare(option,")") == 0)
308 {
309 FireImageStack(MagickTrue,MagickTrue,MagickTrue);
310 if (k == 0)
311 ThrowCompareException(OptionError,"UnableToParseExpression",option);
312 PopImageStack();
313 continue;
314 }
315 if (IsMagickOption(option) == MagickFalse)
316 {
317 Image
318 *images;
319
320 /*
321 Read input image.
322 */
323 FireImageStack(MagickFalse,MagickFalse,pend);
324 filename=argv[i];
325 if ((LocaleCompare(filename,"--") == 0) && (i < (argc-1)))
326 filename=argv[++i];
327 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
328 images=ReadImages(image_info,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 long
344 type;
345
346 if (*option == '+')
347 break;
348 i++;
349 if (i == (long) argc)
350 ThrowCompareException(OptionError,"MissingArgument",option);
351 type=ParseMagickOption(MagickAlphaOptions,MagickFalse,argv[i]);
352 if (type < 0)
353 ThrowCompareException(OptionError,"UnrecognizedAlphaChannelType",
354 argv[i]);
355 break;
356 }
357 if (LocaleCompare("authenticate",option+1) == 0)
358 {
359 if (*option == '+')
360 break;
361 i++;
362 if (i == (long) 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 == (long) 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 long
384 channel;
385
386 if (*option == '+')
387 break;
388 i++;
389 if (i == (long) (argc-1))
390 ThrowCompareException(OptionError,"MissingArgument",option);
391 channel=ParseChannelOption(argv[i]);
392 if (channel < 0)
393 ThrowCompareException(OptionError,"UnrecognizedChannelType",
394 argv[i]);
395 channels=(ChannelType) channel;
396 break;
397 }
398 if (LocaleCompare("colorspace",option+1) == 0)
399 {
400 long
401 colorspace;
402
403 if (*option == '+')
404 break;
405 i++;
406 if (i == (long) (argc-1))
407 ThrowCompareException(OptionError,"MissingArgument",option);
408 colorspace=ParseMagickOption(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 long
418 compose;
419
420 if (*option == '+')
421 break;
422 i++;
423 if (i == (long) argc)
424 ThrowCompareException(OptionError,"MissingArgument",option);
425 compose=ParseMagickOption(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 long
435 compress;
436
437 if (*option == '+')
438 break;
439 i++;
440 if (i == (long) (argc-1))
441 ThrowCompareException(OptionError,"MissingArgument",option);
442 compress=ParseMagickOption(MagickCompressOptions,MagickFalse,
443 argv[i]);
444 if (compress < 0)
445 ThrowCompareException(OptionError,"UnrecognizedImageCompression",
446 argv[i]);
447 break;
448 }
cristy22879752009-10-25 23:55:40 +0000449 if (LocaleCompare("concurrent",option+1) == 0)
450 break;
cristy3ed852e2009-09-05 21:47:34 +0000451 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 == (long) 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 == (long) (argc-1))
477 ThrowCompareException(OptionError,"MissingArgument",option);
478 break;
479 }
480 if (LocaleCompare("define",option+1) == 0)
481 {
482 i++;
483 if (i == (long) 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 == (long) 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 == (long) 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 == (long) 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=atof(argv[i]);
532 break;
533 }
cristy22879752009-10-25 23:55:40 +0000534 if (LocaleCompare("duration",option+1) == 0)
535 {
536 if (*option == '+')
537 break;
538 i++;
539 if (i == (long) (argc-1))
540 ThrowCompareException(OptionError,"MissingArgument",option);
541 if (IsGeometry(argv[i]) == MagickFalse)
542 ThrowCompareInvalidArgumentException(option,argv[i]);
543 break;
544 }
cristy3ed852e2009-09-05 21:47:34 +0000545 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 == (long) (argc-1))
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 == (long) (argc-1))
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 == (long) 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 == (long) (argc-1))
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 == (long) (argc-1))
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 long
619 interlace;
620
621 if (*option == '+')
622 break;
623 i++;
624 if (i == (long) argc)
625 ThrowCompareException(OptionError,"MissingArgument",option);
626 interlace=ParseMagickOption(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 long
646 resource;
647
648 if (*option == '+')
649 break;
650 i++;
651 if (i == (long) argc)
652 ThrowCompareException(OptionError,"MissingArgument",option);
653 resource=ParseMagickOption(MagickResourceOptions,MagickFalse,
654 argv[i]);
655 if (resource < 0)
656 ThrowCompareException(OptionError,"UnrecognizedResourceType",
657 argv[i]);
658 i++;
659 if (i == (long) argc)
660 ThrowCompareException(OptionError,"MissingArgument",option);
661 value=strtod(argv[i],&p);
662 if ((p == argv[i]) && (LocaleCompare("unlimited",argv[i]) != 0))
663 ThrowCompareInvalidArgumentException(option,argv[i]);
664 break;
665 }
666 if (LocaleCompare("list",option+1) == 0)
667 {
668 long
669 list;
670
671 if (*option == '+')
672 break;
673 i++;
674 if (i == (long) argc)
675 ThrowCompareException(OptionError,"MissingArgument",option);
676 list=ParseMagickOption(MagickListOptions,MagickFalse,argv[i]);
677 if (list < 0)
678 ThrowCompareException(OptionError,"UnrecognizedListType",argv[i]);
679 (void) MogrifyImageInfo(image_info,(int) (i-j+1),(const char **)
680 argv+j,exception);
681 DestroyCompare();
682 return(MagickTrue);
683 }
684 if (LocaleCompare("log",option+1) == 0)
685 {
686 if (*option == '+')
687 break;
688 i++;
689 if ((i == (long) argc) || (strchr(argv[i],'%') == (char *) NULL))
690 ThrowCompareException(OptionError,"MissingArgument",option);
691 break;
692 }
693 if (LocaleCompare("lowlight-color",option+1) == 0)
694 {
695 if (*option == '+')
696 break;
697 i++;
698 if (i == (long) (argc-1))
699 ThrowCompareException(OptionError,"MissingArgument",option);
700 break;
701 }
702 ThrowCompareException(OptionError,"UnrecognizedOption",option)
703 }
704 case 'm':
705 {
706 if (LocaleCompare("matte",option+1) == 0)
707 break;
708 if (LocaleCompare("metric",option+1) == 0)
709 {
710 long
711 type;
712
713 if (*option == '+')
714 break;
715 i++;
716 if (i == (long) argc)
717 ThrowCompareException(OptionError,"MissingArgument",option);
718 type=ParseMagickOption(MagickMetricOptions,MagickTrue,argv[i]);
719 if (type < 0)
720 ThrowCompareException(OptionError,"UnrecognizedMetricType",
721 argv[i]);
722 metric=(MetricType) type;
723 break;
724 }
725 if (LocaleCompare("monitor",option+1) == 0)
726 break;
727 ThrowCompareException(OptionError,"UnrecognizedOption",option)
728 }
729 case 'p':
730 {
731 if (LocaleCompare("passphrase",option+1) == 0)
732 {
733 if (*option == '+')
734 break;
735 i++;
736 if (i == (long) argc)
737 ThrowCompareException(OptionError,"MissingArgument",option);
738 break;
739 }
740 if (LocaleCompare("profile",option+1) == 0)
741 {
742 i++;
743 if (i == (long) (argc-1))
744 ThrowCompareException(OptionError,"MissingArgument",option);
745 break;
746 }
747 ThrowCompareException(OptionError,"UnrecognizedOption",option)
748 }
749 case 'q':
750 {
751 if (LocaleCompare("quality",option+1) == 0)
752 {
753 if (*option == '+')
754 break;
755 i++;
756 if (i == (long) (argc-1))
757 ThrowCompareException(OptionError,"MissingArgument",option);
758 if (IsGeometry(argv[i]) == MagickFalse)
759 ThrowCompareInvalidArgumentException(option,argv[i]);
760 break;
761 }
762 if (LocaleCompare("quantize",option+1) == 0)
763 {
764 long
765 colorspace;
766
767 if (*option == '+')
768 break;
769 i++;
770 if (i == (long) (argc-1))
771 ThrowCompareException(OptionError,"MissingArgument",option);
772 colorspace=ParseMagickOption(MagickColorspaceOptions,
773 MagickFalse,argv[i]);
774 if (colorspace < 0)
775 ThrowCompareException(OptionError,"UnrecognizedColorspace",
776 argv[i]);
777 break;
778 }
779 if (LocaleCompare("quiet",option+1) == 0)
780 break;
781 ThrowCompareException(OptionError,"UnrecognizedOption",option)
782 }
783 case 'r':
784 {
785 if (LocaleCompare("regard-warnings",option+1) == 0)
786 break;
787 if (LocaleNCompare("respect-parentheses",option+1,17) == 0)
788 {
789 respect_parenthesis=(*option == '-') ? MagickTrue : MagickFalse;
790 break;
791 }
792 ThrowCompareException(OptionError,"UnrecognizedOption",option)
793 }
794 case 's':
795 {
796 if (LocaleCompare("sampling-factor",option+1) == 0)
797 {
798 if (*option == '+')
799 break;
800 i++;
801 if (i == (long) argc)
802 ThrowCompareException(OptionError,"MissingArgument",option);
803 if (IsGeometry(argv[i]) == MagickFalse)
804 ThrowCompareInvalidArgumentException(option,argv[i]);
805 break;
806 }
807 if (LocaleCompare("seed",option+1) == 0)
808 {
809 if (*option == '+')
810 break;
811 i++;
812 if (i == (long) (argc-1))
813 ThrowCompareException(OptionError,"MissingArgument",option);
814 if (IsGeometry(argv[i]) == MagickFalse)
815 ThrowCompareInvalidArgumentException(option,argv[i]);
816 break;
817 }
818 if (LocaleCompare("set",option+1) == 0)
819 {
820 i++;
821 if (i == (long) argc)
822 ThrowCompareException(OptionError,"MissingArgument",option);
823 if (*option == '+')
824 break;
825 i++;
826 if (i == (long) argc)
827 ThrowCompareException(OptionError,"MissingArgument",option);
828 break;
829 }
830 if (LocaleCompare("size",option+1) == 0)
831 {
832 if (*option == '+')
833 break;
834 i++;
835 if (i == (long) argc)
836 ThrowCompareException(OptionError,"MissingArgument",option);
837 if (IsGeometry(argv[i]) == MagickFalse)
838 ThrowCompareInvalidArgumentException(option,argv[i]);
839 break;
840 }
841 ThrowCompareException(OptionError,"UnrecognizedOption",option)
842 }
843 case 't':
844 {
845 if (LocaleCompare("transparent-color",option+1) == 0)
846 {
847 if (*option == '+')
848 break;
849 i++;
850 if (i == (long) (argc-1))
851 ThrowCompareException(OptionError,"MissingArgument",option);
852 break;
853 }
854 if (LocaleCompare("type",option+1) == 0)
855 {
856 long
857 type;
858
859 if (*option == '+')
860 break;
861 i++;
862 if (i == (long) argc)
863 ThrowCompareException(OptionError,"MissingArgument",option);
864 type=ParseMagickOption(MagickTypeOptions,MagickFalse,argv[i]);
865 if (type < 0)
866 ThrowCompareException(OptionError,"UnrecognizedImageType",
867 argv[i]);
868 break;
869 }
870 ThrowCompareException(OptionError,"UnrecognizedOption",option)
871 }
872 case 'v':
873 {
874 if (LocaleCompare("verbose",option+1) == 0)
875 break;
876 if ((LocaleCompare("version",option+1) == 0) ||
877 (LocaleCompare("-version",option+1) == 0))
878 {
879 (void) fprintf(stdout,"Version: %s\n",
880 GetMagickVersion((unsigned long *) NULL));
cristy3980b0d2009-10-25 14:37:13 +0000881 (void) fprintf(stdout,"Copyright: %s\n",GetMagickCopyright());
cristy104cea82009-10-25 02:26:51 +0000882 (void) fprintf(stdout,"Features: %s\n\n",GetMagickFeatures());
cristy3ed852e2009-09-05 21:47:34 +0000883 break;
884 }
885 if (LocaleCompare("virtual-pixel",option+1) == 0)
886 {
887 long
888 method;
889
890 if (*option == '+')
891 break;
892 i++;
893 if (i == (long) (argc-1))
894 ThrowCompareException(OptionError,"MissingArgument",option);
895 method=ParseMagickOption(MagickVirtualPixelOptions,MagickFalse,
896 argv[i]);
897 if (method < 0)
898 ThrowCompareException(OptionError,
899 "UnrecognizedVirtualPixelMethod",argv[i]);
900 break;
901 }
902 ThrowCompareException(OptionError,"UnrecognizedOption",option)
903 }
904 case '?':
905 break;
906 default:
907 ThrowCompareException(OptionError,"UnrecognizedOption",option)
908 }
909 fire=ParseMagickOption(MagickImageListOptions,MagickFalse,option+1) < 0 ?
910 MagickFalse : MagickTrue;
911 if (fire != MagickFalse)
912 FireImageStack(MagickTrue,MagickTrue,MagickTrue);
913 }
914 if (k != 0)
915 ThrowCompareException(OptionError,"UnbalancedParenthesis",argv[i]);
916 if (i-- != (long) (argc-1))
917 ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
918 if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
919 ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
920 FinalizeImageSettings(image_info,image,MagickTrue);
cristy94f20072009-09-12 02:12:36 +0000921 if ((image == (Image *) NULL) || (GetImageListLength(image) < 2))
922 ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]);
cristy3ed852e2009-09-05 21:47:34 +0000923 image=GetImageFromList(image,0);
924 reconstruct_image=GetImageFromList(image,1);
925 similarity_image=SimilarityImage(image,reconstruct_image,&offset,
926 &similarity_metric,exception);
927 if (similarity_metric > dissimilarity_threshold)
928 ThrowCompareException(ImageError,"ImagesTooDissimilar",image->filename);
929 if ((reconstruct_image->columns == image->columns) &&
930 (reconstruct_image->rows == image->rows))
931 difference_image=CompareImageChannels(image,reconstruct_image,channels,
932 metric,&distortion,exception);
933 else
934 if (similarity_image != (Image *) NULL)
935 {
936 Image
937 *composite_image;
938
939 /*
940 Determine if reconstructed image is a subimage of the image.
941 */
942 composite_image=CloneImage(image,0,0,MagickTrue,exception);
943 if (composite_image == (Image *) NULL)
944 difference_image=CompareImageChannels(image,reconstruct_image,
945 channels,metric,&distortion,exception);
946 else
947 {
948 (void) CompositeImage(composite_image,CopyCompositeOp,
949 reconstruct_image,offset.x,offset.y);
950 difference_image=CompareImageChannels(image,composite_image,
951 channels,metric,&distortion,exception);
952 if (difference_image != (Image *) NULL)
953 {
954 difference_image->page.x=offset.x;
955 difference_image->page.y=offset.y;
956 }
957 composite_image=DestroyImage(composite_image);
958 }
959 if (difference_image == (Image *) NULL)
960 similarity_image=DestroyImage(similarity_image);
961 else
962 {
963 AppendImageToList(&difference_image,similarity_image);
964 similarity_image=(Image *) NULL;
965 }
966 }
967 if (difference_image == (Image *) NULL)
968 status=0;
969 else
970 {
971 if (image_info->verbose != MagickFalse)
972 (void) IsImagesEqual(image,reconstruct_image);
973 if (*difference_image->magick == '\0')
974 (void) CopyMagickString(difference_image->magick,image->magick,
975 MaxTextExtent);
976 if (image_info->verbose == MagickFalse)
977 {
978 switch (metric)
979 {
980 case MeanAbsoluteErrorMetric:
981 case MeanSquaredErrorMetric:
982 case RootMeanSquaredErrorMetric:
983 case PeakAbsoluteErrorMetric:
984 {
985 (void) fprintf(stderr,"%g (%g)",QuantumRange*distortion,(double)
986 distortion);
987 if ((reconstruct_image->columns != image->columns) ||
988 (reconstruct_image->rows != image->rows))
989 (void) fprintf(stderr," @ %ld,%ld",difference_image->page.x,
990 difference_image->page.y);
991 (void) fprintf(stderr,"\n");
992 break;
993 }
994 case AbsoluteErrorMetric:
995 case PeakSignalToNoiseRatioMetric:
996 {
997 (void) fprintf(stderr,"%g",distortion);
998 if ((reconstruct_image->columns != image->columns) ||
999 (reconstruct_image->rows != image->rows))
1000 (void) fprintf(stderr," @ %ld,%ld",difference_image->page.x,
1001 difference_image->page.y);
1002 (void) fprintf(stderr,"\n");
1003 break;
1004 }
1005 case MeanErrorPerPixelMetric:
1006 {
1007 (void) fprintf(stderr,"%g (%g, %g)",distortion,
1008 image->error.normalized_mean_error,
1009 image->error.normalized_maximum_error);
1010 if ((reconstruct_image->columns != image->columns) ||
1011 (reconstruct_image->rows != image->rows))
1012 (void) fprintf(stderr," @ %ld,%ld",difference_image->page.x,
1013 difference_image->page.y);
1014 (void) fprintf(stderr,"\n");
1015 break;
1016 }
1017 case UndefinedMetric:
1018 break;
1019 }
1020 }
1021 else
1022 {
1023 double
1024 *channel_distortion;
1025
1026 channel_distortion=GetImageChannelDistortions(image,reconstruct_image,
1027 metric,&image->exception);
1028 (void) fprintf(stderr,"Image: %s\n",image->filename);
1029 if ((reconstruct_image->columns != image->columns) ||
1030 (reconstruct_image->rows != image->rows))
1031 (void) fprintf(stderr,"Offset: %ld,%ld\n",difference_image->page.x,
1032 difference_image->page.y);
1033 (void) fprintf(stderr," Channel distortion: %s\n",
1034 MagickOptionToMnemonic(MagickMetricOptions,(long) metric));
1035 switch (metric)
1036 {
1037 case MeanAbsoluteErrorMetric:
1038 case MeanSquaredErrorMetric:
1039 case RootMeanSquaredErrorMetric:
1040 case PeakAbsoluteErrorMetric:
1041 {
1042 switch (image->colorspace)
1043 {
1044 case RGBColorspace:
1045 default:
1046 {
1047 (void) fprintf(stderr," red: %g (%g)\n",
1048 QuantumRange*channel_distortion[RedChannel],
1049 channel_distortion[RedChannel]);
1050 (void) fprintf(stderr," green: %g (%g)\n",
1051 QuantumRange*channel_distortion[GreenChannel],
1052 channel_distortion[GreenChannel]);
1053 (void) fprintf(stderr," blue: %g (%g)\n",
1054 QuantumRange*channel_distortion[BlueChannel],
1055 channel_distortion[BlueChannel]);
1056 if (image->matte != MagickFalse)
1057 (void) fprintf(stderr," alpha: %g (%g)\n",
1058 QuantumRange*channel_distortion[OpacityChannel],
1059 channel_distortion[OpacityChannel]);
1060 break;
1061 }
1062 case CMYKColorspace:
1063 {
1064 (void) fprintf(stderr," cyan: %g (%g)\n",
1065 QuantumRange*channel_distortion[CyanChannel],
1066 channel_distortion[CyanChannel]);
1067 (void) fprintf(stderr," magenta: %g (%g)\n",
1068 QuantumRange*channel_distortion[MagentaChannel],
1069 channel_distortion[MagentaChannel]);
1070 (void) fprintf(stderr," yellow: %g (%g)\n",
1071 QuantumRange*channel_distortion[YellowChannel],
1072 channel_distortion[YellowChannel]);
1073 (void) fprintf(stderr," black: %g (%g)\n",
1074 QuantumRange*channel_distortion[BlackChannel],
1075 channel_distortion[BlackChannel]);
1076 if (image->matte != MagickFalse)
1077 (void) fprintf(stderr," alpha: %g (%g)\n",
1078 QuantumRange*channel_distortion[OpacityChannel],
1079 channel_distortion[OpacityChannel]);
1080 break;
1081 }
1082 case GRAYColorspace:
1083 {
1084 (void) fprintf(stderr," gray: %g (%g)\n",
1085 QuantumRange*channel_distortion[GrayChannel],
1086 channel_distortion[GrayChannel]);
1087 if (image->matte != MagickFalse)
1088 (void) fprintf(stderr," alpha: %g (%g)\n",
1089 QuantumRange*channel_distortion[OpacityChannel],
1090 channel_distortion[OpacityChannel]);
1091 break;
1092 }
1093 }
1094 (void) fprintf(stderr," all: %g (%g)\n",
1095 QuantumRange*channel_distortion[AllChannels],
1096 channel_distortion[AllChannels]);
1097 break;
1098 }
1099 case AbsoluteErrorMetric:
1100 case PeakSignalToNoiseRatioMetric:
1101 {
1102 switch (image->colorspace)
1103 {
1104 case RGBColorspace:
1105 default:
1106 {
1107 (void) fprintf(stderr," red: %g\n",
1108 channel_distortion[RedChannel]);
1109 (void) fprintf(stderr," green: %g\n",
1110 channel_distortion[GreenChannel]);
1111 (void) fprintf(stderr," blue: %g\n",
1112 channel_distortion[BlueChannel]);
1113 if (image->matte != MagickFalse)
1114 (void) fprintf(stderr," alpha: %g\n",
1115 channel_distortion[OpacityChannel]);
1116 break;
1117 }
1118 case CMYKColorspace:
1119 {
1120 (void) fprintf(stderr," cyan: %g\n",
1121 channel_distortion[CyanChannel]);
1122 (void) fprintf(stderr," magenta: %g\n",
1123 channel_distortion[MagentaChannel]);
1124 (void) fprintf(stderr," yellow: %g\n",
1125 channel_distortion[YellowChannel]);
1126 (void) fprintf(stderr," black: %g\n",
1127 channel_distortion[BlackChannel]);
1128 if (image->matte != MagickFalse)
1129 (void) fprintf(stderr," alpha: %g\n",
1130 channel_distortion[OpacityChannel]);
1131 break;
1132 }
1133 case GRAYColorspace:
1134 {
1135 (void) fprintf(stderr," gray: %g\n",
1136 channel_distortion[GrayChannel]);
1137 if (image->matte != MagickFalse)
1138 (void) fprintf(stderr," alpha: %g\n",
1139 channel_distortion[OpacityChannel]);
1140 break;
1141 }
1142 }
1143 (void) fprintf(stderr," all: %g\n",
1144 channel_distortion[AllChannels]);
1145 break;
1146 }
1147 case MeanErrorPerPixelMetric:
1148 {
1149 (void) fprintf(stderr," %g (%g, %g)\n",
1150 channel_distortion[AllChannels],
1151 image->error.normalized_mean_error,
1152 image->error.normalized_maximum_error);
1153 break;
1154 }
1155 case UndefinedMetric:
1156 break;
1157 }
1158 channel_distortion=(double *) RelinquishMagickMemory(
1159 channel_distortion);
1160 }
1161 status&=WriteImages(image_info,difference_image,argv[argc-1],exception);
1162 if ((metadata != (char **) NULL) && (format != (char *) NULL))
1163 {
1164 char
1165 *text;
1166
1167 text=InterpretImageProperties(image_info,difference_image,format);
1168 if (text == (char *) NULL)
1169 ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed",
1170 GetExceptionMessage(errno));
1171 (void) ConcatenateString(&(*metadata),text);
1172 (void) ConcatenateString(&(*metadata),"\n");
1173 text=DestroyString(text);
1174 }
1175 difference_image=DestroyImageList(difference_image);
1176 }
1177 DestroyCompare();
1178 return(status != 0 ? MagickTrue : MagickFalse);
1179}