Powered by Pair ImageMagick logo
Image Magick
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Data Structures | File List | Namespace Members | Data Fields | Globals | Related Pages

ImageMagick-6.1.1/magick/compare.c

Go to the documentation of this file.
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % CCCC OOO M M PPPP AAA RRRR EEEEE % 00007 % C O O MM MM P P A A R R E % 00008 % C O O M M M PPPP AAAAA RRRR EEE % 00009 % C O O M M P A A R R E % 00010 % CCCC OOO M M P A A R R EEEEE % 00011 % % 00012 % % 00013 % Image Comparison Methods % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % December 2003 % 00018 % % 00019 % % 00020 % Copyright 1999-2004 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/www/Copyright.html % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % 00037 % 00038 */ 00039 00040 /* 00041 Include declarations. 00042 */ 00043 #include "magick/studio.h" 00044 #include "magick/client.h" 00045 #include "magick/color.h" 00046 #include "magick/color_private.h" 00047 #include "magick/colorspace.h" 00048 #include "magick/colorspace_private.h" 00049 #include "magick/compare.h" 00050 #include "magick/composite_private.h" 00051 #include "magick/constitute.h" 00052 #include "magick/geometry.h" 00053 #include "magick/image_private.h" 00054 #include "magick/list.h" 00055 #include "magick/log.h" 00056 #include "magick/memory_.h" 00057 #include "magick/mogrify.h" 00058 #include "magick/option.h" 00059 #include "magick/resource_.h" 00060 #include "magick/string_.h" 00061 #include "magick/utility.h" 00062 #include "magick/version.h" 00063 00064 /* 00065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00066 % % 00067 % % 00068 % % 00069 % C o m p a r e I m a g e C h a n n e l s % 00070 % % 00071 % % 00072 % % 00073 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00074 % 00075 % CompareImageChannels() compares one or more image channels and returns 00076 % the specified distortion metric. 00077 % 00078 % The format of the CompareImageChannels method is: 00079 % 00080 % Image *CompareImageChannels(const Image *image, 00081 % const Image *reconstruct_image,const ChannelType channel, 00082 % const MetricType metric,double *distortion,ExceptionInfo *exception) 00083 % 00084 % A description of each parameter follows: 00085 % 00086 % o image: The image. 00087 % 00088 % o reconstruct_image: The reconstruct image. 00089 % 00090 % o channel: The channel. 00091 % 00092 % o metric: The metric. 00093 % 00094 % o distortion: The computed distortion between the images. 00095 % 00096 % o exception: Return any errors or warnings in this structure. 00097 % 00098 % 00099 */ 00100 00101 MagickExport Image *CompareImages(Image *image,const Image *reconstruct_image, 00102 const MetricType metric,double *distortion,ExceptionInfo *exception) 00103 { 00104 Image 00105 *difference_image; 00106 00107 difference_image=CompareImageChannels(image,reconstruct_image,AllChannels, 00108 metric,distortion,exception); 00109 return(difference_image); 00110 } 00111 00112 static inline void CompositeOver(const MagickPixelPacket *p, 00113 const MagickRealType alpha,const MagickPixelPacket *q, 00114 const MagickRealType beta,MagickPixelPacket *composite) 00115 { 00116 MagickRealType 00117 gamma; 00118 00119 gamma=1.0-QuantumScale*QuantumScale*alpha*beta; 00120 composite->opacity=MaxRGB*(1.0-gamma); 00121 gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma); 00122 composite->red=gamma*MagickOver_(p->red,alpha,q->red,beta); 00123 composite->green=gamma*MagickOver_(p->green,alpha,q->green,beta); 00124 composite->blue=gamma*MagickOver_(p->blue,alpha,q->blue,beta); 00125 if (q->colorspace == CMYKColorspace) 00126 composite->index=gamma*MagickOver_(p->index,alpha,q->index,beta); 00127 } 00128 00129 static MagickRealType GetMeanAbsoluteError(const Image *image, 00130 const Image *reconstruct_image,const ChannelType channel, 00131 ExceptionInfo *exception) 00132 { 00133 IndexPacket 00134 *indexes, 00135 *reconstruct_indexes; 00136 00137 long 00138 y; 00139 00140 MagickRealType 00141 area, 00142 distortion; 00143 00144 register const PixelPacket 00145 *p, 00146 *q; 00147 00148 register long 00149 x; 00150 00151 area=0.0; 00152 distortion=0.0; 00153 for (y=0; y < (long) image->rows; y++) 00154 { 00155 p=AcquireImagePixels(image,0,y,image->columns,1,exception); 00156 q=AcquireImagePixels(reconstruct_image,0,y,reconstruct_image->columns,1, 00157 exception); 00158 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL)) 00159 break; 00160 indexes=GetIndexes(image); 00161 reconstruct_indexes=GetIndexes(reconstruct_image); 00162 for (x=0; x < (long) image->columns; x++) 00163 { 00164 if ((channel & RedChannel) != 0) 00165 { 00166 distortion+=fabs(p->red-(MagickRealType) q->red); 00167 area++; 00168 } 00169 if ((channel & GreenChannel) != 0) 00170 { 00171 distortion+=fabs(p->green-(MagickRealType) q->green); 00172 area++; 00173 } 00174 if ((channel & BlueChannel) != 0) 00175 { 00176 distortion+=fabs(p->blue-(MagickRealType) q->blue); 00177 area++; 00178 } 00179 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse)) 00180 { 00181 distortion+=fabs(p->opacity-(MagickRealType) q->opacity); 00182 area++; 00183 } 00184 if (((channel & IndexChannel) != 0) && 00185 (image->colorspace == CMYKColorspace)) 00186 { 00187 distortion+=fabs(indexes[x]-(MagickRealType) reconstruct_indexes[x]); 00188 area++; 00189 } 00190 p++; 00191 q++; 00192 } 00193 } 00194 return(distortion/area); 00195 } 00196 00197 static MagickRealType GetMeanSquaredError(const Image *image, 00198 const Image *reconstruct_image,const ChannelType channel, 00199 ExceptionInfo *exception) 00200 { 00201 IndexPacket 00202 *indexes, 00203 *reconstruct_indexes; 00204 00205 long 00206 y; 00207 00208 MagickRealType 00209 area, 00210 distance, 00211 distortion; 00212 00213 register const PixelPacket 00214 *p, 00215 *q; 00216 00217 register long 00218 x; 00219 00220 area=0.0; 00221 distortion=0.0; 00222 for (y=0; y < (long) image->rows; y++) 00223 { 00224 p=AcquireImagePixels(image,0,y,image->columns,1,exception); 00225 q=AcquireImagePixels(reconstruct_image,0,y,reconstruct_image->columns,1, 00226 exception); 00227 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL)) 00228 break; 00229 indexes=GetIndexes(image); 00230 reconstruct_indexes=GetIndexes(reconstruct_image); 00231 for (x=0; x < (long) image->columns; x++) 00232 { 00233 if ((channel & RedChannel) != 0) 00234 { 00235 distance=p->red-(MagickRealType) q->red; 00236 distortion+=distance*distance; 00237 area++; 00238 } 00239 if ((channel & GreenChannel) != 0) 00240 { 00241 distance=p->green-(MagickRealType) q->green; 00242 distortion+=distance*distance; 00243 area++; 00244 } 00245 if ((channel & BlueChannel) != 0) 00246 { 00247 distance=p->blue-(MagickRealType) q->blue; 00248 distortion+=distance*distance; 00249 area++; 00250 } 00251 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse)) 00252 { 00253 distance=p->opacity-(MagickRealType) q->opacity; 00254 distortion+=distance*distance; 00255 area++; 00256 } 00257 if (((channel & IndexChannel) != 0) && 00258 (image->colorspace == CMYKColorspace)) 00259 { 00260 distance=indexes[x]-(MagickRealType) reconstruct_indexes[x]; 00261 distortion+=distance*distance; 00262 area++; 00263 } 00264 p++; 00265 q++; 00266 } 00267 } 00268 return(distortion/area); 00269 } 00270 00271 static MagickRealType GetPeakAbsoluteError(const Image *image, 00272 const Image *reconstruct_image,const ChannelType channel, 00273 ExceptionInfo *exception) 00274 { 00275 IndexPacket 00276 *indexes, 00277 *reconstruct_indexes; 00278 00279 long 00280 y; 00281 00282 MagickRealType 00283 distance, 00284 distortion; 00285 00286 register const PixelPacket 00287 *p, 00288 *q; 00289 00290 register long 00291 x; 00292 00293 distortion=0.0; 00294 for (y=0; y < (long) image->rows; y++) 00295 { 00296 p=AcquireImagePixels(image,0,y,image->columns,1,exception); 00297 q=AcquireImagePixels(reconstruct_image,0,y,reconstruct_image->columns,1, 00298 exception); 00299 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL)) 00300 break; 00301 indexes=GetIndexes(image); 00302 reconstruct_indexes=GetIndexes(reconstruct_image); 00303 for (x=0; x < (long) image->columns; x++) 00304 { 00305 if ((channel & RedChannel) != 0) 00306 { 00307 distance=fabs(p->red-(MagickRealType) q->red); 00308 if (distance > distortion) 00309 distortion=distance; 00310 } 00311 if ((channel & GreenChannel) != 0) 00312 { 00313 distance=fabs(p->green-(MagickRealType) q->green); 00314 if (distance > distortion) 00315 distortion=distance; 00316 } 00317 if ((channel & BlueChannel) != 0) 00318 { 00319 distance=fabs(p->blue-(MagickRealType) q->blue); 00320 if (distance > distortion) 00321 distortion=distance; 00322 } 00323 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse)) 00324 { 00325 distance=fabs(p->opacity-(MagickRealType) q->opacity); 00326 if (distance > distortion) 00327 distortion=distance; 00328 } 00329 if (((channel & IndexChannel) != 0) && 00330 (image->colorspace == CMYKColorspace)) 00331 { 00332 distance= 00333 fabs(indexes[x]-(MagickRealType) reconstruct_indexes[x]); 00334 if (distance > distortion) 00335 distortion=distance; 00336 } 00337 p++; 00338 q++; 00339 } 00340 } 00341 return(distortion); 00342 } 00343 00344 static MagickRealType GetPeakSignalToNoiseRatio(const Image *image, 00345 const Image *reconstruct_image,const ChannelType channel, 00346 ExceptionInfo *exception) 00347 { 00348 MagickRealType 00349 distortion; 00350 00351 distortion=GetMeanSquaredError(image,reconstruct_image,channel,exception); 00352 return(20.0*log10(MaxRGB/sqrt(distortion))); 00353 } 00354 00355 static MagickRealType GetRootMeanSquaredError(const Image *image, 00356 const Image *reconstruct_image,const ChannelType channel, 00357 ExceptionInfo *exception) 00358 { 00359 MagickRealType 00360 distortion; 00361 00362 distortion=GetMeanSquaredError(image,reconstruct_image,channel,exception); 00363 return(sqrt(distortion)); 00364 } 00365 00366 MagickExport Image *CompareImageChannels(Image *image, 00367 const Image *reconstruct_image,const ChannelType channel, 00368 const MetricType metric,double *distortion,ExceptionInfo *exception) 00369 { 00370 Image 00371 *difference_image; 00372 00373 long 00374 y; 00375 00376 MagickPixelPacket 00377 composite, 00378 red, 00379 source, 00380 white; 00381 00382 MagickRealType 00383 alpha, 00384 beta; 00385 00386 register const PixelPacket 00387 *p, 00388 *q; 00389 00390 register IndexPacket 00391 *difference_indexes, 00392 *indexes, 00393 *reconstruct_indexes; 00394 00395 register long 00396 x; 00397 00398 register PixelPacket 00399 *r; 00400 00401 assert(image != (Image *) NULL); 00402 assert(image->signature == MagickSignature); 00403 if (image->debug != MagickFalse) 00404 (void) LogMagickEvent(TraceEvent,GetMagickModule(),image->filename); 00405 assert(reconstruct_image != (const Image *) NULL); 00406 assert(reconstruct_image->signature == MagickSignature); 00407 assert(distortion != (double *) NULL); 00408 if (image->debug != MagickFalse) 00409 (void) LogMagickEvent(TraceEvent,GetMagickModule(),image->filename); 00410 if ((reconstruct_image->columns != image->columns) || 00411 (reconstruct_image->rows != image->rows)) 00412 ThrowImageException(ImageError,"ImageSizeDiffers"); 00413 if (image->colorspace != reconstruct_image->colorspace) 00414 ThrowImageException(ImageError,"ImageColorspaceDiffers"); 00415 if (image->matte != reconstruct_image->matte) 00416 ThrowImageException(ImageError,"ImageOpacityDiffers"); 00417 difference_image=CloneImage(image,image->columns,image->rows,MagickTrue, 00418 exception); 00419 if (difference_image == (Image *) NULL) 00420 return((Image *) NULL); 00421 difference_image->storage_class=DirectClass; 00422 (void) QueryMagickColor("#f1001e00",&red,exception); 00423 (void) QueryMagickColor("#ffffff00",&white,exception); 00424 if (difference_image->colorspace == CMYKColorspace) 00425 { 00426 RGBtoCMYK(&red); 00427 RGBtoCMYK(&white); 00428 } 00429 /* 00430 Get image distortion. 00431 */ 00432 *distortion=0.0; 00433 switch (metric) 00434 { 00435 case MeanAbsoluteErrorMetric: 00436 { 00437 *distortion=GetMeanAbsoluteError(image,reconstruct_image,channel, 00438 exception); 00439 break; 00440 } 00441 case MeanSquaredErrorMetric: 00442 { 00443 *distortion=GetMeanSquaredError(image,reconstruct_image,channel, 00444 exception); 00445 break; 00446 } 00447 case PeakAbsoluteErrorMetric: 00448 default: 00449 { 00450 *distortion=GetPeakAbsoluteError(image,reconstruct_image,channel, 00451 exception); 00452 break; 00453 } 00454 case PeakSignalToNoiseRatioMetric: 00455 { 00456 *distortion=GetPeakSignalToNoiseRatio(image,reconstruct_image,channel, 00457 exception); 00458 break; 00459 } 00460 case RootMeanSquaredErrorMetric: 00461 { 00462 *distortion=GetRootMeanSquaredError(image,reconstruct_image,channel, 00463 exception); 00464 break; 00465 } 00466 } 00467 /* 00468 Generate difference image. 00469 */ 00470 GetMagickPixelPacket(reconstruct_image,&source); 00471 GetMagickPixelPacket(difference_image,&composite); 00472 for (y=0; y < (long) image->rows; y++) 00473 { 00474 p=AcquireImagePixels(image,0,y,image->columns,1,exception); 00475 q=AcquireImagePixels(reconstruct_image,0,y,reconstruct_image->columns,1, 00476 exception); 00477 r=SetImagePixels(difference_image,0,y,difference_image->columns,1); 00478 if ((p == (const PixelPacket *) NULL) || 00479 (q == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL)) 00480 break; 00481 indexes=GetIndexes(image); 00482 reconstruct_indexes=GetIndexes(reconstruct_image); 00483 difference_indexes=GetIndexes(difference_image); 00484 for (x=0; x < (long) image->columns; x++) 00485 { 00486 alpha=0.0; 00487 beta=0.0; 00488 if ((channel & RedChannel) != 0) 00489 { 00490 alpha+=p->red; 00491 beta+=q->red; 00492 } 00493 if ((channel & GreenChannel) != 0) 00494 { 00495 alpha+=p->green; 00496 beta+=q->green; 00497 } 00498 if ((channel & BlueChannel) != 0) 00499 { 00500 alpha+=p->blue; 00501 beta+=q->blue; 00502 } 00503 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse)) 00504 { 00505 alpha+=p->opacity; 00506 beta+=q->opacity; 00507 } 00508 if (((channel & IndexChannel) != 0) && 00509 (image->colorspace == CMYKColorspace)) 00510 { 00511 alpha+=indexes[x]; 00512 beta+=reconstruct_indexes[x]; 00513 } 00514 source.red=(MagickRealType) q->red; 00515 source.green=(MagickRealType) q->green; 00516 source.blue=(MagickRealType) q->blue; 00517 if (reconstruct_image->matte != MagickFalse) 00518 source.opacity=(MagickRealType) q->opacity; 00519 if (alpha != beta) 00520 CompositeOver(&source,9.0*MaxRGB/10.0,&red,(double) red.opacity, 00521 &composite); 00522 else 00523 CompositeOver(&source,9.0*MaxRGB/10.0,&white,(double) white.opacity, 00524 &composite); 00525 SetPixelPacket(&composite,r,difference_indexes+x); 00526 p++; 00527 q++; 00528 r++; 00529 } 00530 if (SyncImagePixels(difference_image) == MagickFalse) 00531 break; 00532 } 00533 return(difference_image); 00534 } 00535 00536 /* 00537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00538 % % 00539 % % 00540 % % 00541 % C o m p a r e I m a g e C o m m a n d % 00542 % % 00543 % % 00544 % % 00545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00546 % 00547 % CompareImageCommand() compares two images and returns the difference between 00548 % them as a distortion metric and as a new image visually annotating their 00549 % differences. 00550 % 00551 % The format of the CompareImageCommand method is: 00552 % 00553 % MagickBooleanType CompareImageCommand(ImageInfo *image_info,int argc, 00554 % char **argv,char **metadata,ExceptionInfo *exception) 00555 % 00556 % A description of each parameter follows: 00557 % 00558 % o image_info: The image info. 00559 % 00560 % o argc: The number of elements in the argument vector. 00561 % 00562 % o argv: A text array containing the command line arguments. 00563 % 00564 % o metadata: any metadata is returned here. 00565 % 00566 % o exception: Return any errors or warnings in this structure. 00567 % 00568 % 00569 */ 00570 00571 static void CompareUsage(void) 00572 { 00573 const char 00574 **p; 00575 00576 static const char 00577 *options[]= 00578 { 00579 "-authenticate value decrypt image with this password", 00580 "-channel type Red, Green, Blue, Opacity, Index, Cyan, Yellow, ", 00581 " Magenta, Black, or All", 00582 "-colorspace type alternate image colorspace", 00583 "-compress type image compression type", 00584 "-debug events display copious debugging information", 00585 "-define format:option", 00586 " define one or more image format options", 00587 "-density geometry horizontal and vertical density of the image", 00588 "-depth value image depth", 00589 "-extract geometry extract area from image", 00590 "-help print program options", 00591 "-interlace type None, Line, Plane, or Partition", 00592 "-limit type value Area, Disk, Map, or Memory resource limit", 00593 "-log format format of debugging information", 00594 "-metric type MAE, MSE, PSE, PSNR, or RMSE", 00595 "-quality value JPEG/MIFF/PNG compression level", 00596 "-profile filename add, delete, or apply an image profile", 00597 "-sampling-factor geometry", 00598 " horizontal and vertical sampling factor", 00599 "-size geometry width and height of image", 00600 "-verbose print detailed information about the image", 00601 "-version print version information", 00602 "-virtual-pixel method", 00603 " Constant, Edge, Mirror, or Tile", 00604 (char *) NULL 00605 }; 00606 00607 (void) printf("Version: %s\n",GetMagickVersion((unsigned long *) NULL)); 00608 (void) printf("Copyright: %s\n\n",GetMagickCopyright()); 00609 (void) printf("Usage: %s [options ...] image reconstruct difference\n", 00610 GetClientName()); 00611 (void) printf("\nWhere options include:\n"); 00612 for (p=options; *p != (char *) NULL; p++) 00613 (void) printf(" %s\n",*p); 00614 Exit(0); 00615 } 00616 00617 MagickExport MagickBooleanType CompareImageCommand(ImageInfo *image_info, 00618 int argc,char **argv,char **metadata,ExceptionInfo *exception) 00619 { 00620 #define DestroyCompare() \ 00621 { \ 00622 DestroyImageList(image); \ 00623 for (i=0; i < (long) argc; i++) \ 00624 argv[i]=(char *) RelinquishMagickMemory(argv[i]); \ 00625 argv=(char **) RelinquishMagickMemory(argv); \ 00626 } 00627 #define ThrowCompareException(severity,tag,option) \ 00628 { \ 00629 (void) ThrowMagickException(exception,GetMagickModule(),severity,tag,option);\ 00630 DestroyCompare(); \ 00631 return(MagickFalse); \ 00632 } 00633 #define ThrowCompareInvalidArgumentException(option,argument) \ 00634 { \ 00635 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, \ 00636 "InvalidArgument",argument,option); \ 00637 DestroyCompare(); \ 00638 return(MagickFalse); \ 00639 } 00640 00641 char 00642 *filename, 00643 *option; 00644 00645 ChannelType 00646 channel; 00647 00648 double 00649 distortion; 00650 00651 Image 00652 *difference_image, 00653 *image, 00654 *reconstruct_image; 00655 00656 long 00657 j; 00658 00659 MagickStatusType 00660 status; 00661 00662 MetricType 00663 metric; 00664 00665 register long 00666 i; 00667 00668 /* 00669 Set defaults. 00670 */ 00671 assert(image_info != (ImageInfo *) NULL); 00672 assert(image_info->signature == MagickSignature); 00673 if (image_info->debug != MagickFalse) 00674 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 00675 assert(exception != (ExceptionInfo *) NULL); 00676 if (argc < 2) 00677 CompareUsage(); 00678 channel=AllChannels; 00679 difference_image=(Image *) NULL; 00680 distortion=0.0; 00681 image=(Image *) NULL; 00682 metric=UndefinedMetric; 00683 option=(char *) NULL; 00684 reconstruct_image=(Image *) NULL; 00685 /* 00686 Compare an image. 00687 */ 00688 ReadCommandlLine(argc,&argv); 00689 status=ExpandFilenames(&argc,&argv); 00690 if (status == MagickFalse) 00691 ThrowCompareException(ResourceLimitError,"MemoryAllocationFailed", 00692 strerror(errno)); 00693 j=1; 00694 for (i=1; i < (long) (argc-1); i++) 00695 { 00696 option=argv[i]; 00697 if ((strlen(option) == 1) || ((*option != '-') && (*option != '+'))) 00698 { 00699 /* 00700 Read input images. 00701 */ 00702 filename=argv[i]; 00703 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent); 00704 if (image == (Image *) NULL) 00705 { 00706 image=ReadImage(image_info,exception); 00707 CatchException(exception); 00708 if (image != (Image *) NULL) 00709 { 00710 status&=MogrifyImages(image_info,(int) (i-j),argv+j,&image); 00711 GetImageException(image,exception); 00712 } 00713 j=i+1; 00714 continue; 00715 } 00716 if (reconstruct_image != (Image *) NULL) 00717 ThrowCompareException(OptionError,"InputImagesAlreadySpecified", 00718 filename); 00719 reconstruct_image=ReadImage(image_info,exception); 00720 CatchException(exception); 00721 status&=reconstruct_image != (Image *) NULL; 00722 continue; 00723 } 00724 switch (*(option+1)) 00725 { 00726 case 'c': 00727 { 00728 if (LocaleCompare("cache",option+1) == 0) 00729 { 00730 if (*option == '-') 00731 { 00732 unsigned long 00733 limit; 00734 00735 i++; 00736 if (i == (long) argc) 00737 ThrowCompareException(OptionError,"MissingArgument",option); 00738 if (IsGeometry(argv[i]) == MagickFalse) 00739 ThrowCompareInvalidArgumentException(option,argv[i]); 00740 limit=(~0UL); 00741 if (LocaleCompare("unlimited",argv[i]) != 0) 00742 limit=(unsigned long) atol(argv[i]); 00743 (void) SetMagickResourceLimit(MemoryResource,limit); 00744 (void) SetMagickResourceLimit(MapResource,2*limit); 00745 } 00746 break; 00747 } 00748 if (LocaleCompare("channel",option+1) == 0) 00749 { 00750 if (*option == '-') 00751 { 00752 i++; 00753 if (i == (long) argc) 00754 ThrowCompareException(OptionError,"MissingArgument",option); 00755 channel=(ChannelType) ParseMagickOption(MagickChannelOptions, 00756 MagickTrue,argv[i]); 00757 if (channel <= UndefinedChannel) 00758 ThrowCompareException(OptionError,"UnrecognizedChannelType", 00759 argv[i]); 00760 } 00761 break; 00762 } 00763 if (LocaleCompare("colorspace",option+1) == 0) 00764 { 00765 image_info->colorspace=UndefinedColorspace; 00766 if (*option == '-') 00767 { 00768 long 00769 colorspace; 00770 00771 i++; 00772 if (i == (long) (argc-1)) 00773 ThrowCompareException(OptionError,"MissingArgument",option); 00774 colorspace=ParseMagickOption(MagickColorspaceOptions,MagickFalse, 00775 argv[i]); 00776 if (colorspace < 0) 00777 ThrowCompareException(OptionError,"UnrecognizedColorspace", 00778 argv[i]); 00779 image_info->colorspace=(ColorspaceType) colorspace; 00780 } 00781 break; 00782 } 00783 if (LocaleCompare("compress",option+1) == 0) 00784 { 00785 image_info->compression=UndefinedCompression; 00786 if (*option == '-') 00787 { 00788 long 00789 compression; 00790 00791 i++; 00792 if (i == (long) (argc-1)) 00793 ThrowCompareException(OptionError,"MissingArgument",option); 00794 compression=ParseMagickOption(MagickCompressionOptions, 00795 MagickFalse,argv[i]); 00796 if (compression < 0) 00797 ThrowCompareException(OptionError, 00798 "UnrecognizedImageCompression",argv[i]); 00799 image_info->compression=(CompressionType) compression; 00800 } 00801 break; 00802 } 00803 ThrowCompareException(OptionError,"UnrecognizedOption",option) 00804 } 00805 case 'd': 00806 { 00807 if (LocaleCompare("debug",option+1) == 0) 00808 { 00809 (void) SetLogEventMask("None"); 00810 if (*option == '-') 00811 { 00812 LogEventType 00813 event_mask; 00814 00815 i++; 00816 if (i == (long) argc) 00817 ThrowCompareException(OptionError,"MissingArgument",option); 00818 event_mask=SetLogEventMask(argv[i]); 00819 if (event_mask == UndefinedEvents) 00820 ThrowCompareException(OptionError,"UnrecognizedEventType", 00821 option); 00822 } 00823 image_info->debug=IsEventLogging(); 00824 break; 00825 } 00826 if (LocaleCompare("define",option+1) == 0) 00827 { 00828 i++; 00829 if (i == (long) argc) 00830 ThrowCompareException(OptionError,"MissingArgument",option); 00831 if (*option == '+') 00832 { 00833 char 00834 *define; 00835 00836 define=RemoveImageOption(image_info,argv[i]); 00837 if (define == (char *) NULL) 00838 ThrowCompareException(OptionError,"NoSuchOption",argv[i]); 00839 define=(char *) RelinquishMagickMemory(define); 00840 break; 00841 } 00842 status=DefineImageOption(image_info,argv[i]); 00843 if (status == MagickFalse) 00844 ThrowCompareException(OptionError,"UnrecognizedOption",argv[i]); 00845 break; 00846 } 00847 if (LocaleCompare("density",option+1) == 0) 00848 { 00849 (void) CloneString(&image_info->density,(char *) NULL); 00850 if (*option == '-') 00851 { 00852 i++; 00853 if (i == (long) argc) 00854 ThrowCompareException(OptionError,"MissingArgument",option); 00855 if (IsGeometry(argv[i]) == MagickFalse) 00856 ThrowCompareInvalidArgumentException(option,argv[i]); 00857 (void) CloneString(&image_info->density,argv[i]); 00858 } 00859 break; 00860 } 00861 if (LocaleCompare("depth",option+1) == 0) 00862 { 00863 image_info->depth=QuantumDepth; 00864 if (*option == '-') 00865 { 00866 i++; 00867 if (i == (long) argc) 00868 ThrowCompareException(OptionError,"MissingArgument",option); 00869 if (IsGeometry(argv[i]) == MagickFalse) 00870 ThrowCompareInvalidArgumentException(option,argv[i]); 00871 image_info->depth=(unsigned long) atol(argv[i]); 00872 } 00873 break; 00874 } 00875 ThrowCompareException(OptionError,"UnrecognizedOption",option) 00876 } 00877 case 'f': 00878 { 00879 if (LocaleCompare("format",option+1) == 0) 00880 { 00881 if (*option == '-') 00882 { 00883 i++; 00884 if (i == (long) argc) 00885 ThrowCompareException(OptionError,"MissingArgument",option); 00886 } 00887 break; 00888 } 00889 ThrowCompareException(OptionError,"UnrecognizedOption",option) 00890 } 00891 case 'h': 00892 { 00893 if (LocaleCompare("help",option+1) == 0) 00894 CompareUsage(); 00895 ThrowCompareException(OptionError,"UnrecognizedOption",option) 00896 } 00897 case 'i': 00898 { 00899 if (LocaleCompare("interlace",option+1) == 0) 00900 { 00901 image_info->interlace=UndefinedInterlace; 00902 if (*option == '-') 00903 { 00904 long 00905 interlace; 00906 00907 i++; 00908 if (i == (long) argc) 00909 ThrowCompareException(OptionError,"MissingArgument",option); 00910 interlace=ParseMagickOption(MagickInterlaceOptions,MagickFalse, 00911 argv[i]); 00912 if (interlace < 0) 00913 ThrowCompareException(OptionError,"UnrecognizedInterlaceType", 00914 argv[i]); 00915 image->interlace=(InterlaceType) interlace; 00916 } 00917 break; 00918 } 00919 ThrowCompareException(OptionError,"UnrecognizedOption",option) 00920 } 00921 case 'l': 00922 { 00923 if (LocaleCompare("limit",option+1) == 0) 00924 { 00925 if (*option == '-') 00926 { 00927 char 00928 *type; 00929 00930 long 00931 resource; 00932 00933 unsigned long 00934 limit; 00935 00936 i++; 00937 if (i == (long) argc) 00938 ThrowCompareException(OptionError,"MissingArgument",option); 00939 type=argv[i]; 00940 i++; 00941 if (i == (long) argc) 00942 ThrowCompareException(OptionError,"MissingArgument",option); 00943 if (IsGeometry(argv[i]) == MagickFalse) 00944 ThrowCompareInvalidArgumentException(option,argv[i]); 00945 limit=(~0UL); 00946 if (LocaleCompare("unlimited",argv[i]) != 0) 00947 limit=(unsigned long) atol(argv[i]); 00948 resource=ParseMagickOption(MagickResourceOptions,MagickFalse, 00949 type); 00950 if (resource < 0) 00951 ThrowCompareException(OptionError, 00952 "UnrecognizedResourceType",type); 00953 (void) SetMagickResourceLimit((ResourceType) resource,limit); 00954 break; 00955 } 00956 break; 00957 } 00958 if (LocaleCompare("log",option+1) == 0) 00959 { 00960 if (*option == '-') 00961 { 00962 i++; 00963 if ((i == (long) argc) || (strchr(argv[i],'%') == (char *) NULL)) 00964 ThrowCompareException(OptionError,"MissingArgument",option); 00965 (void) SetLogFormat(argv[i]); 00966 } 00967 break; 00968 } 00969 ThrowCompareException(OptionError,"UnrecognizedOption",option) 00970 } 00971 case 'm': 00972 { 00973 if (LocaleCompare("metric",option+1) == 0) 00974 { 00975 if (*option == '-') 00976 { 00977 i++; 00978 if (i == (long) argc) 00979 ThrowCompareException(OptionError,"MissingArgument",option); 00980 metric=(MetricType) ParseMagickOption(MagickMetricOptions, 00981 MagickTrue,argv[i]); 00982 if (metric <= UndefinedMetric) 00983 ThrowCompareException(OptionError,"UnrecognizedMetricType", 00984 argv[i]); 00985 } 00986 break; 00987 } 00988 ThrowCompareException(OptionError,"UnrecognizedOption",option) 00989 } 00990 case 'p': 00991 { 00992 if (LocaleCompare("profile",option+1) == 0) 00993 { 00994 i++; 00995 if (i == (long) (argc-1)) 00996 ThrowCompareException(OptionError,"MissingArgument",option); 00997 break; 00998 } 00999 ThrowCompareException(OptionError,"UnrecognizedOption",option) 01000 } 01001 case 'q': 01002 { 01003 if (LocaleCompare("quality",option+1) == 0) 01004 { 01005 image_info->quality=UndefinedCompressionQuality; 01006 if (*option == '-') 01007 { 01008 i++; 01009 if (i == (long) (argc-1)) 01010 ThrowCompareException(OptionError,"MissingArgument",option); 01011 if (IsGeometry(argv[i]) == MagickFalse) 01012 ThrowCompareInvalidArgumentException(option,argv[i]); 01013 image_info->quality=(unsigned long) atol(argv[i]); 01014 } 01015 break; 01016 } 01017 ThrowCompareException(OptionError,"UnrecognizedOption",option) 01018 } 01019 case 's': 01020 { 01021 if (LocaleCompare("sampling-factor",option+1) == 0) 01022 { 01023 (void) CloneString(&image_info->sampling_factor,(char *) NULL); 01024 if (*option == '-') 01025 { 01026 i++; 01027 if (i == (long) argc) 01028 ThrowCompareException(OptionError,"MissingArgument",option); 01029 if (IsGeometry(argv[i]) == MagickFalse) 01030 ThrowCompareInvalidArgumentException(option,argv[i]); 01031 (void) CloneString(&image_info->sampling_factor,argv[i]); 01032 } 01033 break; 01034 } 01035 if (LocaleCompare("size",option+1) == 0) 01036 { 01037 (void) CloneString(&image_info->size,(char *) NULL); 01038 if (*option == '-') 01039 { 01040 i++; 01041 if (i == (long) argc) 01042 ThrowCompareException(OptionError,"MissingArgument",option); 01043 if (IsGeometry(argv[i]) == MagickFalse) 01044 ThrowCompareInvalidArgumentException(option,argv[i]); 01045 (void) CloneString(&image_info->size,argv[i]); 01046 } 01047 break; 01048 } 01049 ThrowCompareException(OptionError,"UnrecognizedOption",option) 01050 } 01051 case 'v': 01052 { 01053 if (LocaleCompare("verbose",option+1) == 0) 01054 { 01055 image_info->verbose=(MagickBooleanType) (*option == '-'); 01056 break; 01057 } 01058 if (LocaleCompare("version",option+1) == 0) 01059 break; 01060 if (LocaleCompare("virtual-pixel",option+1) == 0) 01061 { 01062 if (*option == '-') 01063 { 01064 long 01065 method; 01066 01067 i++; 01068 if (i == (long) (argc-1)) 01069 ThrowCompareException(OptionError,"MissingArgument",option); 01070 method=ParseMagickOption(MagickVirtualPixelOptions,MagickFalse, 01071 argv[i]); 01072 if (method < 0) 01073 ThrowCompareException(OptionError, 01074 "UnrecognizedVirtualPixelMethod",argv[i]); 01075 } 01076 break; 01077 } 01078 ThrowCompareException(OptionError,"UnrecognizedOption",option) 01079 } 01080 case '?': 01081 break; 01082 default: 01083 ThrowCompareException(OptionError,"UnrecognizedOption",option) 01084 } 01085 } 01086 option=argv[i]; 01087 if (LocaleCompare("help",option+1) == 0) 01088 CompareUsage(); 01089 if (i != (long) (argc-1)) 01090 ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]); 01091 if ((image == (Image *) NULL) || (reconstruct_image == (Image *) NULL)) 01092 ThrowCompareException(OptionError,"MissingAnImageFilename",argv[i]); 01093 status&=MogrifyImages(image_info,(int) (i-j),argv+j,&reconstruct_image); 01094 GetImageException(image,exception); 01095 difference_image=CompareImageChannels(image,reconstruct_image,channel, 01096 metric,&distortion,exception); 01097 if (difference_image != (Image *) NULL) 01098 { 01099 if (image_info->verbose != MagickFalse) 01100 (void) IsImagesEqual(image,reconstruct_image); 01101 difference_image->error=image->error; 01102 status&=WriteImages(image_info,difference_image,argv[argc-1],exception); 01103 DestroyImageList(difference_image); 01104 } 01105 if (metric != UndefinedMetric) 01106 (void) fprintf(stdout,"%g dB\n",distortion); 01107 DestroyCompare(); 01108 return(status != 0 ? MagickTrue : MagickFalse); 01109 } 01110 01111 /* 01112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01113 % % 01114 % % 01115 % % 01116 % I s I m a g e s E q u a l % 01117 % % 01118 % % 01119 % % 01120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01121 % 01122 % IsImagesEqual() measures the difference between colors at each pixel 01123 % location of two images. A value other than 0 means the colors match 01124 % exactly. Otherwise an error measure is computed by summing over all 01125 % pixels in an image the distance squared in RGB space between each image 01126 % pixel and its corresponding pixel in the reconstruct image. The error 01127 % measure is assigned to these image members: 01128 % 01129 % o mean_error_per_pixel: The mean error for any single pixel in 01130 % the image. 01131 % 01132 % o normalized_mean_error: The normalized mean quantization error for 01133 % any single pixel in the image. This distance measure is normalized to 01134 % a range between 0 and 1. It is independent of the range of red, green, 01135 % and blue values in the image. 01136 % 01137 % o normalized_maximum_error: The normalized maximum quantization 01138 % error for any single pixel in the image. This distance measure is 01139 % normalized to a range between 0 and 1. It is independent of the range 01140 % of red, green, and blue values in your image. 01141 % 01142 % A small normalized mean square error, accessed as 01143 % image->normalized_mean_error, suggests the images are very similiar in 01144 % spatial layout and color. 01145 % 01146 % The format of the IsImagesEqual method is: 01147 % 01148 % MagickBooleanType IsImagesEqual(Image *image, 01149 % const Image *reconstruct_image) 01150 % 01151 % A description of each parameter follows. 01152 % 01153 % o image: The image. 01154 % 01155 % o reconstruct_image: The reconstruct image. 01156 % 01157 */ 01158 MagickExport MagickBooleanType IsImagesEqual(Image *image, 01159 const Image *reconstruct_image) 01160 { 01161 IndexPacket 01162 *indexes, 01163 *reconstruct_indexes; 01164 01165 long 01166 y; 01167 01168 MagickBooleanType 01169 status; 01170 01171 MagickRealType 01172 area, 01173 distance, 01174 maximum_error, 01175 mean_error, 01176 mean_error_per_pixel; 01177 01178 register const PixelPacket 01179 *p, 01180 *q; 01181 01182 register long 01183 x; 01184 01185 assert(image != (Image *) NULL); 01186 assert(image->signature == MagickSignature); 01187 assert(reconstruct_image != (const Image *) NULL); 01188 assert(reconstruct_image->signature == MagickSignature); 01189 if ((reconstruct_image->columns != image->columns) || 01190 (reconstruct_image->rows != image->rows)) 01191 ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename); 01192 if (image->colorspace != reconstruct_image->colorspace) 01193 ThrowBinaryException(ImageError,"ImageColorspaceDiffers",image->filename); 01194 if (image->matte != reconstruct_image->matte) 01195 ThrowBinaryException(ImageError,"ImageOpacityDiffers",image->filename); 01196 area=0.0; 01197 maximum_error=0.0; 01198 mean_error_per_pixel=0.0; 01199 mean_error=0.0; 01200 for (y=0; y < (long) image->rows; y++) 01201 { 01202 p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); 01203 q=AcquireImagePixels(reconstruct_image,0,y,reconstruct_image->columns,1, 01204 &image->exception); 01205 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL)) 01206 break; 01207 indexes=GetIndexes(image); 01208 reconstruct_indexes=GetIndexes(reconstruct_image); 01209 for (x=0; x < (long) image->columns; x++) 01210 { 01211 distance=fabs(p->red-(MagickRealType) q->red); 01212 mean_error_per_pixel+=distance; 01213 mean_error+=distance*distance; 01214 if (distance > maximum_error) 01215 maximum_error=distance; 01216 area++; 01217 distance=fabs(p->green-(MagickRealType) q->green); 01218 mean_error_per_pixel+=distance; 01219 mean_error+=distance*distance; 01220 if (distance > maximum_error) 01221 maximum_error=distance; 01222 area++; 01223 distance=fabs(p->blue-(MagickRealType) q->blue); 01224 mean_error_per_pixel+=distance; 01225 mean_error+=distance*distance; 01226 if (distance > maximum_error) 01227 maximum_error=distance; 01228 area++; 01229 if (image->matte != MagickFalse) 01230 { 01231 distance=fabs(p->opacity-(MagickRealType) q->opacity); 01232 mean_error_per_pixel+=distance; 01233 mean_error+=distance*distance; 01234 if (distance > maximum_error) 01235 maximum_error=distance; 01236 area++; 01237 } 01238 if (image->colorspace == CMYKColorspace) 01239 { 01240 distance=fabs(indexes[x]-(MagickRealType) reconstruct_indexes[x]); 01241 mean_error_per_pixel+=distance; 01242 mean_error+=distance*distance; 01243 if (distance > maximum_error) 01244 maximum_error=distance; 01245 area++; 01246 } 01247 p++; 01248 q++; 01249 } 01250 } 01251 image->error.mean_error_per_pixel=mean_error_per_pixel/area; 01252 image->error.normalized_mean_error=mean_error/area/MaxRGB/MaxRGB; 01253 image->error.normalized_maximum_error=maximum_error/MaxRGB; 01254 status=(MagickBooleanType) 01255 (image->error.mean_error_per_pixel == 0.0 ? MagickTrue : MagickFalse); 01256 return(status); 01257 }

Generated on Mon Oct 25 13:42:09 2004 for ImageMagick by doxygen 1.3.7
ImageMagick Copyright © 2004, ImageMagick Studio LLC