00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "magick/studio.h"
00044 #include "magick/artifact.h"
00045 #include "magick/cache-view.h"
00046 #include "magick/client.h"
00047 #include "magick/color.h"
00048 #include "magick/color-private.h"
00049 #include "magick/colorspace.h"
00050 #include "magick/colorspace-private.h"
00051 #include "magick/compare.h"
00052 #include "magick/composite-private.h"
00053 #include "magick/constitute.h"
00054 #include "magick/exception-private.h"
00055 #include "magick/geometry.h"
00056 #include "magick/image-private.h"
00057 #include "magick/list.h"
00058 #include "magick/log.h"
00059 #include "magick/memory_.h"
00060 #include "magick/option.h"
00061 #include "magick/pixel-private.h"
00062 #include "magick/resource_.h"
00063 #include "magick/string_.h"
00064 #include "magick/utility.h"
00065 #include "magick/version.h"
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 MagickExport Image *CompareImages(Image *image,const Image *reconstruct_image,
00104 const MetricType metric,double *distortion,ExceptionInfo *exception)
00105 {
00106 Image
00107 *highlight_image;
00108
00109 highlight_image=CompareImageChannels(image,reconstruct_image,AllChannels,
00110 metric,distortion,exception);
00111 return(highlight_image);
00112 }
00113
00114 MagickExport Image *CompareImageChannels(Image *image,
00115 const Image *reconstruct_image,const ChannelType channel,
00116 const MetricType metric,double *distortion,ExceptionInfo *exception)
00117 {
00118 const char
00119 *artifact;
00120
00121 Image
00122 *difference_image,
00123 *highlight_image;
00124
00125 long
00126 y;
00127
00128 MagickBooleanType
00129 status;
00130
00131 MagickPixelPacket
00132 highlight,
00133 lowlight,
00134 zero;
00135
00136 ViewInfo
00137 **highlight_view,
00138 **image_view,
00139 **reconstruct_view;
00140
00141 assert(image != (Image *) NULL);
00142 assert(image->signature == MagickSignature);
00143 if (image->debug != MagickFalse)
00144 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00145 assert(reconstruct_image != (const Image *) NULL);
00146 assert(reconstruct_image->signature == MagickSignature);
00147 assert(distortion != (double *) NULL);
00148 *distortion=0.0;
00149 if (image->debug != MagickFalse)
00150 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00151 if ((reconstruct_image->columns != image->columns) ||
00152 (reconstruct_image->rows != image->rows))
00153 ThrowImageException(ImageError,"ImageSizeDiffers");
00154 status=GetImageChannelDistortion(image,reconstruct_image,channel,metric,
00155 distortion,exception);
00156 if (status == MagickFalse)
00157 return((Image *) NULL);
00158 difference_image=CloneImage(image,0,0,MagickTrue,exception);
00159 if (difference_image == (Image *) NULL)
00160 return((Image *) NULL);
00161 (void) SetImageAlphaChannel(difference_image,OpaqueAlphaChannel);
00162 highlight_image=CloneImage(image,image->columns,image->rows,MagickTrue,
00163 exception);
00164 if (highlight_image == (Image *) NULL)
00165 {
00166 difference_image=DestroyImage(difference_image);
00167 return((Image *) NULL);
00168 }
00169 if (SetImageStorageClass(highlight_image,DirectClass) == MagickFalse)
00170 {
00171 InheritException(exception,&highlight_image->exception);
00172 difference_image=DestroyImage(difference_image);
00173 highlight_image=DestroyImage(highlight_image);
00174 return((Image *) NULL);
00175 }
00176 (void) SetImageAlphaChannel(highlight_image,OpaqueAlphaChannel);
00177 (void) QueryMagickColor("#f1001ecc",&highlight,exception);
00178 artifact=GetImageArtifact(image,"highlight-color");
00179 if (artifact != (const char *) NULL)
00180 (void) QueryMagickColor(artifact,&highlight,exception);
00181 (void) QueryMagickColor("#ffffffcc",&lowlight,exception);
00182 artifact=GetImageArtifact(image,"lowlight-color");
00183 if (artifact != (const char *) NULL)
00184 (void) QueryMagickColor(artifact,&lowlight,exception);
00185 if (highlight_image->colorspace == CMYKColorspace)
00186 {
00187 ConvertRGBToCMYK(&highlight);
00188 ConvertRGBToCMYK(&lowlight);
00189 }
00190
00191
00192
00193 status=MagickTrue;
00194 GetMagickPixelPacket(image,&zero);
00195 image_view=AcquireCacheViewThreadSet(image);
00196 reconstruct_view=AcquireCacheViewThreadSet(reconstruct_image);
00197 highlight_view=AcquireCacheViewThreadSet(highlight_image);
00198 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00199 #pragma omp parallel for schedule(dynamic,8) shared(status)
00200 #endif
00201 for (y=0; y < (long) image->rows; y++)
00202 {
00203 MagickBooleanType
00204 sync;
00205
00206 MagickPixelPacket
00207 pixel,
00208 reconstruct_pixel;
00209
00210 register const IndexPacket
00211 *indexes,
00212 *reconstruct_indexes;
00213
00214 register const PixelPacket
00215 *p,
00216 *q;
00217
00218 register IndexPacket
00219 *highlight_indexes;
00220
00221 register long
00222 id,
00223 x;
00224
00225 register PixelPacket
00226 *r;
00227
00228 if (status == MagickFalse)
00229 continue;
00230 id=GetCacheViewThreadId();
00231 p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,
00232 exception);
00233 q=GetCacheViewVirtualPixels(reconstruct_view[id],0,y,
00234 reconstruct_image->columns,1,exception);
00235 r=QueueCacheViewAuthenticPixels(highlight_view[id],0,y,
00236 highlight_image->columns,1,exception);
00237 if ((p == (const PixelPacket *) NULL) ||
00238 (q == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
00239 {
00240 status=MagickFalse;
00241 continue;
00242 }
00243 indexes=GetCacheViewVirtualIndexes(image_view[id]);
00244 reconstruct_indexes=GetCacheViewVirtualIndexes(reconstruct_view[id]);
00245 highlight_indexes=GetCacheViewAuthenticIndexes(highlight_view[id]);
00246 pixel=zero;
00247 reconstruct_pixel=zero;
00248 for (x=0; x < (long) image->columns; x++)
00249 {
00250 MagickStatusType
00251 difference;
00252
00253 SetMagickPixelPacket(image,p,indexes+x,&pixel);
00254 SetMagickPixelPacket(reconstruct_image,q,reconstruct_indexes+x,
00255 &reconstruct_pixel);
00256 difference=MagickFalse;
00257 if (channel == AllChannels)
00258 {
00259 if (IsMagickColorSimilar(&pixel,&reconstruct_pixel) == MagickFalse)
00260 difference=MagickTrue;
00261 }
00262 else
00263 {
00264 if (((channel & RedChannel) != 0) && (p->red != q->red))
00265 difference=MagickTrue;
00266 if (((channel & GreenChannel) != 0) && (p->green != q->green))
00267 difference=MagickTrue;
00268 if (((channel & BlueChannel) != 0) && (p->blue != q->blue))
00269 difference=MagickTrue;
00270 if (((channel & OpacityChannel) != 0) &&
00271 (image->matte != MagickFalse) && (p->opacity != q->opacity))
00272 difference=MagickTrue;
00273 if ((((channel & IndexChannel) != 0) &&
00274 (image->colorspace == CMYKColorspace) &&
00275 (reconstruct_image->colorspace == CMYKColorspace)) &&
00276 (indexes[x] != reconstruct_indexes[x]))
00277 difference=MagickTrue;
00278 }
00279 if (difference != MagickFalse)
00280 SetPixelPacket(highlight_image,&highlight,r,highlight_indexes+x);
00281 else
00282 SetPixelPacket(highlight_image,&lowlight,r,highlight_indexes+x);
00283 p++;
00284 q++;
00285 r++;
00286 }
00287 sync=SyncCacheViewAuthenticPixels(highlight_view[id],exception);
00288 if (sync == MagickFalse)
00289 status=MagickFalse;
00290 }
00291 highlight_view=DestroyCacheViewThreadSet(highlight_view);
00292 reconstruct_view=DestroyCacheViewThreadSet(reconstruct_view);
00293 image_view=DestroyCacheViewThreadSet(image_view);
00294 (void) CompositeImage(difference_image,image->compose,highlight_image,0,0);
00295 highlight_image=DestroyImage(highlight_image);
00296 if (status == MagickFalse)
00297 difference_image=DestroyImage(difference_image);
00298 return(difference_image);
00299 }
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337 MagickExport MagickBooleanType GetImageDistortion(Image *image,
00338 const Image *reconstruct_image,const MetricType metric,double *distortion,
00339 ExceptionInfo *exception)
00340 {
00341 MagickBooleanType
00342 status;
00343
00344 status=GetImageChannelDistortion(image,reconstruct_image,AllChannels,
00345 metric,distortion,exception);
00346 return(status);
00347 }
00348
00349 static MagickBooleanType GetAbsoluteError(const Image *image,
00350 const Image *reconstruct_image,const ChannelType channel,double *distortion,
00351 ExceptionInfo *exception)
00352 {
00353 long
00354 y;
00355
00356 MagickBooleanType
00357 status;
00358
00359 MagickPixelPacket
00360 zero;
00361
00362 ViewInfo
00363 **image_view,
00364 **reconstruct_view;
00365
00366
00367
00368
00369 status=MagickTrue;
00370 GetMagickPixelPacket(image,&zero);
00371 image_view=AcquireCacheViewThreadSet(image);
00372 reconstruct_view=AcquireCacheViewThreadSet(reconstruct_image);
00373 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00374 #pragma omp parallel for schedule(dynamic,8) shared(status)
00375 #endif
00376 for (y=0; y < (long) image->rows; y++)
00377 {
00378 double
00379 channel_distortion[AllChannels+1];
00380
00381 MagickPixelPacket
00382 pixel,
00383 reconstruct_pixel;
00384
00385 register const IndexPacket
00386 *indexes,
00387 *reconstruct_indexes;
00388
00389 register const PixelPacket
00390 *p,
00391 *q;
00392
00393 register long
00394 i,
00395 id,
00396 x;
00397
00398 if (status == MagickFalse)
00399 continue;
00400 id=GetCacheViewThreadId();
00401 p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,exception);
00402 q=GetCacheViewVirtualPixels(reconstruct_view[id],0,y,
00403 reconstruct_image->columns,1,exception);
00404 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
00405 {
00406 status=MagickFalse;
00407 continue;
00408 }
00409 indexes=GetCacheViewVirtualIndexes(image_view[id]);
00410 reconstruct_indexes=GetCacheViewVirtualIndexes(reconstruct_view[id]);
00411 pixel=zero;
00412 reconstruct_pixel=pixel;
00413 (void) ResetMagickMemory(channel_distortion,0,sizeof(channel_distortion));
00414 for (x=0; x < (long) image->columns; x++)
00415 {
00416 SetMagickPixelPacket(image,p,indexes+x,&pixel);
00417 SetMagickPixelPacket(reconstruct_image,q,reconstruct_indexes+x,
00418 &reconstruct_pixel);
00419 if (IsMagickColorSimilar(&pixel,&reconstruct_pixel) == MagickFalse)
00420 {
00421 if ((channel & RedChannel) != 0)
00422 channel_distortion[RedChannel]++;
00423 if ((channel & GreenChannel) != 0)
00424 channel_distortion[GreenChannel]++;
00425 if ((channel & BlueChannel) != 0)
00426 channel_distortion[BlueChannel]++;
00427 if (((channel & OpacityChannel) != 0) &&
00428 (image->matte != MagickFalse))
00429 channel_distortion[OpacityChannel]++;
00430 if (((channel & IndexChannel) != 0) &&
00431 (image->colorspace == CMYKColorspace))
00432 channel_distortion[BlackChannel]++;
00433 channel_distortion[AllChannels]++;
00434 }
00435 p++;
00436 q++;
00437 }
00438 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00439 #pragma omp critical
00440 #endif
00441 for (i=0; i <= (long) AllChannels; i++)
00442 distortion[i]+=channel_distortion[i];
00443 }
00444 reconstruct_view=DestroyCacheViewThreadSet(reconstruct_view);
00445 image_view=DestroyCacheViewThreadSet(image_view);
00446 return(status);
00447 }
00448
00449 static unsigned long GetNumberChannels(const Image *image,
00450 const ChannelType channel)
00451 {
00452 unsigned long
00453 channels;
00454
00455 channels=0;
00456 if ((channel & RedChannel) != 0)
00457 channels++;
00458 if ((channel & GreenChannel) != 0)
00459 channels++;
00460 if ((channel & BlueChannel) != 0)
00461 channels++;
00462 if (((channel & OpacityChannel) != 0) &&
00463 (image->matte != MagickFalse))
00464 channels++;
00465 if (((channel & IndexChannel) != 0) &&
00466 (image->colorspace == CMYKColorspace))
00467 channels++;
00468 return(channels);
00469 }
00470
00471 static MagickBooleanType GetMeanAbsoluteError(const Image *image,
00472 const Image *reconstruct_image,const ChannelType channel,
00473 double *distortion,ExceptionInfo *exception)
00474 {
00475 double
00476 scale;
00477
00478 long
00479 y;
00480
00481 MagickBooleanType
00482 status;
00483
00484 ViewInfo
00485 **image_view,
00486 **reconstruct_view;
00487
00488 status=MagickTrue;
00489 scale=1.0/((double) image->columns*image->rows);
00490 image_view=AcquireCacheViewThreadSet(image);
00491 reconstruct_view=AcquireCacheViewThreadSet(reconstruct_image);
00492 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00493 #pragma omp parallel for schedule(dynamic,8) shared(status)
00494 #endif
00495 for (y=0; y < (long) image->rows; y++)
00496 {
00497 double
00498 channel_distortion[AllChannels+1];
00499
00500 register const IndexPacket
00501 *indexes,
00502 *reconstruct_indexes;
00503
00504 register const PixelPacket
00505 *p,
00506 *q;
00507
00508 register long
00509 i,
00510 id,
00511 x;
00512
00513 if (status == MagickFalse)
00514 continue;
00515 id=GetCacheViewThreadId();
00516 p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,exception);
00517 q=GetCacheViewVirtualPixels(reconstruct_view[id],0,y,
00518 reconstruct_image->columns,1,exception);
00519 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
00520 {
00521 status=MagickFalse;
00522 continue;
00523 }
00524 indexes=GetCacheViewVirtualIndexes(image_view[id]);
00525 reconstruct_indexes=GetCacheViewVirtualIndexes(reconstruct_view[id]);
00526 (void) ResetMagickMemory(channel_distortion,0,sizeof(channel_distortion));
00527 for (x=0; x < (long) image->columns; x++)
00528 {
00529 MagickRealType
00530 distance;
00531
00532 if ((channel & RedChannel) != 0)
00533 {
00534 distance=fabs(p->red-(double) q->red);
00535 channel_distortion[RedChannel]+=scale*distance;
00536 channel_distortion[AllChannels]+=scale*distance;
00537 }
00538 if ((channel & GreenChannel) != 0)
00539 {
00540 distance=fabs(p->green-(double) q->green);
00541 channel_distortion[GreenChannel]+=scale*distance;
00542 channel_distortion[AllChannels]+=scale*distance;
00543 }
00544 if ((channel & BlueChannel) != 0)
00545 {
00546 distance=fabs(p->blue-(double) q->blue);
00547 channel_distortion[BlueChannel]+=scale*distance;
00548 channel_distortion[AllChannels]+=scale*distance;
00549 }
00550 if (((channel & OpacityChannel) != 0) &&
00551 (image->matte != MagickFalse))
00552 {
00553 distance=fabs(p->opacity-(double) q->opacity);
00554 channel_distortion[OpacityChannel]+=scale*distance;
00555 channel_distortion[AllChannels]+=scale*distance;
00556 }
00557 if (((channel & IndexChannel) != 0) &&
00558 (image->colorspace == CMYKColorspace))
00559 {
00560 distance=fabs(indexes[x]-(double) reconstruct_indexes[x]);
00561 channel_distortion[BlackChannel]+=scale*distance;
00562 channel_distortion[AllChannels]+=scale*distance;
00563 }
00564 p++;
00565 q++;
00566 }
00567 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00568 #pragma omp critical
00569 #endif
00570 for (i=0; i <= (long) AllChannels; i++)
00571 distortion[i]+=channel_distortion[i];
00572 }
00573 reconstruct_view=DestroyCacheViewThreadSet(reconstruct_view);
00574 image_view=DestroyCacheViewThreadSet(image_view);
00575 distortion[AllChannels]/=(double) GetNumberChannels(image,channel);
00576 return(status);
00577 }
00578
00579 static MagickBooleanType GetMeanErrorPerPixel(Image *image,
00580 const Image *reconstruct_image,const ChannelType channel,double *distortion,
00581 ExceptionInfo *exception)
00582 {
00583 long
00584 y;
00585
00586 MagickBooleanType
00587 status;
00588
00589 MagickRealType
00590 alpha,
00591 area,
00592 beta,
00593 maximum_error,
00594 mean_error;
00595
00596 ViewInfo
00597 *image_view,
00598 *reconstruct_view;
00599
00600 status=MagickTrue;
00601 alpha=1.0;
00602 beta=1.0;
00603 area=0.0;
00604 maximum_error=0.0;
00605 mean_error=0.0;
00606 image_view=AcquireCacheView(image);
00607 reconstruct_view=AcquireCacheView(reconstruct_image);
00608 for (y=0; y < (long) image->rows; y++)
00609 {
00610 register const IndexPacket
00611 *indexes,
00612 *reconstruct_indexes;
00613
00614 register const PixelPacket
00615 *p,
00616 *q;
00617
00618 register long
00619 x;
00620
00621 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00622 q=GetCacheViewVirtualPixels(reconstruct_view,0,y,reconstruct_image->columns,1,
00623 exception);
00624 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
00625 {
00626 status=MagickFalse;
00627 break;
00628 }
00629 indexes=GetCacheViewVirtualIndexes(image_view);
00630 reconstruct_indexes=GetCacheViewVirtualIndexes(reconstruct_view);
00631 for (x=0; x < (long) image->columns; x++)
00632 {
00633 MagickRealType
00634 distance;
00635
00636 if ((channel & OpacityChannel) != 0)
00637 {
00638 if (image->matte != MagickFalse)
00639 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
00640 if (reconstruct_image->matte != MagickFalse)
00641 beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity));
00642 }
00643 if ((channel & RedChannel) != 0)
00644 {
00645 distance=fabs(alpha*p->red-beta*q->red);
00646 distortion[RedChannel]+=distance;
00647 distortion[AllChannels]+=distance;
00648 mean_error+=distance*distance;
00649 if (distance > maximum_error)
00650 maximum_error=distance;
00651 area++;
00652 }
00653 if ((channel & GreenChannel) != 0)
00654 {
00655 distance=fabs(alpha*p->green-beta*q->green);
00656 distortion[GreenChannel]+=distance;
00657 distortion[AllChannels]+=distance;
00658 mean_error+=distance*distance;
00659 if (distance > maximum_error)
00660 maximum_error=distance;
00661 area++;
00662 }
00663 if ((channel & BlueChannel) != 0)
00664 {
00665 distance=fabs(alpha*p->blue-beta*q->blue);
00666 distortion[BlueChannel]+=distance;
00667 distortion[AllChannels]+=distance;
00668 mean_error+=distance*distance;
00669 if (distance > maximum_error)
00670 maximum_error=distance;
00671 area++;
00672 }
00673 if (((channel & OpacityChannel) != 0) &&
00674 (image->matte != MagickFalse))
00675 {
00676 distance=fabs((double) p->opacity-q->opacity);
00677 distortion[OpacityChannel]+=distance;
00678 distortion[AllChannels]+=distance;
00679 mean_error+=distance*distance;
00680 if (distance > maximum_error)
00681 maximum_error=distance;
00682 area++;
00683 }
00684 if (((channel & IndexChannel) != 0) &&
00685 (image->colorspace == CMYKColorspace) &&
00686 (reconstruct_image->colorspace == CMYKColorspace))
00687 {
00688 distance=fabs(alpha*indexes[x]-beta*reconstruct_indexes[x]);
00689 distortion[BlackChannel]+=distance;
00690 distortion[AllChannels]+=distance;
00691 mean_error+=distance*distance;
00692 if (distance > maximum_error)
00693 maximum_error=distance;
00694 area++;
00695 }
00696 p++;
00697 q++;
00698 }
00699 }
00700 reconstruct_view=DestroyCacheView(reconstruct_view);
00701 image_view=DestroyCacheView(image_view);
00702 image->error.mean_error_per_pixel=distortion[AllChannels]/area;
00703 image->error.normalized_mean_error=QuantumScale*QuantumScale*mean_error/area;
00704 image->error.normalized_maximum_error=QuantumScale*maximum_error;
00705 return(status);
00706 }
00707
00708 static MagickBooleanType GetMeanSquaredError(const Image *image,
00709 const Image *reconstruct_image,const ChannelType channel,
00710 double *distortion,ExceptionInfo *exception)
00711 {
00712 double
00713 scale;
00714
00715 long
00716 y;
00717
00718 MagickBooleanType
00719 status;
00720
00721 ViewInfo
00722 **image_view,
00723 **reconstruct_view;
00724
00725 status=MagickTrue;
00726 scale=QuantumScale/((double) image->columns*image->rows);
00727 image_view=AcquireCacheViewThreadSet(image);
00728 reconstruct_view=AcquireCacheViewThreadSet(reconstruct_image);
00729 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00730 #pragma omp parallel for schedule(dynamic,8) shared(status)
00731 #endif
00732 for (y=0; y < (long) image->rows; y++)
00733 {
00734 double
00735 channel_distortion[AllChannels+1];
00736
00737 register const IndexPacket
00738 *indexes,
00739 *reconstruct_indexes;
00740
00741 register const PixelPacket
00742 *p,
00743 *q;
00744
00745 register long
00746 i,
00747 id,
00748 x;
00749
00750 if (status == MagickFalse)
00751 continue;
00752 id=GetCacheViewThreadId();
00753 p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,exception);
00754 q=GetCacheViewVirtualPixels(reconstruct_view[id],0,y,
00755 reconstruct_image->columns,1,exception);
00756 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
00757 {
00758 status=MagickFalse;
00759 continue;
00760 }
00761 indexes=GetCacheViewVirtualIndexes(image_view[id]);
00762 reconstruct_indexes=GetCacheViewVirtualIndexes(reconstruct_view[id]);
00763 (void) ResetMagickMemory(channel_distortion,0,sizeof(channel_distortion));
00764 for (x=0; x < (long) image->columns; x++)
00765 {
00766 MagickRealType
00767 distance;
00768
00769 if ((channel & RedChannel) != 0)
00770 {
00771 distance=(p->red-(MagickRealType) q->red);
00772 channel_distortion[RedChannel]+=scale*distance*distance;
00773 channel_distortion[AllChannels]+=scale*distance*distance;
00774 }
00775 if ((channel & GreenChannel) != 0)
00776 {
00777 distance=(p->green-(MagickRealType) q->green);
00778 channel_distortion[GreenChannel]+=scale*distance*distance;
00779 channel_distortion[AllChannels]+=scale*distance*distance;
00780 }
00781 if ((channel & BlueChannel) != 0)
00782 {
00783 distance=(p->blue-(MagickRealType) q->blue);
00784 channel_distortion[BlueChannel]+=scale*distance*distance;
00785 channel_distortion[AllChannels]+=scale*distance*distance;
00786 }
00787 if (((channel & OpacityChannel) != 0) &&
00788 (image->matte != MagickFalse))
00789 {
00790 distance=(p->opacity-(MagickRealType) q->opacity);
00791 channel_distortion[OpacityChannel]+=scale*distance*distance;
00792 channel_distortion[AllChannels]+=scale*distance*distance;
00793 }
00794 if (((channel & IndexChannel) != 0) &&
00795 (image->colorspace == CMYKColorspace) &&
00796 (reconstruct_image->colorspace == CMYKColorspace))
00797 {
00798 distance=(indexes[x]-(MagickRealType) reconstruct_indexes[x]);
00799 channel_distortion[BlackChannel]+=scale*distance*distance;
00800 channel_distortion[AllChannels]+=scale*distance*distance;
00801 }
00802 p++;
00803 q++;
00804 }
00805 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00806 #pragma omp critical
00807 #endif
00808 for (i=0; i <= (long) AllChannels; i++)
00809 distortion[i]+=channel_distortion[i];
00810 }
00811 reconstruct_view=DestroyCacheViewThreadSet(reconstruct_view);
00812 image_view=DestroyCacheViewThreadSet(image_view);
00813 distortion[AllChannels]/=(double) GetNumberChannels(image,channel);
00814 return(status);
00815 }
00816
00817 static MagickBooleanType GetPeakAbsoluteError(const Image *image,
00818 const Image *reconstruct_image,const ChannelType channel,
00819 double *distortion,ExceptionInfo *exception)
00820 {
00821 long
00822 y;
00823
00824 MagickBooleanType
00825 status;
00826
00827 ViewInfo
00828 **image_view,
00829 **reconstruct_view;
00830
00831 status=MagickTrue;
00832 image_view=AcquireCacheViewThreadSet(image);
00833 reconstruct_view=AcquireCacheViewThreadSet(reconstruct_image);
00834 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00835 #pragma omp parallel for schedule(dynamic,8) shared(status)
00836 #endif
00837 for (y=0; y < (long) image->rows; y++)
00838 {
00839 double
00840 channel_distortion[AllChannels+1];
00841
00842 register const IndexPacket
00843 *indexes,
00844 *reconstruct_indexes;
00845
00846 register const PixelPacket
00847 *p,
00848 *q;
00849
00850 register long
00851 i,
00852 id,
00853 x;
00854
00855 if (status == MagickFalse)
00856 continue;
00857 id=GetCacheViewThreadId();
00858 p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,exception);
00859 q=GetCacheViewVirtualPixels(reconstruct_view[id],0,y,
00860 reconstruct_image->columns,1,exception);
00861 if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
00862 {
00863 status=MagickFalse;
00864 continue;
00865 }
00866 indexes=GetCacheViewVirtualIndexes(image_view[id]);
00867 reconstruct_indexes=GetCacheViewVirtualIndexes(reconstruct_view[id]);
00868 (void) ResetMagickMemory(channel_distortion,0,sizeof(channel_distortion));
00869 for (x=0; x < (long) image->columns; x++)
00870 {
00871 MagickRealType
00872 distance;
00873
00874 if ((channel & RedChannel) != 0)
00875 {
00876 distance=fabs(p->red-(double) q->red);
00877 if (distance > channel_distortion[RedChannel])
00878 channel_distortion[RedChannel]=distance;
00879 if (distance > channel_distortion[AllChannels])
00880 channel_distortion[AllChannels]=distance;
00881 }
00882 if ((channel & GreenChannel) != 0)
00883 {
00884 distance=fabs(p->green-(double) q->green);
00885 if (distance > channel_distortion[GreenChannel])
00886 channel_distortion[GreenChannel]=distance;
00887 if (distance > channel_distortion[AllChannels])
00888 channel_distortion[AllChannels]=distance;
00889 }
00890 if ((channel & BlueChannel) != 0)
00891 {
00892 distance=fabs(p->blue-(double) q->blue);
00893 if (distance > channel_distortion[BlueChannel])
00894 channel_distortion[BlueChannel]=distance;
00895 if (distance > channel_distortion[AllChannels])
00896 channel_distortion[AllChannels]=distance;
00897 }
00898 if (((channel & OpacityChannel) != 0) &&
00899 (image->matte != MagickFalse))
00900 {
00901 distance=fabs(p->opacity-(double) q->opacity);
00902 if (distance > channel_distortion[OpacityChannel])
00903 channel_distortion[OpacityChannel]=distance;
00904 if (distance > channel_distortion[AllChannels])
00905 channel_distortion[AllChannels]=distance;
00906 }
00907 if (((channel & IndexChannel) != 0) &&
00908 (image->colorspace == CMYKColorspace) &&
00909 (reconstruct_image->colorspace == CMYKColorspace))
00910 {
00911 distance=fabs(indexes[x]-(double) reconstruct_indexes[x]);
00912 if (distance > channel_distortion[BlackChannel])
00913 channel_distortion[BlackChannel]=distance;
00914 if (distance > channel_distortion[AllChannels])
00915 channel_distortion[AllChannels]=distance;
00916 }
00917 p++;
00918 q++;
00919 }
00920 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00921 #pragma omp critical
00922 #endif
00923 for (i=0; i <= (long) AllChannels; i++)
00924 if (channel_distortion[i] > distortion[i])
00925 distortion[i]=channel_distortion[i];
00926 }
00927 reconstruct_view=DestroyCacheViewThreadSet(reconstruct_view);
00928 image_view=DestroyCacheViewThreadSet(image_view);
00929 return(status);
00930 }
00931
00932 static MagickBooleanType GetPeakSignalToNoiseRatio(const Image *image,
00933 const Image *reconstruct_image,const ChannelType channel,
00934 double *distortion,ExceptionInfo *exception)
00935 {
00936 MagickBooleanType
00937 status;
00938
00939 status=GetMeanSquaredError(image,reconstruct_image,channel,distortion,
00940 exception);
00941 if ((channel & RedChannel) != 0)
00942 distortion[RedChannel]=20.0*log10((double) 1.0/sqrt(QuantumScale*
00943 distortion[RedChannel]));
00944 if ((channel & GreenChannel) != 0)
00945 distortion[GreenChannel]=20.0*log10((double) 1.0/sqrt(QuantumScale*
00946 distortion[GreenChannel]));
00947 if ((channel & BlueChannel) != 0)
00948 distortion[BlueChannel]=20.0*log10((double) 1.0/sqrt(QuantumScale*
00949 distortion[BlueChannel]));
00950 if (((channel & OpacityChannel) != 0) &&
00951 (image->matte != MagickFalse))
00952 distortion[OpacityChannel]=20.0*log10((double) 1.0/sqrt(QuantumScale*
00953 distortion[OpacityChannel]));
00954 if (((channel & IndexChannel) != 0) &&
00955 (image->colorspace == CMYKColorspace))
00956 distortion[BlackChannel]=20.0*log10((double) 1.0/sqrt(QuantumScale*
00957 distortion[BlackChannel]));
00958 distortion[AllChannels]=20.0*log10((double) 1.0/sqrt(QuantumScale*
00959 distortion[AllChannels]));
00960 return(status);
00961 }
00962
00963 static MagickBooleanType GetRootMeanSquaredError(const Image *image,
00964 const Image *reconstruct_image,const ChannelType channel,
00965 double *distortion,ExceptionInfo *exception)
00966 {
00967 MagickBooleanType
00968 status;
00969
00970 status=GetMeanSquaredError(image,reconstruct_image,channel,distortion,
00971 exception);
00972 if ((channel & RedChannel) != 0)
00973 distortion[RedChannel]=sqrt(QuantumRange*distortion[RedChannel]);
00974 if ((channel & GreenChannel) != 0)
00975 distortion[GreenChannel]=sqrt(QuantumRange*distortion[GreenChannel]);
00976 if ((channel & BlueChannel) != 0)
00977 distortion[BlueChannel]=sqrt(QuantumRange*distortion[BlueChannel]);
00978 if (((channel & OpacityChannel) != 0) &&
00979 (image->matte != MagickFalse))
00980 distortion[OpacityChannel]=sqrt(QuantumRange*distortion[OpacityChannel]);
00981 if (((channel & IndexChannel) != 0) &&
00982 (image->colorspace == CMYKColorspace))
00983 distortion[BlackChannel]=sqrt(QuantumRange*distortion[BlackChannel]);
00984 distortion[AllChannels]=sqrt(QuantumRange*distortion[AllChannels]);
00985 return(status);
00986 }
00987
00988 MagickExport MagickBooleanType GetImageChannelDistortion(Image *image,
00989 const Image *reconstruct_image,const ChannelType channel,
00990 const MetricType metric,double *distortion,ExceptionInfo *exception)
00991 {
00992 double
00993 *channel_distortion;
00994
00995 MagickBooleanType
00996 status;
00997
00998 size_t
00999 length;
01000
01001 assert(image != (Image *) NULL);
01002 assert(image->signature == MagickSignature);
01003 if (image->debug != MagickFalse)
01004 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01005 assert(reconstruct_image != (const Image *) NULL);
01006 assert(reconstruct_image->signature == MagickSignature);
01007 assert(distortion != (double *) NULL);
01008 *distortion=0.0;
01009 if (image->debug != MagickFalse)
01010 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01011 if ((reconstruct_image->columns != image->columns) ||
01012 (reconstruct_image->rows != image->rows))
01013 ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename);
01014
01015
01016
01017 length=AllChannels+1UL;
01018 channel_distortion=(double *) AcquireQuantumMemory(length,
01019 sizeof(*channel_distortion));
01020 if (channel_distortion == (double *) NULL)
01021 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
01022 (void) ResetMagickMemory(channel_distortion,0,length*
01023 sizeof(*channel_distortion));
01024 switch (metric)
01025 {
01026 case AbsoluteErrorMetric:
01027 {