% MagickCore Image Statistical Methods %
% %
% Software Design %
-% John Cristy %
+% Cristy %
% July 1992 %
% %
% %
-% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
typedef struct _PixelChannels
{
- MagickRealType
+ double
channel[CompositePixelChannel];
} PixelChannels;
length,
number_threads;
- number_threads=GetOpenMPMaximumThreads();
+ number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
pixels=(PixelChannels **) AcquireQuantumMemory(number_threads,
sizeof(*pixels));
if (pixels == (PixelChannels **) NULL)
*color_1,
*color_2;
- MagickRealType
+ double
distance;
register ssize_t
color_2=(const PixelChannels *) y;
distance=0.0;
for (i=0; i < MaxPixelChannels; i++)
- distance+=color_1->channel[i]-(MagickRealType) color_2->channel[i];
+ distance+=color_1->channel[i]-(double) color_2->channel[i];
return(distance < 0 ? -1 : distance > 0 ? 1 : 0);
}
return(y);
}
-static MagickRealType ApplyEvaluateOperator(RandomInfo *random_info,
- Quantum pixel,const MagickEvaluateOperator op,const MagickRealType value)
+static double ApplyEvaluateOperator(RandomInfo *random_info,const Quantum pixel,
+ const MagickEvaluateOperator op,const double value)
{
- MagickRealType
+ double
result;
result=0.0;
break;
case AbsEvaluateOperator:
{
- result=(MagickRealType) fabs((double) (pixel+value));
+ result=(double) fabs((double) (pixel+value));
break;
}
case AddEvaluateOperator:
{
- result=(MagickRealType) (pixel+value);
+ result=(double) (pixel+value);
break;
}
case AddModulusEvaluateOperator:
}
case AndEvaluateOperator:
{
- result=(MagickRealType) ((size_t) pixel & (size_t) (value+0.5));
+ result=(double) ((size_t) pixel & (size_t) (value+0.5));
break;
}
case CosineEvaluateOperator:
{
- result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
+ result=(double) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
QuantumScale*pixel*value))+0.5));
break;
}
}
case ExponentialEvaluateOperator:
{
- result=(MagickRealType) (QuantumRange*exp((double) (value*QuantumScale*
- pixel)));
+ result=(double) (QuantumRange*exp((double) (value*QuantumScale*pixel)));
break;
}
case GaussianNoiseEvaluateOperator:
{
- result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ result=(double) GenerateDifferentialNoise(random_info,pixel,
GaussianNoise,value);
break;
}
case ImpulseNoiseEvaluateOperator:
{
- result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
- ImpulseNoise,value);
+ result=(double) GenerateDifferentialNoise(random_info,pixel,ImpulseNoise,
+ value);
break;
}
case LaplacianNoiseEvaluateOperator:
{
- result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ result=(double) GenerateDifferentialNoise(random_info,pixel,
LaplacianNoise,value);
break;
}
case LeftShiftEvaluateOperator:
{
- result=(MagickRealType) ((size_t) pixel << (size_t) (value+0.5));
+ result=(double) ((size_t) pixel << (size_t) (value+0.5));
break;
}
case LogEvaluateOperator:
{
if ((QuantumScale*pixel) >= MagickEpsilon)
- result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
- pixel+1.0))/log((double) (value+1.0)));
+ result=(double) (QuantumRange*log((double) (QuantumScale*value*pixel+
+ 1.0))/log((double) (value+1.0)));
break;
}
case MaxEvaluateOperator:
{
- result=(MagickRealType) EvaluateMax((double) pixel,value);
+ result=(double) EvaluateMax((double) pixel,value);
break;
}
case MeanEvaluateOperator:
{
- result=(MagickRealType) (pixel+value);
+ result=(double) (pixel+value);
break;
}
case MedianEvaluateOperator:
{
- result=(MagickRealType) (pixel+value);
+ result=(double) (pixel+value);
break;
}
case MinEvaluateOperator:
{
- result=(MagickRealType) MagickMin((double) pixel,value);
+ result=(double) MagickMin((double) pixel,value);
break;
}
case MultiplicativeNoiseEvaluateOperator:
{
- result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ result=(double) GenerateDifferentialNoise(random_info,pixel,
MultiplicativeGaussianNoise,value);
break;
}
case MultiplyEvaluateOperator:
{
- result=(MagickRealType) (value*pixel);
+ result=(double) (value*pixel);
break;
}
case OrEvaluateOperator:
{
- result=(MagickRealType) ((size_t) pixel | (size_t) (value+0.5));
+ result=(double) ((size_t) pixel | (size_t) (value+0.5));
break;
}
case PoissonNoiseEvaluateOperator:
{
- result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
- PoissonNoise,value);
+ result=(double) GenerateDifferentialNoise(random_info,pixel,PoissonNoise,
+ value);
break;
}
case PowEvaluateOperator:
{
- result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
- (double) value));
+ result=(double) (QuantumRange*pow((double) (QuantumScale*pixel),(double)
+ value));
break;
}
case RightShiftEvaluateOperator:
{
- result=(MagickRealType) ((size_t) pixel >> (size_t) (value+0.5));
+ result=(double) ((size_t) pixel >> (size_t) (value+0.5));
break;
}
case SetEvaluateOperator:
}
case SineEvaluateOperator:
{
- result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
+ result=(double) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
QuantumScale*pixel*value))+0.5));
break;
}
case SubtractEvaluateOperator:
{
- result=(MagickRealType) (pixel-value);
+ result=(double) (pixel-value);
break;
}
case SumEvaluateOperator:
{
- result=(MagickRealType) (pixel+value);
+ result=(double) (pixel+value);
break;
}
case ThresholdEvaluateOperator:
{
- result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
- QuantumRange);
+ result=(double) (((double) pixel <= value) ? 0 : QuantumRange);
break;
}
case ThresholdBlackEvaluateOperator:
{
- result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
+ result=(double) (((double) pixel <= value) ? 0 : pixel);
break;
}
case ThresholdWhiteEvaluateOperator:
{
- result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
- pixel);
+ result=(double) (((double) pixel > value) ? QuantumRange : pixel);
break;
}
case UniformNoiseEvaluateOperator:
{
- result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
- UniformNoise,value);
+ result=(double) GenerateDifferentialNoise(random_info,pixel,UniformNoise,
+ value);
break;
}
case XorEvaluateOperator:
{
- result=(MagickRealType) ((size_t) pixel ^ (size_t) (value+0.5));
+ result=(double) ((size_t) pixel ^ (size_t) (value+0.5));
break;
}
}
CacheView
*evaluate_view;
- const Image
- *next;
-
Image
*image;
ssize_t
y;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
unsigned long
key;
+#endif
- /*
- Ensure the image are the same size.
- */
assert(images != (Image *) NULL);
assert(images->signature == MagickSignature);
if (images->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
- for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
- if ((next->columns != images->columns) || (next->rows != images->rows))
- {
- (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
- "ImageWidthsOrHeightsDiffer","'%s'",images->filename);
- return((Image *) NULL);
- }
- /*
- Initialize evaluate next attributes.
- */
image=CloneImage(images,images->columns,images->rows,MagickTrue,
exception);
if (image == (Image *) NULL)
{
image=DestroyImage(image);
(void) ThrowMagickException(exception,GetMagickModule(),
- ResourceLimitError,"MemoryAllocationFailed","'%s'",images->filename);
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
return((Image *) NULL);
}
/*
status=MagickTrue;
progress=0;
random_info=AcquireRandomInfoThreadSet();
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
key=GetRandomSecretKey(random_info[0]);
+#endif
evaluate_view=AcquireAuthenticCacheView(image,exception);
if (op == MedianEvaluateOperator)
{
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static) shared(progress,status) \
- dynamic_number_threads(image,image->columns,image->rows,key == ~0UL)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ magick_threads(image,images,image->rows,key == ~0UL)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
if (status == MagickFalse)
continue;
- q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,
- image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
+ exception);
if (q == (Quantum *) NULL)
{
status=MagickFalse;
}
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- evaluate_traits,
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- evaluate_traits=GetPixelChannelMapTraits(image,channel);
- traits=GetPixelChannelMapTraits(next,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait evaluate_traits=GetPixelChannelTraits(image,channel);
+ PixelTrait traits=GetPixelChannelTraits(next,channel);
if ((traits == UndefinedPixelTrait) ||
(evaluate_traits == UndefinedPixelTrait))
continue;
else
{
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static) shared(progress,status) \
- dynamic_number_threads(image,image->columns,image->rows,key == ~0UL)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ magick_threads(image,images,image->rows,key == ~0UL)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
if (status == MagickFalse)
continue;
- q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,
- image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(evaluate_view,0,y,image->columns,1,
+ exception);
if (q == (Quantum *) NULL)
{
status=MagickFalse;
register ssize_t
i;
- if (GetPixelMask(next,p) != 0)
+ if (GetPixelReadMask(next,p) == 0)
{
p+=GetPixelChannels(next);
continue;
}
for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- evaluate_traits,
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(next,channel);
- evaluate_traits=GetPixelChannelMapTraits(image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(next,channel);
+ PixelTrait evaluate_traits=GetPixelChannelTraits(image,channel);
if ((traits == UndefinedPixelTrait) ||
(evaluate_traits == UndefinedPixelTrait))
continue;
if ((traits & UpdatePixelTrait) == 0)
continue;
evaluate_pixel[x].channel[i]=ApplyEvaluateOperator(
- random_info[id],GetPixelChannel(image,channel,p),j ==
- 0 ? AddEvaluateOperator : op,evaluate_pixel[x].channel[i]);
+ random_info[id],GetPixelChannel(image,channel,p),j == 0 ?
+ AddEvaluateOperator : op,evaluate_pixel[x].channel[i]);
}
p+=GetPixelChannels(next);
}
case MeanEvaluateOperator:
{
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
- evaluate_pixel[x].channel[i]/=(MagickRealType) number_images;
+ evaluate_pixel[x].channel[i]/=(double) number_images;
break;
}
case MultiplyEvaluateOperator:
register ssize_t
i;
- if (GetPixelMask(image,q) != 0)
+ if (GetPixelReadMask(image,q) == 0)
{
q+=GetPixelChannels(image);
continue;
}
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
if (traits == UndefinedPixelTrait)
continue;
if ((traits & UpdatePixelTrait) == 0)
ssize_t
y;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
unsigned long
key;
+#endif
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
status=MagickTrue;
progress=0;
random_info=AcquireRandomInfoThreadSet();
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
key=GetRandomSecretKey(random_info[0]);
+#endif
image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(static,4) shared(progress,status) \
- dynamic_number_threads(image,image->columns,image->rows,key == ~0UL)
+ magick_threads(image,image,image->rows,key == ~0UL)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
register ssize_t
i;
- if (GetPixelMask(image,q) != 0)
- {
- q+=GetPixelChannels(image);
- continue;
- }
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
if (traits == UndefinedPixelTrait)
continue;
- if ((traits & CopyPixelTrait) != 0)
+ if (((traits & CopyPixelTrait) != 0) ||
+ (GetPixelReadMask(image,q) == 0))
continue;
q[i]=ClampToQuantum(ApplyEvaluateOperator(random_info[id],q[i],op,
value));
const size_t number_parameters,const double *parameters,
ExceptionInfo *exception)
{
- MagickRealType
+ double
result;
register ssize_t
}
case SinusoidFunction:
{
- MagickRealType
+ double
amplitude,
bias,
frequency,
phase=(number_parameters >= 2) ? parameters[1] : 0.0;
amplitude=(number_parameters >= 3) ? parameters[2] : 0.5;
bias=(number_parameters >= 4) ? parameters[3] : 0.5;
- result=(MagickRealType) (QuantumRange*(amplitude*sin((double) (2.0*
+ result=(double) (QuantumRange*(amplitude*sin((double) (2.0*
MagickPI*(frequency*QuantumScale*pixel+phase/360.0)))+bias));
break;
}
case ArcsinFunction:
{
- MagickRealType
+ double
bias,
center,
range,
if (result >= 1.0)
result=bias+range/2.0;
else
- result=(MagickRealType) (range/MagickPI*asin((double) result)+bias);
+ result=(double) (range/MagickPI*asin((double) result)+bias);
result*=QuantumRange;
break;
}
case ArctanFunction:
{
- MagickRealType
+ double
center,
bias,
range,
center=(number_parameters >= 2) ? parameters[1] : 0.5;
range=(number_parameters >= 3) ? parameters[2] : 1.0;
bias=(number_parameters >= 4) ? parameters[3] : 0.5;
- result=(MagickRealType) (MagickPI*slope*(QuantumScale*pixel-center));
- result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
+ result=(double) (MagickPI*slope*(QuantumScale*pixel-center));
+ result=(double) (QuantumRange*(range/MagickPI*atan((double)
result)+bias));
break;
}
image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(static,4) shared(progress,status) \
- dynamic_number_threads(image,image->columns,image->rows,1)
+ magick_threads(image,image,image->rows,1)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
register ssize_t
i;
- if (GetPixelMask(image,q) != 0)
+ if (GetPixelReadMask(image,q) == 0)
{
q+=GetPixelChannels(image);
continue;
}
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
if (traits == UndefinedPixelTrait)
continue;
if ((traits & UpdatePixelTrait) == 0)
% %
% %
% %
+% G e t I m a g e K u r t o s i s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageKurtosis() returns the kurtosis and skewness of one or more image
+% channels.
+%
+% The format of the GetImageKurtosis method is:
+%
+% MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
+% double *skewness,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o kurtosis: the kurtosis of the channel.
+%
+% o skewness: the skewness of the channel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
+ double *kurtosis,double *skewness,ExceptionInfo *exception)
+{
+ CacheView
+ *image_view;
+
+ double
+ area,
+ mean,
+ standard_deviation,
+ sum_squares,
+ sum_cubes,
+ sum_fourth_power;
+
+ MagickBooleanType
+ status;
+
+ ssize_t
+ y;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ status=MagickTrue;
+ *kurtosis=0.0;
+ *skewness=0.0;
+ area=0.0;
+ mean=0.0;
+ standard_deviation=0.0;
+ sum_squares=0.0;
+ sum_cubes=0.0;
+ sum_fourth_power=0.0;
+ image_view=AcquireVirtualCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(image,image,image->rows,1)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ register const Quantum
+ *restrict p;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const Quantum *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ register ssize_t
+ i;
+
+ if (GetPixelReadMask(image,p) == 0)
+ {
+ p+=GetPixelChannels(image);
+ continue;
+ }
+ for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+ {
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ if (traits == UndefinedPixelTrait)
+ continue;
+ if ((traits & UpdatePixelTrait) == 0)
+ continue;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_GetImageKurtosis)
+#endif
+ {
+ mean+=p[i];
+ sum_squares+=(double) p[i]*p[i];
+ sum_cubes+=(double) p[i]*p[i]*p[i];
+ sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
+ area++;
+ }
+ }
+ p+=GetPixelChannels(image);
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ if (area != 0.0)
+ {
+ mean/=area;
+ sum_squares/=area;
+ sum_cubes/=area;
+ sum_fourth_power/=area;
+ }
+ standard_deviation=sqrt(sum_squares-(mean*mean));
+ if (standard_deviation != 0.0)
+ {
+ *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
+ 3.0*mean*mean*mean*mean;
+ *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
+ standard_deviation;
+ *kurtosis-=3.0;
+ *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
+ *skewness/=standard_deviation*standard_deviation*standard_deviation;
+ }
+ return(status);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
% G e t I m a g e M e a n %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% GetImageMean() returns the mean and standard deviation of one or more
-% image channels.
+% GetImageMean() returns the mean and standard deviation of one or more image
+% channels.
%
% The format of the GetImageMean method is:
%
MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
double *standard_deviation,ExceptionInfo *exception)
{
+ double
+ area;
+
ChannelStatistics
*channel_statistics;
register ssize_t
i;
- size_t
- area;
-
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
if (image->debug != MagickFalse)
channel_statistics=GetImageStatistics(image,exception);
if (channel_statistics == (ChannelStatistics *) NULL)
return(MagickFalse);
- area=0;
+ area=0.0;
channel_statistics[CompositePixelChannel].mean=0.0;
channel_statistics[CompositePixelChannel].standard_deviation=0.0;
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
if (traits == UndefinedPixelTrait)
continue;
if ((traits & UpdatePixelTrait) == 0)
% %
% %
% %
-% G e t I m a g e K u r t o s i s %
+% G e t I m a g e M o m e n t s %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% GetImageKurtosis() returns the kurtosis and skewness of one or more
-% image channels.
+% GetImageMoments() returns the normalized moments of one or more image
+% channels.
%
-% The format of the GetImageKurtosis method is:
+% The format of the GetImageMoments method is:
%
-% MagickBooleanType GetImageKurtosis(const Image *image,double *kurtosis,
-% double *skewness,ExceptionInfo *exception)
+% ChannelMoments *GetImageMoments(const Image *image,
+% ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the image.
%
-% o kurtosis: the kurtosis of the channel.
-%
-% o skewness: the skewness of the channel.
-%
% o exception: return any errors or warnings in this structure.
%
*/
-MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
- double *kurtosis,double *skewness,ExceptionInfo *exception)
+MagickExport ChannelMoments *GetImageMoments(const Image *image,
+ ExceptionInfo *exception)
{
+#define MaxNumberImageMoments 8
+
CacheView
*image_view;
- double
- area,
- mean,
- standard_deviation,
- sum_squares,
- sum_cubes,
- sum_fourth_power;
+ ChannelMoments
+ *channel_moments;
- MagickBooleanType
- status;
+ double
+ M00[MaxPixelChannels+1],
+ M01[MaxPixelChannels+1],
+ M02[MaxPixelChannels+1],
+ M03[MaxPixelChannels+1],
+ M10[MaxPixelChannels+1],
+ M11[MaxPixelChannels+1],
+ M12[MaxPixelChannels+1],
+ M20[MaxPixelChannels+1],
+ M21[MaxPixelChannels+1],
+ M22[MaxPixelChannels+1],
+ M30[MaxPixelChannels+1];
+
+ PointInfo
+ centroid[MaxPixelChannels+1];
ssize_t
+ channel,
y;
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
- status=MagickTrue;
- *kurtosis=0.0;
- *skewness=0.0;
- area=0.0;
- mean=0.0;
- standard_deviation=0.0;
- sum_squares=0.0;
- sum_cubes=0.0;
- sum_fourth_power=0.0;
+ channel_moments=(ChannelMoments *) AcquireQuantumMemory(MaxPixelChannels+1,
+ sizeof(*channel_moments));
+ if (channel_moments == (ChannelMoments *) NULL)
+ return(channel_moments);
+ (void) ResetMagickMemory(channel_moments,0,(MaxPixelChannels+1)*
+ sizeof(*channel_moments));
+ (void) ResetMagickMemory(centroid,0,sizeof(centroid));
+ (void) ResetMagickMemory(M00,0,sizeof(M00));
+ (void) ResetMagickMemory(M01,0,sizeof(M01));
+ (void) ResetMagickMemory(M02,0,sizeof(M02));
+ (void) ResetMagickMemory(M03,0,sizeof(M03));
+ (void) ResetMagickMemory(M10,0,sizeof(M10));
+ (void) ResetMagickMemory(M11,0,sizeof(M11));
+ (void) ResetMagickMemory(M12,0,sizeof(M12));
+ (void) ResetMagickMemory(M20,0,sizeof(M20));
+ (void) ResetMagickMemory(M21,0,sizeof(M21));
+ (void) ResetMagickMemory(M22,0,sizeof(M22));
+ (void) ResetMagickMemory(M30,0,sizeof(M30));
image_view=AcquireVirtualCacheView(image,exception);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static) shared(status) \
- dynamic_number_threads(image,image->columns,image->rows,1)
-#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
register const Quantum
register ssize_t
x;
- if (status == MagickFalse)
- continue;
+ /*
+ Compute center of mass (centroid).
+ */
p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
if (p == (const Quantum *) NULL)
+ break;
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ register ssize_t
+ i;
+
+ if (GetPixelReadMask(image,p) == 0)
+ {
+ p+=GetPixelChannels(image);
+ continue;
+ }
+ for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- status=MagickFalse;
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ if (traits == UndefinedPixelTrait)
+ continue;
+ if ((traits & UpdatePixelTrait) == 0)
+ continue;
+ M00[channel]+=QuantumScale*p[i];
+ M00[MaxPixelChannels]+=QuantumScale*p[i];
+ M10[channel]+=x*QuantumScale*p[i];
+ M10[MaxPixelChannels]+=QuantumScale*p[i];
+ M01[channel]+=y*QuantumScale*p[i];
+ M01[MaxPixelChannels]+=QuantumScale*p[i];
+ }
+ p+=GetPixelChannels(image);
+ }
+ }
+ for (channel=0; channel <= MaxPixelChannels; channel++)
+ {
+ /*
+ Compute center of mass (centroid).
+ */
+ if (M00[channel] < MagickEpsilon)
+ {
+ M00[channel]+=MagickEpsilon;
+ centroid[channel].x=image->columns/2.0;
+ centroid[channel].y=image->rows/2.0;
continue;
}
+ M00[channel]+=MagickEpsilon;
+ centroid[channel].x=M10[channel]/M00[channel];
+ centroid[channel].y=M01[channel]/M00[channel];
+ }
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ register const Quantum
+ *restrict p;
+
+ register ssize_t
+ x;
+
+ /*
+ Compute the image moments.
+ */
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const Quantum *) NULL)
+ break;
for (x=0; x < (ssize_t) image->columns; x++)
{
register ssize_t
i;
- if (GetPixelMask(image,p) != 0)
+ if (GetPixelReadMask(image,p) == 0)
{
p+=GetPixelChannels(image);
continue;
}
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
if (traits == UndefinedPixelTrait)
continue;
if ((traits & UpdatePixelTrait) == 0)
continue;
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_GetImageKurtosis)
-#endif
- {
- mean+=p[i];
- sum_squares+=(double) p[i]*p[i];
- sum_cubes+=(double) p[i]*p[i]*p[i];
- sum_fourth_power+=(double) p[i]*p[i]*p[i]*p[i];
- area++;
- }
+ M11[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
+ QuantumScale*p[i];
+ M11[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
+ QuantumScale*p[i];
+ M20[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
+ QuantumScale*p[i];
+ M20[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
+ QuantumScale*p[i];
+ M02[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
+ QuantumScale*p[i];
+ M02[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
+ QuantumScale*p[i];
+ M21[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
+ (y-centroid[channel].y)*QuantumScale*p[i];
+ M21[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
+ (y-centroid[channel].y)*QuantumScale*p[i];
+ M12[channel]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
+ (y-centroid[channel].y)*QuantumScale*p[i];
+ M12[MaxPixelChannels]+=(x-centroid[channel].x)*(y-centroid[channel].y)*
+ (y-centroid[channel].y)*QuantumScale*p[i];
+ M22[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
+ (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
+ M22[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
+ (y-centroid[channel].y)*(y-centroid[channel].y)*QuantumScale*p[i];
+ M30[channel]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
+ (x-centroid[channel].x)*QuantumScale*p[i];
+ M30[MaxPixelChannels]+=(x-centroid[channel].x)*(x-centroid[channel].x)*
+ (x-centroid[channel].x)*QuantumScale*p[i];
+ M03[channel]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
+ (y-centroid[channel].y)*QuantumScale*p[i];
+ M03[MaxPixelChannels]+=(y-centroid[channel].y)*(y-centroid[channel].y)*
+ (y-centroid[channel].y)*QuantumScale*p[i];
}
p+=GetPixelChannels(image);
}
}
+ M00[MaxPixelChannels]/=GetPixelChannels(image);
+ M01[MaxPixelChannels]/=GetPixelChannels(image);
+ M02[MaxPixelChannels]/=GetPixelChannels(image);
+ M03[MaxPixelChannels]/=GetPixelChannels(image);
+ M10[MaxPixelChannels]/=GetPixelChannels(image);
+ M11[MaxPixelChannels]/=GetPixelChannels(image);
+ M12[MaxPixelChannels]/=GetPixelChannels(image);
+ M20[MaxPixelChannels]/=GetPixelChannels(image);
+ M21[MaxPixelChannels]/=GetPixelChannels(image);
+ M22[MaxPixelChannels]/=GetPixelChannels(image);
+ M30[MaxPixelChannels]/=GetPixelChannels(image);
+ for (channel=0; channel <= MaxPixelChannels; channel++)
+ {
+ /*
+ Compute elliptical angle, major and minor axes, eccentricity, & intensity.
+ */
+ channel_moments[channel].centroid=centroid[channel];
+ channel_moments[channel].ellipse_axis.x=sqrt((2.0/M00[channel])*
+ ((M20[channel]+M02[channel])+sqrt(4.0*M11[channel]*M11[channel]+
+ (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
+ channel_moments[channel].ellipse_axis.y=sqrt((2.0/M00[channel])*
+ ((M20[channel]+M02[channel])-sqrt(4.0*M11[channel]*M11[channel]+
+ (M20[channel]-M02[channel])*(M20[channel]-M02[channel]))));
+ channel_moments[channel].ellipse_angle=RadiansToDegrees(0.5*atan(2.0*
+ M11[channel]/(M20[channel]-M02[channel]+MagickEpsilon)));
+ channel_moments[channel].ellipse_eccentricity=sqrt(1.0-(
+ channel_moments[channel].ellipse_axis.y/
+ (channel_moments[channel].ellipse_axis.x+MagickEpsilon)));
+ channel_moments[channel].ellipse_intensity=M00[channel]/
+ (MagickPI*channel_moments[channel].ellipse_axis.x*
+ channel_moments[channel].ellipse_axis.y+MagickEpsilon);
+ }
+ for (channel=0; channel <= MaxPixelChannels; channel++)
+ {
+ /*
+ Normalize image moments.
+ */
+ M10[channel]=0.0;
+ M01[channel]=0.0;
+ M11[channel]/=pow(M00[channel],1.0+(1.0+1.0)/2.0);
+ M20[channel]/=pow(M00[channel],1.0+(2.0+0.0)/2.0);
+ M02[channel]/=pow(M00[channel],1.0+(0.0+2.0)/2.0);
+ M21[channel]/=pow(M00[channel],1.0+(2.0+1.0)/2.0);
+ M12[channel]/=pow(M00[channel],1.0+(1.0+2.0)/2.0);
+ M22[channel]/=pow(M00[channel],1.0+(2.0+2.0)/2.0);
+ M30[channel]/=pow(M00[channel],1.0+(3.0+0.0)/2.0);
+ M03[channel]/=pow(M00[channel],1.0+(0.0+3.0)/2.0);
+ M00[channel]=1.0;
+ }
image_view=DestroyCacheView(image_view);
- if (area != 0.0)
+ for (channel=0; channel <= MaxPixelChannels; channel++)
+ {
+ /*
+ Compute Hu invariant moments.
+ */
+ channel_moments[channel].I[0]=M20[channel]+M02[channel];
+ channel_moments[channel].I[1]=(M20[channel]-M02[channel])*
+ (M20[channel]-M02[channel])+4.0*M11[channel]*M11[channel];
+ channel_moments[channel].I[2]=(M30[channel]-3.0*M12[channel])*
+ (M30[channel]-3.0*M12[channel])+(3.0*M21[channel]-M03[channel])*
+ (3.0*M21[channel]-M03[channel]);
+ channel_moments[channel].I[3]=(M30[channel]+M12[channel])*
+ (M30[channel]+M12[channel])+(M21[channel]+M03[channel])*
+ (M21[channel]+M03[channel]);
+ channel_moments[channel].I[4]=(M30[channel]-3.0*M12[channel])*
+ (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
+ (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
+ (M21[channel]+M03[channel]))+(3.0*M21[channel]-M03[channel])*
+ (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
+ (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
+ (M21[channel]+M03[channel]));
+ channel_moments[channel].I[5]=(M20[channel]-M02[channel])*
+ ((M30[channel]+M12[channel])*(M30[channel]+M12[channel])-
+ (M21[channel]+M03[channel])*(M21[channel]+M03[channel]))+
+ 4.0*M11[channel]*(M30[channel]+M12[channel])*(M21[channel]+M03[channel]);
+ channel_moments[channel].I[6]=(3.0*M21[channel]-M03[channel])*
+ (M30[channel]+M12[channel])*((M30[channel]+M12[channel])*
+ (M30[channel]+M12[channel])-3.0*(M21[channel]+M03[channel])*
+ (M21[channel]+M03[channel]))-(M30[channel]-3*M12[channel])*
+ (M21[channel]+M03[channel])*(3.0*(M30[channel]+M12[channel])*
+ (M30[channel]+M12[channel])-(M21[channel]+M03[channel])*
+ (M21[channel]+M03[channel]));
+ channel_moments[channel].I[7]=M11[channel]*((M30[channel]+M12[channel])*
+ (M30[channel]+M12[channel])-(M03[channel]+M21[channel])*
+ (M03[channel]+M21[channel]))-(M20[channel]-M02[channel])*
+ (M30[channel]+M12[channel])*(M03[channel]+M21[channel]);
+ }
+ if (y < (ssize_t) image->rows)
+ channel_moments=(ChannelMoments *) RelinquishMagickMemory(channel_moments);
+ return(channel_moments);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C h a n n e l P e r c e p t u a l H a s h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImagePerceptualHash() returns the perceptual hash of one or more
+% image channels.
+%
+% The format of the GetImagePerceptualHash method is:
+%
+% ChannelPerceptualHash *GetImagePerceptualHash(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline double MagickLog10(const double x)
+{
+#define Log10Epsilon (1.0e-11)
+
+ if (fabs(x) < Log10Epsilon)
+ return(log10(Log10Epsilon));
+ return(log10(fabs(x)));
+}
+
+MagickExport ChannelPerceptualHash *GetImagePerceptualHash(
+ const Image *image,ExceptionInfo *exception)
+{
+ ChannelMoments
+ *moments;
+
+ ChannelPerceptualHash
+ *perceptual_hash;
+
+ Image
+ *hash_image;
+
+ MagickBooleanType
+ status;
+
+ register ssize_t
+ i;
+
+ ssize_t
+ channel;
+
+ /*
+ Blur then transform to sRGB colorspace.
+ */
+ hash_image=BlurImage(image,0.0,1.0,exception);
+ if (hash_image == (Image *) NULL)
+ return((ChannelPerceptualHash *) NULL);
+ hash_image->depth=8;
+ status=TransformImageColorspace(hash_image,sRGBColorspace,exception);
+ if (status == MagickFalse)
+ return((ChannelPerceptualHash *) NULL);
+ moments=GetImageMoments(hash_image,exception);
+ hash_image=DestroyImage(hash_image);
+ if (moments == (ChannelMoments *) NULL)
+ return((ChannelPerceptualHash *) NULL);
+ perceptual_hash=(ChannelPerceptualHash *) AcquireQuantumMemory(
+ CompositeChannels+1UL,sizeof(*perceptual_hash));
+ if (perceptual_hash == (ChannelPerceptualHash *) NULL)
+ return((ChannelPerceptualHash *) NULL);
+ for (channel=0; channel <= MaxPixelChannels; channel++)
+ for (i=0; i < 7; i++)
+ perceptual_hash[channel].P[i]=(-MagickLog10(moments[channel].I[i]));
+ moments=(ChannelMoments *) RelinquishMagickMemory(moments);
+ /*
+ Blur then transform to HCLp colorspace.
+ */
+ hash_image=BlurImage(image,0.0,1.0,exception);
+ if (hash_image == (Image *) NULL)
{
- mean/=area;
- sum_squares/=area;
- sum_cubes/=area;
- sum_fourth_power/=area;
+ perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
+ perceptual_hash);
+ return((ChannelPerceptualHash *) NULL);
}
- standard_deviation=sqrt(sum_squares-(mean*mean));
- if (standard_deviation != 0.0)
+ hash_image->depth=8;
+ status=TransformImageColorspace(hash_image,HCLpColorspace,exception);
+ if (status == MagickFalse)
{
- *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
- 3.0*mean*mean*mean*mean;
- *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
- standard_deviation;
- *kurtosis-=3.0;
- *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
- *skewness/=standard_deviation*standard_deviation*standard_deviation;
+ perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
+ perceptual_hash);
+ return((ChannelPerceptualHash *) NULL);
}
- return(status);
+ moments=GetImageMoments(hash_image,exception);
+ hash_image=DestroyImage(hash_image);
+ if (moments == (ChannelMoments *) NULL)
+ {
+ perceptual_hash=(ChannelPerceptualHash *) RelinquishMagickMemory(
+ perceptual_hash);
+ return((ChannelPerceptualHash *) NULL);
+ }
+ for (channel=0; channel <= MaxPixelChannels; channel++)
+ for (i=0; i < 7; i++)
+ perceptual_hash[channel].Q[i]=(-MagickLog10(moments[channel].I[i]));
+ moments=(ChannelMoments *) RelinquishMagickMemory(moments);
+ return(perceptual_hash);
}
\f
/*
*minima=0.0;
image_view=AcquireVirtualCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static) shared(status,initialize) \
- dynamic_number_threads(image,image->columns,image->rows,1)
+ #pragma omp parallel for schedule(static,4) shared(status,initialize) \
+ magick_threads(image,image,image->rows,1)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
register ssize_t
i;
- if (GetPixelMask(image,p) != 0)
+ if (GetPixelReadMask(image,p) == 0)
{
p+=GetPixelChannels(image);
continue;
}
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
if (traits == UndefinedPixelTrait)
continue;
if ((traits & UpdatePixelTrait) == 0)
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% GetImageStatistics() returns statistics for each channel in the
-% image. The statistics include the channel depth, its minima, maxima, mean,
-% standard deviation, kurtosis and skewness. You can access the red channel
-% mean, for example, like this:
+% GetImageStatistics() returns statistics for each channel in the image. The
+% statistics include the channel depth, its minima, maxima, mean, standard
+% deviation, kurtosis and skewness. You can access the red channel mean, for
+% example, like this:
%
% channel_statistics=GetImageStatistics(image,exception);
% red_mean=channel_statistics[RedPixelChannel].mean;
channels=0;
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
- if ((traits & UpdatePixelTrait) != 0)
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ if (traits != UndefinedPixelTrait)
channels++;
}
return(channels);
ChannelStatistics
*channel_statistics;
- double
- area;
-
MagickStatusType
- initialize,
status;
QuantumAny
channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
MaxPixelChannels+1,sizeof(*channel_statistics));
if (channel_statistics == (ChannelStatistics *) NULL)
- ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ return(channel_statistics);
(void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
sizeof(*channel_statistics));
for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
{
channel_statistics[i].depth=1;
- channel_statistics[i].maxima=0.0;
- channel_statistics[i].minima=0.0;
+ channel_statistics[i].maxima=(-MagickMaximumValue);
+ channel_statistics[i].minima=MagickMaximumValue;
}
- initialize=MagickTrue;
for (y=0; y < (ssize_t) image->rows; y++)
{
register const Quantum
register ssize_t
i;
- if (GetPixelMask(image,p) != 0)
+ if (GetPixelReadMask(image,p) == 0)
{
p+=GetPixelChannels(image);
continue;
}
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- traits;
-
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
if (traits == UndefinedPixelTrait)
continue;
if (channel_statistics[channel].depth != MAGICKCORE_QUANTUM_DEPTH)
continue;
}
}
- if (initialize != MagickFalse)
- {
- channel_statistics[channel].minima=(double) p[i];
- channel_statistics[channel].maxima=(double) p[i];
- initialize=MagickFalse;
- }
- else
- {
- if ((double) p[i] < channel_statistics[channel].minima)
- channel_statistics[channel].minima=(double) p[i];
- if ((double) p[i] > channel_statistics[channel].maxima)
- channel_statistics[channel].maxima=(double) p[i];
- }
+ if ((double) p[i] < channel_statistics[channel].minima)
+ channel_statistics[channel].minima=(double) p[i];
+ if ((double) p[i] > channel_statistics[channel].maxima)
+ channel_statistics[channel].maxima=(double) p[i];
channel_statistics[channel].sum+=p[i];
channel_statistics[channel].sum_squared+=(double) p[i]*p[i];
channel_statistics[channel].sum_cubed+=(double) p[i]*p[i]*p[i];
channel_statistics[channel].sum_fourth_power+=(double) p[i]*p[i]*p[i]*
p[i];
+ channel_statistics[channel].area++;
}
p+=GetPixelChannels(image);
}
}
- area=(double) image->columns*image->rows;
for (i=0; i < (ssize_t) MaxPixelChannels; i++)
{
- channel_statistics[i].sum/=area;
- channel_statistics[i].sum_squared/=area;
- channel_statistics[i].sum_cubed/=area;
- channel_statistics[i].sum_fourth_power/=area;
+ double
+ area;
+
+ area=PerceptibleReciprocal(channel_statistics[i].area);
+ channel_statistics[i].sum*=area;
+ channel_statistics[i].sum_squared*=area;
+ channel_statistics[i].sum_cubed*=area;
+ channel_statistics[i].sum_fourth_power*=area;
channel_statistics[i].mean=channel_statistics[i].sum;
channel_statistics[i].variance=channel_statistics[i].sum_squared;
channel_statistics[i].standard_deviation=sqrt(
}
for (i=0; i < (ssize_t) MaxPixelChannels; i++)
{
- channel_statistics[CompositePixelChannel].depth=(size_t) EvaluateMax(
- (double) channel_statistics[CompositePixelChannel].depth,(double)
- channel_statistics[i].depth);
+ channel_statistics[CompositePixelChannel].area+=channel_statistics[i].area;
channel_statistics[CompositePixelChannel].minima=MagickMin(
channel_statistics[CompositePixelChannel].minima,
channel_statistics[i].minima);
channel_statistics[i].mean;
}
channels=GetImageChannels(image);
+ channel_statistics[CompositePixelChannel].area/=channels;
channel_statistics[CompositePixelChannel].sum/=channels;
channel_statistics[CompositePixelChannel].sum_squared/=channels;
channel_statistics[CompositePixelChannel].sum_cubed/=channels;
channel_statistics[CompositePixelChannel].skewness/=channels;
for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
{
+ double
+ standard_deviation;
+
if (channel_statistics[i].standard_deviation == 0.0)
continue;
+ standard_deviation=PerceptibleReciprocal(
+ channel_statistics[i].standard_deviation);
channel_statistics[i].skewness=(channel_statistics[i].sum_cubed-3.0*
channel_statistics[i].mean*channel_statistics[i].sum_squared+2.0*
channel_statistics[i].mean*channel_statistics[i].mean*
- channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
- channel_statistics[i].standard_deviation*
- channel_statistics[i].standard_deviation);
+ channel_statistics[i].mean)*(standard_deviation*standard_deviation*
+ standard_deviation);
channel_statistics[i].kurtosis=(channel_statistics[i].sum_fourth_power-4.0*
channel_statistics[i].mean*channel_statistics[i].sum_cubed+6.0*
channel_statistics[i].mean*channel_statistics[i].mean*
channel_statistics[i].sum_squared-3.0*channel_statistics[i].mean*
channel_statistics[i].mean*1.0*channel_statistics[i].mean*
- channel_statistics[i].mean)/(channel_statistics[i].standard_deviation*
- channel_statistics[i].standard_deviation*
- channel_statistics[i].standard_deviation*
- channel_statistics[i].standard_deviation)-3.0;
+ channel_statistics[i].mean)*(standard_deviation*standard_deviation*
+ standard_deviation*standard_deviation)-3.0;
}
+ if (y < (ssize_t) image->rows)
+ channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
+ channel_statistics);
return(channel_statistics);
}
\f
% %
% %
% %
+% P o l y n o m i a l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PolynomialImage() returns a new image where each pixel is the sum of the
+% pixels in the image sequence after applying its corresponding terms
+% (coefficient and degree pairs).
+%
+% The format of the PolynomialImage method is:
+%
+% Image *PolynomialImage(const Image *images,const size_t number_terms,
+% const double *terms,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image sequence.
+%
+% o number_terms: the number of terms in the list. The actual list length
+% is 2 x number_terms + 1 (the constant).
+%
+% o terms: the list of polynomial coefficients and degree pairs and a
+% constant.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *PolynomialImage(const Image *images,
+ const size_t number_terms,const double *terms,ExceptionInfo *exception)
+{
+#define PolynomialImageTag "Polynomial/Image"
+
+ CacheView
+ *polynomial_view;
+
+ Image
+ *image;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ progress;
+
+ PixelChannels
+ **restrict polynomial_pixels;
+
+ size_t
+ number_images;
+
+ ssize_t
+ y;
+
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ image=CloneImage(images,images->columns,images->rows,MagickTrue,
+ exception);
+ if (image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+ {
+ image=DestroyImage(image);
+ return((Image *) NULL);
+ }
+ number_images=GetImageListLength(images);
+ polynomial_pixels=AcquirePixelThreadSet(images,number_images);
+ if (polynomial_pixels == (PixelChannels **) NULL)
+ {
+ image=DestroyImage(image);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ return((Image *) NULL);
+ }
+ /*
+ Polynomial image pixels.
+ */
+ status=MagickTrue;
+ progress=0;
+ polynomial_view=AcquireAuthenticCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ magick_threads(image,image,image->rows,1)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ CacheView
+ *image_view;
+
+ const Image
+ *next;
+
+ const int
+ id = GetOpenMPThreadId();
+
+ register ssize_t
+ i,
+ x;
+
+ register PixelChannels
+ *polynomial_pixel;
+
+ register Quantum
+ *restrict q;
+
+ ssize_t
+ j;
+
+ if (status == MagickFalse)
+ continue;
+ q=QueueCacheViewAuthenticPixels(polynomial_view,0,y,image->columns,1,
+ exception);
+ if (q == (Quantum *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ polynomial_pixel=polynomial_pixels[id];
+ for (j=0; j < (ssize_t) image->columns; j++)
+ for (i=0; i < MaxPixelChannels; i++)
+ polynomial_pixel[j].channel[i]=0.0;
+ next=images;
+ for (j=0; j < (ssize_t) number_images; j++)
+ {
+ register const Quantum
+ *p;
+
+ if (j >= (ssize_t) number_terms)
+ continue;
+ image_view=AcquireVirtualCacheView(next,exception);
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const Quantum *) NULL)
+ {
+ image_view=DestroyCacheView(image_view);
+ break;
+ }
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ register ssize_t
+ i;
+
+ if (GetPixelReadMask(next,p) == 0)
+ {
+ p+=GetPixelChannels(next);
+ continue;
+ }
+ for (i=0; i < (ssize_t) GetPixelChannels(next); i++)
+ {
+ MagickRealType
+ coefficient,
+ degree;
+
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(next,channel);
+ PixelTrait polynomial_traits=GetPixelChannelTraits(image,channel);
+ if ((traits == UndefinedPixelTrait) ||
+ (polynomial_traits == UndefinedPixelTrait))
+ continue;
+ if ((traits & UpdatePixelTrait) == 0)
+ continue;
+ coefficient=(MagickRealType) terms[2*i];
+ degree=(MagickRealType) terms[(i << 1)+1];
+ polynomial_pixel[x].channel[i]+=coefficient*
+ pow(QuantumScale*GetPixelChannel(image,channel,p),degree);
+ }
+ p+=GetPixelChannels(next);
+ }
+ image_view=DestroyCacheView(image_view);
+ next=GetNextImageInList(next);
+ }
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ register ssize_t
+ i;
+
+ if (GetPixelReadMask(image,q) == 0)
+ {
+ q+=GetPixelChannels(image);
+ continue;
+ }
+ for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+ {
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ if (traits == UndefinedPixelTrait)
+ continue;
+ if ((traits & UpdatePixelTrait) == 0)
+ continue;
+ q[i]=ClampToQuantum(QuantumRange*polynomial_pixel[x].channel[i]);
+ }
+ q+=GetPixelChannels(image);
+ }
+ if (SyncCacheViewAuthenticPixels(polynomial_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (images->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_PolynomialImages)
+#endif
+ proceed=SetImageProgress(images,PolynomialImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ polynomial_view=DestroyCacheView(polynomial_view);
+ polynomial_pixels=DestroyPixelThreadSet(polynomial_pixels);
+ if (status == MagickFalse)
+ image=DestroyImage(image);
+ return(image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
% S t a t i s t i c I m a g e %
% %
% %
size_t
number_threads;
- number_threads=GetOpenMPMaximumThreads();
+ number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
sizeof(*pixel_list));
if (pixel_list == (PixelList **) NULL)
static inline void GetMeanPixelList(PixelList *pixel_list,Quantum *pixel)
{
- MagickRealType
+ double
sum;
register SkipList
do
{
color=p->nodes[color].next[0];
- sum+=(MagickRealType) p->nodes[color].count*color;
+ sum+=(double) p->nodes[color].count*color;
count+=p->nodes[color].count;
} while (count < (ssize_t) pixel_list->length);
sum/=pixel_list->length;
static inline void GetStandardDeviationPixelList(PixelList *pixel_list,
Quantum *pixel)
{
- MagickRealType
+ double
sum,
sum_squared;
i;
color=p->nodes[color].next[0];
- sum+=(MagickRealType) p->nodes[color].count*color;
+ sum+=(double) p->nodes[color].count*color;
for (i=0; i < (ssize_t) p->nodes[color].count; i++)
- sum_squared+=((MagickRealType) color)*((MagickRealType) color);
+ sum_squared+=((double) color)*((double) color);
count+=p->nodes[color].count;
} while (count < (ssize_t) pixel_list->length);
sum/=pixel_list->length;
*pixel=ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum)));
}
-static inline void InsertPixelList(const Image *image,const Quantum pixel,
- PixelList *pixel_list)
+static inline void InsertPixelList(const Quantum pixel,PixelList *pixel_list)
{
size_t
signature;
AddNodePixelList(pixel_list,index);
}
-static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
+static inline double MagickAbsoluteValue(const double x)
{
if (x < 0)
return(-x);
statistic_view=AcquireAuthenticCacheView(statistic_image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(static,4) shared(progress,status) \
- dynamic_number_threads(image,image->columns,image->rows,1)
+ magick_threads(image,statistic_image,statistic_image->rows,1)
#endif
for (y=0; y < (ssize_t) statistic_image->rows; y++)
{
register ssize_t
i;
- if (GetPixelMask(image,p) != 0)
- {
- p+=GetPixelChannels(image);
- q+=GetPixelChannels(statistic_image);
- continue;
- }
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- statistic_traits,
- traits;
-
Quantum
pixel;
ssize_t
v;
- channel=GetPixelChannelMapChannel(image,i);
- traits=GetPixelChannelMapTraits(image,channel);
- statistic_traits=GetPixelChannelMapTraits(statistic_image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ PixelTrait statistic_traits=GetPixelChannelTraits(statistic_image,
+ channel);
if ((traits == UndefinedPixelTrait) ||
(statistic_traits == UndefinedPixelTrait))
continue;
- if ((statistic_traits & CopyPixelTrait) != 0)
+ if (((statistic_traits & CopyPixelTrait) != 0) ||
+ (GetPixelReadMask(image,p) == 0))
{
SetPixelChannel(statistic_image,channel,p[center+i],q);
continue;
{
for (u=0; u < (ssize_t) MagickMax(width,1); u++)
{
- InsertPixelList(image,pixels[i],pixel_list[id]);
+ InsertPixelList(pixels[i],pixel_list[id]);
pixels+=GetPixelChannels(image);
}
- pixels+=image->columns*GetPixelChannels(image);
+ pixels+=(image->columns-1)*GetPixelChannels(image);
}
switch (type)
{
case GradientStatistic:
{
- MagickRealType
+ double
maximum,
minimum;
GetMinimumPixelList(pixel_list[id],&pixel);
- minimum=(MagickRealType) pixel;
+ minimum=(double) pixel;
GetMaximumPixelList(pixel_list[id],&pixel);
- maximum=(MagickRealType) pixel;
+ maximum=(double) pixel;
pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
break;
}
statistic_view=DestroyCacheView(statistic_view);
image_view=DestroyCacheView(image_view);
pixel_list=DestroyPixelListThreadSet(pixel_list);
+ if (status == MagickFalse)
+ statistic_image=DestroyImage(statistic_image);
return(statistic_image);
}