]> granicus.if.org Git - imagemagick/commitdiff
(no commit message)
authorcristy <urban-warrior@git.imagemagick.org>
Tue, 26 Jan 2010 15:24:15 +0000 (15:24 +0000)
committercristy <urban-warrior@git.imagemagick.org>
Tue, 26 Jan 2010 15:24:15 +0000 (15:24 +0000)
PerlMagick/Magick.xs
magick/deprecate.h
magick/feature.c
magick/feature.h
magick/identify.c
magick/pixel.h
wand/identify.c
wand/magick-image.c
wand/magick-image.h
wand/mogrify.c

index 2f6fb7924a0ac096e63725d353f6cf2c0f18a08c..8a2779e2cc14322d3126172c3fea73b603242b1f 100644 (file)
@@ -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
+#
+###############################################################################
+#                                                                             #
+#                                                                             #
+#                                                                             #
 #   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);
     }
index 29571dd25fecf8774d5568d8c6e038ca8e8a5260..bd1ca1999997d67a3b7d7e3bbef4c35b87c7c672 100644 (file)
@@ -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
index f1c7bbd0e23ce2db9d960ee0bae2bf8df987299f..6f7def52147478f30670d93ca7d165f84f3e4025 100644 (file)
 %  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;
       }
     }
   }
index 3cdabd2668cf43e95114ae4dc4aba8aaec3653fb..fcd0a6f6d482d73c84164a75fb8d9da43d15d8a5 100644 (file)
@@ -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],
index 2da90ae58d458c945b361c3cb7e7c4f1e770d44d..0f23dc7f6bd325785d617107003adb16ff7769c6 100644 (file)
@@ -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)
index 8499f4cff081e644d07b75a40339dec2204c9c6a..660d0db8c0ef2c58e19d6840332ca9bed3286fa7 100644 (file)
@@ -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
index c85e9d69b1d9f51f2114fe7a0e6f90acf9b24acc..35bc511bf2522209baa00c9862e15805028ae080 100644 (file)
@@ -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;
index cc4c8f7ffc6ae7db41954dc2446cf3d607ecfaf2..46d0c47d96a3af90d59e74f78c7cd7c54f01e4df 100644 (file)
@@ -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));
 }
 \f
 /*
@@ -4350,7 +4356,54 @@ WandExport MagickBooleanType MagickGetImageChannelKurtosis(MagickWand *wand,
     wand->exception);
   return(status);
 }
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   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);
+}
+\f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %                                                                             %
@@ -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.
index 93a13faec72964380fa1a96e82ed0a9c2237154b..e7314c6131216642abd02e97e56ea0c882a9354f 100644 (file)
@@ -23,6 +23,9 @@
 extern "C" {
 #endif
 
+extern WandExport ChannelFeatures
+  *MagickGetImageChannelFeatures(MagickWand *,const unsigned long);
+
 extern WandExport ChannelStatistics
   *MagickGetImageChannelStatistics(MagickWand *);
 
index 6c2c0a490d9b3f2c0fa8bd41a24334a563593420..2814a0f038e480a649975480b2f2155d4115be71 100644 (file)
@@ -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 == '+')