return(status);
}
-static MagickBooleanType GetAbsoluteError(const Image *image,
+static MagickBooleanType GetAbsoluteDistortion(const Image *image,
const Image *reconstruct_image,const ChannelType channel,double *distortion,
ExceptionInfo *exception)
{
return(channels);
}
-static MagickBooleanType GetMeanAbsoluteError(const Image *image,
+static MagickBooleanType GetMeanAbsoluteDistortion(const Image *image,
const Image *reconstruct_image,const ChannelType channel,
double *distortion,ExceptionInfo *exception)
{
return(status);
}
-static MagickBooleanType GetMeanSquaredError(const Image *image,
+static MagickBooleanType GetMeanSquaredDistortion(const Image *image,
const Image *reconstruct_image,const ChannelType channel,
double *distortion,ExceptionInfo *exception)
{
return(status);
}
-static MagickBooleanType GetNormalizedCrossCorrelationError(const Image *image,
- const Image *reconstruct_image,const ChannelType channel,
+static MagickBooleanType GetNormalizedCrossCorrelationDistortion(
+ const Image *image,const Image *reconstruct_image,const ChannelType channel,
double *distortion,ExceptionInfo *exception)
{
#define SimilarityImageTag "Similarity/Image"
MagickOffsetType
progress;
- ssize_t
- y;
-
MagickBooleanType
status;
register ssize_t
i;
+ ssize_t
+ y;
+
/*
Subtract the mean.
*/
return(status);
}
-static MagickBooleanType GetPeakAbsoluteError(const Image *image,
+static MagickBooleanType GetPeakAbsoluteDistortion(const Image *image,
const Image *reconstruct_image,const ChannelType channel,
double *distortion,ExceptionInfo *exception)
{
MagickBooleanType
status;
- status=GetMeanSquaredError(image,reconstruct_image,channel,distortion,
+ status=GetMeanSquaredDistortion(image,reconstruct_image,channel,distortion,
exception);
if ((channel & RedChannel) != 0)
distortion[RedChannel]=20.0*log10((double) 1.0/sqrt(
return(status);
}
-static MagickBooleanType GetRootMeanSquaredError(const Image *image,
+static MagickBooleanType GetRootMeanSquaredDistortion(const Image *image,
const Image *reconstruct_image,const ChannelType channel,
double *distortion,ExceptionInfo *exception)
{
MagickBooleanType
status;
- status=GetMeanSquaredError(image,reconstruct_image,channel,distortion,
+ status=GetMeanSquaredDistortion(image,reconstruct_image,channel,distortion,
exception);
if ((channel & RedChannel) != 0)
distortion[RedChannel]=sqrt(distortion[RedChannel]);
{
case AbsoluteErrorMetric:
{
- status=GetAbsoluteError(image,reconstruct_image,channel,
+ status=GetAbsoluteDistortion(image,reconstruct_image,channel,
channel_distortion,exception);
break;
}
case MeanAbsoluteErrorMetric:
{
- status=GetMeanAbsoluteError(image,reconstruct_image,channel,
+ status=GetMeanAbsoluteDistortion(image,reconstruct_image,channel,
channel_distortion,exception);
break;
}
}
case MeanSquaredErrorMetric:
{
- status=GetMeanSquaredError(image,reconstruct_image,channel,
+ status=GetMeanSquaredDistortion(image,reconstruct_image,channel,
channel_distortion,exception);
break;
}
case NormalizedCrossCorrelationErrorMetric:
+ default:
{
- status=GetNormalizedCrossCorrelationError(image,reconstruct_image,channel,
- channel_distortion,exception);
+ status=GetNormalizedCrossCorrelationDistortion(image,reconstruct_image,
+ channel,channel_distortion,exception);
break;
}
case PeakAbsoluteErrorMetric:
- default:
{
- status=GetPeakAbsoluteError(image,reconstruct_image,channel,
+ status=GetPeakAbsoluteDistortion(image,reconstruct_image,channel,
channel_distortion,exception);
break;
}
}
case RootMeanSquaredErrorMetric:
{
- status=GetRootMeanSquaredError(image,reconstruct_image,channel,
+ status=GetRootMeanSquaredDistortion(image,reconstruct_image,channel,
channel_distortion,exception);
break;
}
{
case AbsoluteErrorMetric:
{
- status=GetAbsoluteError(image,reconstruct_image,AllChannels,
+ status=GetAbsoluteDistortion(image,reconstruct_image,AllChannels,
channel_distortion,exception);
break;
}
case MeanAbsoluteErrorMetric:
{
- status=GetMeanAbsoluteError(image,reconstruct_image,AllChannels,
+ status=GetMeanAbsoluteDistortion(image,reconstruct_image,AllChannels,
channel_distortion,exception);
break;
}
}
case MeanSquaredErrorMetric:
{
- status=GetMeanSquaredError(image,reconstruct_image,AllChannels,
+ status=GetMeanSquaredDistortion(image,reconstruct_image,AllChannels,
channel_distortion,exception);
break;
}
case NormalizedCrossCorrelationErrorMetric:
+ default:
{
- status=GetNormalizedCrossCorrelationError(image,reconstruct_image,
+ status=GetNormalizedCrossCorrelationDistortion(image,reconstruct_image,
AllChannels,channel_distortion,exception);
break;
}
case PeakAbsoluteErrorMetric:
- default:
{
- status=GetPeakAbsoluteError(image,reconstruct_image,AllChannels,
+ status=GetPeakAbsoluteDistortion(image,reconstruct_image,AllChannels,
channel_distortion,exception);
break;
}
}
case RootMeanSquaredErrorMetric:
{
- status=GetRootMeanSquaredError(image,reconstruct_image,AllChannels,
+ status=GetRootMeanSquaredDistortion(image,reconstruct_image,AllChannels,
channel_distortion,exception);
break;
}
%
*/
+static double GetNCCDistortion(const Image *image,
+ const Image *reconstruct_image,
+ const ChannelStatistics *reconstruct_statistics,ExceptionInfo *exception)
+{
+#define SimilarityImageTag "Similarity/Image"
+
+ CacheView
+ *image_view,
+ *reconstruct_view;
+
+ ChannelStatistics
+ *image_statistics;
+
+ double
+ distortion;
+
+ MagickBooleanType
+ status;
+
+ MagickRealType
+ alpha,
+ area;
+
+ ssize_t
+ y;
+
+ unsigned long
+ number_channels;
+
+ /*
+ Subtract the mean.
+ */
+ image_statistics=GetImageChannelStatistics(image,exception);
+ status=MagickTrue;
+ distortion=0.0;
+ area=1.0/((MagickRealType) image->columns*image->rows);
+ image_view=AcquireCacheView(image);
+ reconstruct_view=AcquireCacheView(reconstruct_image);
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ register const IndexPacket
+ *restrict indexes,
+ *restrict reconstruct_indexes;
+
+ register const PixelPacket
+ *restrict p,
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reconstruct_view,0,y,reconstruct_image->columns,
+ 1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reconstruct_indexes=GetCacheViewVirtualIndexQueue(reconstruct_view);
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ distortion+=area*QuantumScale*(p->red-
+ image_statistics[RedChannel].mean)*(q->red-
+ reconstruct_statistics[RedChannel].mean);
+ distortion+=area*QuantumScale*(p->green-
+ image_statistics[GreenChannel].mean)*(q->green-
+ reconstruct_statistics[GreenChannel].mean);
+ distortion+=area*QuantumScale*(p->blue-
+ image_statistics[BlueChannel].mean)*(q->blue-
+ reconstruct_statistics[BlueChannel].mean);
+ if (image->matte != MagickFalse)
+ distortion+=area*QuantumScale*(p->opacity-
+ image_statistics[OpacityChannel].mean)*(q->opacity-
+ reconstruct_statistics[OpacityChannel].mean);
+ if ((image->colorspace == CMYKColorspace) &&
+ (reconstruct_image->colorspace == CMYKColorspace))
+ distortion+=area*QuantumScale*(indexes[x]-
+ image_statistics[OpacityChannel].mean)*(reconstruct_indexes[x]-
+ reconstruct_statistics[OpacityChannel].mean);
+ p++;
+ q++;
+ }
+ }
+ reconstruct_view=DestroyCacheView(reconstruct_view);
+ image_view=DestroyCacheView(image_view);
+ /*
+ Divide by the standard deviation.
+ */
+ alpha=image_statistics[AllChannels].standard_deviation*
+ reconstruct_statistics[AllChannels].standard_deviation;
+ distortion=QuantumRange*distortion/alpha;
+ number_channels=3;
+ if (image->matte != MagickFalse)
+ number_channels++;
+ if (image->colorspace == CMYKColorspace)
+ number_channels++;
+ distortion=sqrt(distortion/number_channels);
+ if (fabs(alpha) <= MagickEpsilon)
+ distortion=1.0;
+ /*
+ Free resources.
+ */
+ image_statistics=(ChannelStatistics *) RelinquishMagickMemory(
+ image_statistics);
+ return(1.0-distortion);
+}
+
static double GetSimilarityMetric(const Image *image,const Image *reference,
- const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
+ const ChannelStatistics *reference_statistics,const ssize_t x_offset,
+ const ssize_t y_offset,ExceptionInfo *exception)
{
double
- channel_distortion[AllChannels+1];
+ distortion;
Image
*similarity_image;
-
- MagickBooleanType
- status;
RectangleInfo
geometry;
similarity_image=CropImage(image,&geometry,exception);
if (similarity_image == (Image *) NULL)
return(0.0);
- status=GetNormalizedCrossCorrelationError(reference,similarity_image,
- DefaultChannels,channel_distortion,exception);
+ distortion=GetNCCDistortion(reference,similarity_image,reference_statistics,
+ exception);
similarity_image=DestroyImage(similarity_image);
- if (status == MagickFalse)
- return(0.0);
- return(1.0-channel_distortion[AllChannels]);
+ return(distortion);
}
MagickExport Image *SimilarityImage(Image *image,const Image *reference,
CacheView
*similarity_view;
+ ChannelStatistics
+ *reference_statistics;
+
Image
*similarity_image;
*/
status=MagickTrue;
progress=0;
+ reference_statistics=GetImageChannelStatistics(reference,exception);
similarity_view=AcquireCacheView(similarity_image);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(dynamic,4) shared(progress,status)
if (status == MagickFalse)
continue;
- q=GetCacheViewAuthenticPixels(similarity_view,0,y,
- similarity_image->columns,1,exception);
+ q=GetCacheViewAuthenticPixels(similarity_view,0,y,similarity_image->columns,
+ 1,exception);
if (q == (const PixelPacket *) NULL)
{
status=MagickFalse;
}
for (x=0; x < (ssize_t) (image->columns-reference->columns+1); x++)
{
- similarity=GetSimilarityMetric(image,reference,x,y,exception);
+ similarity=GetSimilarityMetric(image,reference,reference_statistics,x,y,
+ exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp critical (MagickCore_SimilarityImage)
#endif
}
}
similarity_view=DestroyCacheView(similarity_view);
+ reference_statistics=(ChannelStatistics *) RelinquishMagickMemory(
+ reference_statistics);
return(similarity_image);
}