From 549a37e6cd4593dcb997230cd3584c5afead5552 Mon Sep 17 00:00:00 2001 From: cristy Date: Tue, 26 Jan 2010 15:24:15 +0000 Subject: [PATCH] --- PerlMagick/Magick.xs | 279 ++++++++++++++++++++++++++----------------- magick/deprecate.h | 10 -- magick/feature.c | 58 +++++++-- magick/feature.h | 2 +- magick/identify.c | 31 +++-- magick/pixel.h | 10 ++ wand/identify.c | 3 + wand/magick-image.c | 95 +++++++++++---- wand/magick-image.h | 3 + wand/mogrify.c | 10 ++ 10 files changed, 338 insertions(+), 163 deletions(-) diff --git a/PerlMagick/Magick.xs b/PerlMagick/Magick.xs index 2f6fb7924..8a2779e2c 100644 --- a/PerlMagick/Magick.xs +++ b/PerlMagick/Magick.xs @@ -3524,6 +3524,144 @@ Display(ref,...) # # # # # # +# F e a t u r e s # +# # +# # +# # +############################################################################### +# +# +void +Features(ref,...) + Image::Magick ref=NO_INIT + ALIAS: + FeaturesImage = 1 + features = 2 + featuresimage = 3 + PPCODE: + { +#define ChannelFeatures(channel) \ +{ \ + (void) FormatMagickString(message,MaxTextExtent,"%.15g",1.0); \ + PUSHs(sv_2mortal(newSVpv(message,0))); \ +} + + AV + *av; + + char + *attribute, + message[MaxTextExtent]; + + ChannelFeatures + *channel_features; + + double + distance, + scale; + + ExceptionInfo + *exception; + + HV + *hv; + + Image + *image; + + register long + i; + + ssize_t + count; + + struct PackageInfo + *info; + + SV + *av_reference, + *perl_exception, + *reference; + + exception=AcquireExceptionInfo(); + perl_exception=newSVpv("",0); + av=NULL; + if (sv_isobject(ST(0)) == 0) + { + ThrowPerlException(exception,OptionError,"ReferenceIsNotMyType", + PackageName); + goto PerlException; + } + reference=SvRV(ST(0)); + hv=SvSTASH(reference); + av=newAV(); + av_reference=sv_2mortal(sv_bless(newRV((SV *) av),hv)); + SvREFCNT_dec(av); + image=SetupList(aTHX_ reference,&info,(SV ***) NULL,exception); + if (image == (Image *) NULL) + { + ThrowPerlException(exception,OptionError,"NoImagesDefined", + PackageName); + goto PerlException; + } + info=GetPackageInfo(aTHX_ (void *) av,info,exception); + distance=1; + for (i=2; i < items; i+=2) + { + attribute=(char *) SvPV(ST(i-1),na); + switch (*attribute) + { + case 'D': + case 'd': + { + if (LocaleCompare(attribute,"distance") == 0) + { + distance=StringToLong((char *) SvPV(ST(1),na)); + break; + } + ThrowPerlException(exception,OptionError,"UnrecognizedAttribute", + attribute); + break; + } + default: + { + ThrowPerlException(exception,OptionError,"UnrecognizedAttribute", + attribute); + break; + } + } + } + count=0; + for ( ; image; image=image->next) + { + channel_features=GetImageChannelFeatures(image,distance, + &image->exception); + if (channel_features == (ChannelFeatures *) NULL) + continue; + count++; + EXTEND(sp,75*count); + ChannelFeatures(RedChannel); + ChannelFeatures(GreenChannel); + ChannelFeatures(BlueChannel); + if (image->colorspace == CMYKColorspace) + ChannelFeatures(IndexChannel); + if (image->matte != MagickFalse) + ChannelFeatures(OpacityChannel); + channel_features=(ChannelFeatures *) + RelinquishMagickMemory(channel_features); + } + + PerlException: + InheritPerlException(exception,perl_exception); + exception=DestroyExceptionInfo(exception); + SvREFCNT_dec(perl_exception); + } + +# +############################################################################### +# # +# # +# # # F l a t t e n # # # # # @@ -12890,6 +13028,31 @@ Statistics(ref,...) statisticsimage = 3 PPCODE: { +#define ChannelStatistics(channel) \ +{ \ + (void) FormatMagickString(message,MaxTextExtent,"%lu", \ + channel_statistics[channel].depth); \ + PUSHs(sv_2mortal(newSVpv(message,0))); \ + (void) FormatMagickString(message,MaxTextExtent,"%.15g", \ + channel_statistics[channel].minima/scale); \ + PUSHs(sv_2mortal(newSVpv(message,0))); \ + (void) FormatMagickString(message,MaxTextExtent,"%.15g", \ + channel_statistics[channel].maxima/scale); \ + PUSHs(sv_2mortal(newSVpv(message,0))); \ + (void) FormatMagickString(message,MaxTextExtent,"%.15g", \ + channel_statistics[channel].mean/scale); \ + PUSHs(sv_2mortal(newSVpv(message,0))); \ + (void) FormatMagickString(message,MaxTextExtent,"%.15g", \ + channel_statistics[channel].standard_deviation/scale); \ + PUSHs(sv_2mortal(newSVpv(message,0))); \ + (void) FormatMagickString(message,MaxTextExtent,"%.15g", \ + channel_statistics[channel].kurtosis); \ + PUSHs(sv_2mortal(newSVpv(message,0))); \ + (void) FormatMagickString(message,MaxTextExtent,"%.15g", \ + channel_statistics[channel].skewness); \ + PUSHs(sv_2mortal(newSVpv(message,0))); \ +} + AV *av; @@ -12951,119 +13114,15 @@ Statistics(ref,...) if (channel_statistics == (ChannelStatistics *) NULL) continue; count++; - EXTEND(sp,25*count); + EXTEND(sp,35*count); scale=(double) QuantumRange; - (void) FormatMagickString(message,MaxTextExtent,"%lu", - channel_statistics[RedChannel].depth); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[RedChannel].minima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[RedChannel].maxima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[RedChannel].mean/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[RedChannel].standard_deviation/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[RedChannel].kurtosis); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[RedChannel].skewness); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%lu", - channel_statistics[GreenChannel].depth); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[GreenChannel].minima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[GreenChannel].maxima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[GreenChannel].mean/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[GreenChannel].standard_deviation/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[GreenChannel].kurtosis); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[GreenChannel].skewness); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%lu", - channel_statistics[BlueChannel].depth); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlueChannel].minima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlueChannel].maxima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlueChannel].mean/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlueChannel].standard_deviation/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlueChannel].kurtosis); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlueChannel].skewness); - PUSHs(sv_2mortal(newSVpv(message,0))); + ChannelStatistics(RedChannel); + ChannelStatistics(GreenChannel); + ChannelStatistics(BlueChannel); if (image->colorspace == CMYKColorspace) - { - (void) FormatMagickString(message,MaxTextExtent,"%lu", - channel_statistics[BlackChannel].depth); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlackChannel].minima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlackChannel].maxima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlackChannel].mean/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlackChannel].standard_deviation/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlackChannel].kurtosis); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[BlackChannel].skewness); - PUSHs(sv_2mortal(newSVpv(message,0))); - } + ChannelStatistics(IndexChannel); if (image->matte != MagickFalse) - { - (void) FormatMagickString(message,MaxTextExtent,"%lu", - channel_statistics[OpacityChannel].depth); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[OpacityChannel].minima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[OpacityChannel].maxima/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[OpacityChannel].mean/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[OpacityChannel].standard_deviation/scale); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[OpacityChannel].kurtosis); - PUSHs(sv_2mortal(newSVpv(message,0))); - (void) FormatMagickString(message,MaxTextExtent,"%.15g", - channel_statistics[OpacityChannel].skewness); - PUSHs(sv_2mortal(newSVpv(message,0))); - } + ChannelStatistics(OpacityChannel); channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(channel_statistics); } diff --git a/magick/deprecate.h b/magick/deprecate.h index 29571dd25..bd1ca1999 100644 --- a/magick/deprecate.h +++ b/magick/deprecate.h @@ -57,16 +57,6 @@ extern "C" { #define XDownscale(value) ScaleShortToQuantum(value) #define XUpscale(quantum) ScaleQuantumToShort(quantum) -typedef struct _DoublePixelPacket -{ - double - red, - green, - blue, - opacity, - index; -} DoublePixelPacket; - typedef enum { UndefinedMagickLayerMethod diff --git a/magick/feature.c b/magick/feature.c index f1c7bbd0e..6f7def521 100644 --- a/magick/feature.c +++ b/magick/feature.c @@ -110,14 +110,14 @@ % example, like this: % % channel_features=GetImageChannelFeatures(image,1,excepton); -% contrast=channel_features[RedChannel].contrast; +% contrast=channel_features[RedChannel].contrast[0]; % % Use MagickRelinquishMemory() to free the features buffer. % % The format of the GetImageChannelFeatures method is: % % ChannelFeatures *GetImageChannelFeatures(const Image *image, -% ExceptionInfo *exception) +% const unsigned long distance,ExceptionInfo *exception) % % A description of each parameter follows: % @@ -133,7 +133,7 @@ MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image, { typedef struct _SpatialDependenceMatrix { - LongPixelPacket + DoublePixelPacket tones[4]; } SpatialDependenceMatrix; @@ -351,6 +351,7 @@ MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image, switch (i) { case 0: + default: { /* 0 degrees. @@ -454,12 +455,12 @@ MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image, #endif for (y=0; y < (long) number_tones; y++) { + double + normalize; + register long x; - unsigned long - normalize; - for (x=0; x < (long) number_tones; x++) { for (i=0; i < 4; i++) @@ -467,11 +468,12 @@ MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image, switch (i) { case 0: + default: { /* 0 degrees. */ - normalize=2*image->rows*(image->columns-distance); + normalize=2.0*image->rows*(image->columns-distance); break; } case 1: @@ -479,7 +481,7 @@ MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image, /* 45 degrees. */ - normalize=2*(image->rows-distance)*(image->columns-distance); + normalize=2.0*(image->rows-distance)*(image->columns-distance); break; } case 2: @@ -487,7 +489,7 @@ MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image, /* 90 degrees. */ - normalize=2*(image->rows-distance)*image->columns; + normalize=2.0*(image->rows-distance)*image->columns; break; } case 3: @@ -495,11 +497,47 @@ MagickExport ChannelFeatures *GetImageChannelFeatures(const Image *image, /* 135 degrees. */ - normalize=2*(image->rows-distance)*(image->columns-distance); + normalize=2.0*(image->rows-distance)*(image->columns-distance); break; } } pixels[x][y].tones[i].red/=normalize; + pixels[x][y].tones[i].green/=normalize; + pixels[x][y].tones[i].blue/=normalize; + if (image->matte != MagickFalse) + pixels[x][y].tones[i].opacity/=normalize; + if (image->colorspace == CMYKColorspace) + pixels[x][y].tones[i].index/=normalize; + } + } + } + /* + Compute texture features. + */ + for (y=0; y < (long) number_tones; y++) + { + register long + x; + + for (x=0; x < (long) number_tones; x++) + { + for (i=0; i < 4; i++) + { + /* + Angular second moment. + */ + channel_features[RedChannel].angular_second_moment[i]+= + pixels[x][y].tones[i].red*pixels[x][y].tones[i].red; + channel_features[GreenChannel].angular_second_moment[i]+= + pixels[x][y].tones[i].green*pixels[x][y].tones[i].green; + channel_features[BlueChannel].angular_second_moment[i]+= + pixels[x][y].tones[i].blue*pixels[x][y].tones[i].blue; + if (image->matte != MagickFalse) + channel_features[OpacityChannel].angular_second_moment[i]+= + pixels[x][y].tones[i].opacity*pixels[x][y].tones[i].opacity; + if (image->colorspace == CMYKColorspace) + channel_features[IndexChannel].angular_second_moment[i]+= + pixels[x][y].tones[i].index*pixels[x][y].tones[i].index; } } } diff --git a/magick/feature.h b/magick/feature.h index 3cdabd266..fcd0a6f6d 100644 --- a/magick/feature.h +++ b/magick/feature.h @@ -28,7 +28,7 @@ extern "C" { typedef struct _ChannelFeatures { double - angular_second_momentum[4], + angular_second_moment[4], contrast[4], correlation[4], variance_sum_of_squares[4], diff --git a/magick/identify.c b/magick/identify.c index 2da90ae58..0f23dc7f6 100644 --- a/magick/identify.c +++ b/magick/identify.c @@ -59,6 +59,7 @@ #include "magick/effect.h" #include "magick/exception.h" #include "magick/exception-private.h" +#include "magick/feature.h" #include "magick/gem.h" #include "magick/geometry.h" #include "magick/histogram.h" @@ -507,6 +508,17 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file, } channel_statistics=(ChannelStatistics *) RelinquishMagickMemory( channel_statistics); + artifact=GetImageArtifact(image,"identify:features"); + if ((artifact != (const char *) NULL) && + (IsMagickTrue(artifact) != MagickFalse) && (ping == MagickFalse)) + { + ChannelFeatures + *channel_features; + + channel_features=GetImageChannelFeatures(image,1,&image->exception); + channel_features=(ChannelFeatures *) RelinquishMagickMemory( + channel_features); + } if (image->colorspace == CMYKColorspace) (void) fprintf(file," Total ink density: %.0f%%\n",100.0* GetImageTotalInkDensity(image)/(double) QuantumRange); @@ -553,18 +565,15 @@ MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file, (void) fprintf(file," %s\n",tuple); } } - if (ping == MagickFalse) + artifact=GetImageArtifact(image,"identify:unique"); + if ((artifact != (const char *) NULL) && + (IsMagickTrue(artifact) != MagickFalse)) + (void) fprintf(file," Colors: %lu\n",GetNumberColors(image, + (FILE *) NULL,&image->exception)); + if (IsHistogramImage(image,&image->exception) != MagickFalse) { - artifact=GetImageArtifact(image,"identify:unique"); - if ((artifact != (const char *) NULL) && - (IsMagickTrue(artifact) != MagickFalse)) - (void) fprintf(file," Colors: %lu\n",GetNumberColors(image, - (FILE *) NULL,&image->exception)); - if (IsHistogramImage(image,&image->exception) != MagickFalse) - { - (void) fprintf(file," Histogram:\n"); - (void) GetNumberColors(image,file,&image->exception); - } + (void) fprintf(file," Histogram:\n"); + (void) GetNumberColors(image,file,&image->exception); } } if (image->storage_class == PseudoClass) diff --git a/magick/pixel.h b/magick/pixel.h index 8499f4cff..660d0db8c 100644 --- a/magick/pixel.h +++ b/magick/pixel.h @@ -68,6 +68,16 @@ extern "C" { #define SetYellowPixelComponent(q,component) ((q)->blue=(component)) #define SetBlackPixelComponent(q,component) ((q)->opacity=(component)) +typedef struct _DoublePixelPacket +{ + double + red, + green, + blue, + opacity, + index; +} DoublePixelPacket; + typedef struct _LongPixelPacket { unsigned long diff --git a/wand/identify.c b/wand/identify.c index c85e9d69b..35bc511bf 100644 --- a/wand/identify.c +++ b/wand/identify.c @@ -119,6 +119,7 @@ static MagickBooleanType IdentifyUsage(void) "-density geometry horizontal and vertical density of the image", "-depth value image depth", "-extract geometry extract area from image", + "-features display image features (e.g. contrast, correlation)", "-format \"string\" output formatted image characteristics", "-fuzz distance colors within this distance are considered equal", "-gamma value level of gamma correction", @@ -509,6 +510,8 @@ WandExport MagickBooleanType IdentifyImageCommand(ImageInfo *image_info, } case 'f': { + if (LocaleCompare("features",option+1) == 0) + break; if (LocaleCompare("format",option+1) == 0) { format=(char *) NULL; diff --git a/wand/magick-image.c b/wand/magick-image.c index cc4c8f7ff..46d0c47d9 100644 --- a/wand/magick-image.c +++ b/wand/magick-image.c @@ -4262,46 +4262,52 @@ WandExport double *MagickGetImageChannelDistortions(MagickWand *wand, % % % % % % -% M a g i c k G e t I m a g e C h a n n e l M e a n % +% M a g i c k G e t I m a g e C h a n n e l F e a t u r e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % -% MagickGetImageChannelMean() gets the mean and standard deviation of one or -% more image channels. +% MagickGetImageChannelFeatures() returns features for each channel in the +% image at at 0, 45, 90, and 135 degrees for the specified distance. The +% features include the angular second moment, contrast, correlation, sum of +% squares: variance, inverse difference moment, sum average, sum varience, +% sum entropy, entropy, difference variance, difference entropy, information +% measures of correlation 1, information measures of correlation 2, and +% maximum correlation coefficient. You can access the red channel contrast, +% for example, like this: % -% The format of the MagickGetImageChannelMean method is: +% channel_features=MagickGetImageChannelFeatures(wand,1); +% contrast=channel_features[RedChannel].contrast[0]; % -% MagickBooleanType MagickGetImageChannelMean(MagickWand *wand, -% const ChannelType channel,double *mean,double *standard_deviation) +% Use MagickRelinquishMemory() to free the statistics buffer. % -% A description of each parameter follows: +% The format of the MagickGetImageChannelFeatures method is: % -% o wand: the magick wand. +% ChannelFeatures *MagickGetImageChannelFeatures(MagickWand *wand, +% const unsigned long distance) % -% o channel: the image channel(s). +% A description of each parameter follows: % -% o mean: The mean pixel value for the specified channel(s). +% o wand: the magick wand. % -% o standard_deviation: The standard deviation for the specified channel(s). +% o distance: the distance. % */ -WandExport MagickBooleanType MagickGetImageChannelMean(MagickWand *wand, - const ChannelType channel,double *mean,double *standard_deviation) +WandExport ChannelFeatures *MagickGetImageChannelFeatures(MagickWand *wand, + const unsigned long distance) { - MagickBooleanType - status; - assert(wand != (MagickWand *) NULL); assert(wand->signature == WandSignature); if (wand->debug != MagickFalse) (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",wand->name); if (wand->images == (Image *) NULL) - ThrowWandException(WandError,"ContainsNoImages",wand->name); - status=GetImageChannelMean(wand->images,channel,mean,standard_deviation, - wand->exception); - return(status); + { + (void) ThrowMagickException(wand->exception,GetMagickModule(),WandError, + "ContainsNoImages","`%s'",wand->name); + return((ChannelFeatures *) NULL); + } + return(GetImageChannelFeatures(wand->images,distance,wand->exception)); } /* @@ -4350,7 +4356,54 @@ WandExport MagickBooleanType MagickGetImageChannelKurtosis(MagickWand *wand, wand->exception); return(status); } + +/* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % +% M a g i c k G e t I m a g e C h a n n e l M e a n % +% % +% % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% MagickGetImageChannelMean() gets the mean and standard deviation of one or +% more image channels. +% +% The format of the MagickGetImageChannelMean method is: +% +% MagickBooleanType MagickGetImageChannelMean(MagickWand *wand, +% const ChannelType channel,double *mean,double *standard_deviation) +% +% A description of each parameter follows: +% +% o wand: the magick wand. +% +% o channel: the image channel(s). +% +% o mean: The mean pixel value for the specified channel(s). +% +% o standard_deviation: The standard deviation for the specified channel(s). +% +*/ +WandExport MagickBooleanType MagickGetImageChannelMean(MagickWand *wand, + const ChannelType channel,double *mean,double *standard_deviation) +{ + MagickBooleanType + status; + assert(wand != (MagickWand *) NULL); + assert(wand->signature == WandSignature); + if (wand->debug != MagickFalse) + (void) LogMagickEvent(WandEvent,GetMagickModule(),"%s",wand->name); + if (wand->images == (Image *) NULL) + ThrowWandException(WandError,"ContainsNoImages",wand->name); + status=GetImageChannelMean(wand->images,channel,mean,standard_deviation, + wand->exception); + return(status); +} + /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % @@ -4413,7 +4466,7 @@ WandExport MagickBooleanType MagickGetImageChannelRange(MagickWand *wand, % maxima, the mean, the standard deviation, the kurtosis and the skewness. % You can access the red channel mean, for example, like this: % -% channel_statistics=MagickGetImageChannelStatistics(image,excepton); +% channel_statistics=MagickGetImageChannelStatistics(wand); % red_mean=channel_statistics[RedChannel].mean; % % Use MagickRelinquishMemory() to free the statistics buffer. diff --git a/wand/magick-image.h b/wand/magick-image.h index 93a13faec..e7314c613 100644 --- a/wand/magick-image.h +++ b/wand/magick-image.h @@ -23,6 +23,9 @@ extern "C" { #endif +extern WandExport ChannelFeatures + *MagickGetImageChannelFeatures(MagickWand *,const unsigned long); + extern WandExport ChannelStatistics *MagickGetImageChannelStatistics(MagickWand *); diff --git a/wand/mogrify.c b/wand/mogrify.c index 6c2c0a490..2814a0f03 100644 --- a/wand/mogrify.c +++ b/wand/mogrify.c @@ -6711,6 +6711,16 @@ WandExport MagickBooleanType MogrifyImageInfo(ImageInfo *image_info, } case 'f': { + if (LocaleCompare("features",option+1) == 0) + { + if (*option == '+') + { + (void) DeleteImageOption(image_info,"identify:features"); + break; + } + (void) SetImageOption(image_info,"identify:features","true"); + break; + } if (LocaleCompare("fill",option+1) == 0) { if (*option == '+') -- 2.40.0