Commit ec1a53b39 for imagemagick.org

commit ec1a53b39bdd5c0ec991c215716dafbddc771383
Author: Cristy <mikayla-grace@urban-warrior.org>
Date:   Sat Oct 17 21:07:18 2020 -0400

    support median image statistic and median property

diff --git a/ChangeLog b/ChangeLog
index 529d65e37..8491a3fac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,9 @@
-2020-10-14  7.0.10-35  <quetzlzacatenango@image...>
+2020-10-17  7.0.10-35  <quetzlzacatenango@image...>
   * Release ImageMagick version 7.0.10-35 GIT revision 17...

 2020-10-14  7.0.10-35  <quetzlzacatenango@image...>
-  * prefer ffmpeg over avconv
+  * prefer ffmpeg over avconv.
+  * support median image statistic and median property.

 2020-10-07  7.0.10-34  <quetzlzacatenango@image...>
   * Release ImageMagick version 7.0.10-34 GIT revision 17695:de3284341:20201008
diff --git a/MagickCore/identify.c b/MagickCore/identify.c
index 18a1dbba9..90db64689 100644
--- a/MagickCore/identify.c
+++ b/MagickCore/identify.c
@@ -441,7 +441,7 @@ static ssize_t PrintChannelStatistics(FILE *file,const PixelChannel channel,
   const ChannelStatistics *channel_statistics)
 {
 #define StatisticsFormat "    %s:\n      min: %.*g  (%.*g)\n      " \
-  "max: %.*g (%.*g)\n      mean: %.*g (%.*g)\n      " \
+  "max: %.*g (%.*g)\n      mean: %.*g (%.*g)\n      median: %.*g (%.*g)\n      " \
   "standard deviation: %.*g (%.*g)\n      kurtosis: %.*g\n      " \
   "skewness: %.*g\n      entropy: %.*g\n"

@@ -457,6 +457,8 @@ static ssize_t PrintChannelStatistics(FILE *file,const PixelChannel channel,
     channel_statistics[channel].maxima/(double) QuantumRange,
     GetMagickPrecision(),scale*channel_statistics[channel].mean,
     GetMagickPrecision(),channel_statistics[channel].mean/(double) QuantumRange,
+    GetMagickPrecision(),scale*channel_statistics[channel].median,
+    GetMagickPrecision(),channel_statistics[channel].median/(double) QuantumRange,
     GetMagickPrecision(),scale*channel_statistics[channel].standard_deviation,
     GetMagickPrecision(),channel_statistics[channel].standard_deviation/
     (double) QuantumRange,GetMagickPrecision(),
diff --git a/MagickCore/property.c b/MagickCore/property.c
index bee8aadba..52dcb1c94 100644
--- a/MagickCore/property.c
+++ b/MagickCore/property.c
@@ -3083,6 +3083,17 @@ MagickExport const char *GetMagickProperty(ImageInfo *image_info,
             GetMagickPrecision(),mean);
           break;
         }
+      if (LocaleCompare("median",property) == 0)
+        {
+          double
+            median;
+
+          WarnNoImageReturn("\"%%[%s]\"",property);
+          (void) GetImageMedian(image,&median,exception);
+          (void) FormatLocaleString(value,MagickPathExtent,"%.*g",
+            GetMagickPrecision(),median);
+          break;
+        }
       if ((LocaleCompare("minima",property) == 0) ||
           (LocaleCompare("min",property) == 0))
         {
diff --git a/MagickCore/statistic.c b/MagickCore/statistic.c
index 231f5462b..dacec8552 100644
--- a/MagickCore/statistic.c
+++ b/MagickCore/statistic.c
@@ -229,7 +229,7 @@ static int IntensityCompare(const void *x,const void *y)
   distance=0.0;
   for (i=0; i < MaxPixelChannels; i++)
     distance+=color_1->channel[i]-(double) color_2->channel[i];
-  return(distance < 0 ? -1 : distance > 0 ? 1 : 0);
+  return(distance < 0.0 ? -1 : distance > 0.0 ? 1 : 0);
 }

 #if defined(__cplusplus) || defined(c_plusplus)
@@ -1357,6 +1357,52 @@ MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
   return(MagickTrue);
 }

+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   G e t I m a g e M e d i a n                                               %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  GetImageMedian() returns the median pixel of one or more image channels.
+%
+%  The format of the GetImageMedian method is:
+%
+%      MagickBooleanType GetImageMedian(const Image *image,double *median,
+%        ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o median: the average value in the channel.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetImageMedian(const Image *image,double *median,
+  ExceptionInfo *exception)
+{
+  ChannelStatistics
+    *channel_statistics;
+
+  assert(image != (Image *) NULL);
+  assert(image->signature == MagickCoreSignature);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  channel_statistics=GetImageStatistics(image,exception);
+  if (channel_statistics == (ChannelStatistics *) NULL)
+    return(MagickFalse);
+  *median=channel_statistics[CompositePixelChannel].median;
+  channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
+    channel_statistics);
+  return(MagickTrue);
+}
+
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %                                                                             %
@@ -1964,6 +2010,30 @@ MagickExport MagickBooleanType GetImageRange(const Image *image,double *minima,
 %    o exception: return any errors or warnings in this structure.
 %
 */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int ChannelCompare(const void *x,const void *y)
+{
+  const Quantum
+    *pixel_1,
+    *pixel_2;
+
+  double
+    distance;
+
+  pixel_1=(const Quantum *) x;
+  pixel_2=(const Quantum *) y;
+  distance=(*pixel_1)-(double) (*pixel_2);
+  return(distance < 0.0 ? -1 : distance > 0.0 ? 1 : 0);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
 MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
   ExceptionInfo *exception)
 {
@@ -1978,6 +2048,12 @@ MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
   MagickStatusType
     status;

+  MemoryInfo
+    *median_info;
+
+  Quantum
+    *median;
+
   QuantumAny
     range;

@@ -2015,6 +2091,7 @@ MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
     channel_statistics[i].depth=1;
     channel_statistics[i].maxima=(-MagickMaximumValue);
     channel_statistics[i].minima=MagickMaximumValue;
+//    channel_statistics[i].median=sqrt((double)-1.0);
   }
   (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
     sizeof(*histogram));
@@ -2160,13 +2237,71 @@ MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
       channel_statistics[i].mean)*(standard_deviation*standard_deviation*
       standard_deviation*standard_deviation)-3.0;
   }
+  median_info=AcquireVirtualMemory(image->columns,image->rows*sizeof(*median));
+  if (median_info == (MemoryInfo *) NULL)
+    (void) ThrowMagickException(exception,GetMagickModule(),
+      ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+  else
+    {
+      ssize_t
+        i;
+
+      median=(Quantum *) GetVirtualMemoryBlob(median_info);
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        size_t
+          n;
+
+        /*
+          Compute median statistics for each channel.
+        */
+        PixelChannel channel = GetPixelChannelChannel(image,i);
+        PixelTrait traits = GetPixelChannelTraits(image,channel);
+        if (traits == UndefinedPixelTrait)
+          continue;
+        if ((traits & UpdatePixelTrait) == 0)
+          continue;
+        n=0;
+        for (y=0; y < (ssize_t) image->rows; y++)
+        {
+          register const Quantum
+            *magick_restrict p;
+
+          register ssize_t
+            x;
+
+          p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+          if (p == (const Quantum *) NULL)
+            break;
+          for (x=0; x < (ssize_t) image->columns; x++)
+          {
+            if (GetPixelReadMask(image,p) <= (QuantumRange/2))
+              {
+                p+=GetPixelChannels(image);
+                continue;
+              }
+            median[n++]=p[i];
+          }
+          p+=GetPixelChannels(image);
+        }
+        (void) qsort((void *) median,n,sizeof(*median),ChannelCompare);
+        channel_statistics[channel].median=(double) median[n/2];
+        if ((n % 2) == 0)
+          channel_statistics[channel].median=((double) median[n/2]+
+            (double) median[n/2-1])/2.0;
+      }
+      median_info=RelinquishVirtualMemory(median_info);
+    }
   channel_statistics[CompositePixelChannel].mean=0.0;
+  channel_statistics[CompositePixelChannel].median=0.0;
   channel_statistics[CompositePixelChannel].standard_deviation=0.0;
   channel_statistics[CompositePixelChannel].entropy=0.0;
   for (i=0; i < (ssize_t) MaxPixelChannels; i++)
   {
     channel_statistics[CompositePixelChannel].mean+=
       channel_statistics[i].mean;
+    channel_statistics[CompositePixelChannel].median+=
+      channel_statistics[i].median;
     channel_statistics[CompositePixelChannel].standard_deviation+=
       channel_statistics[i].standard_deviation;
     channel_statistics[CompositePixelChannel].entropy+=
@@ -2174,6 +2309,8 @@ MagickExport ChannelStatistics *GetImageStatistics(const Image *image,
   }
   channel_statistics[CompositePixelChannel].mean/=(double)
     GetImageChannels(image);
+  channel_statistics[CompositePixelChannel].median/=(double)
+    GetImageChannels(image);
   channel_statistics[CompositePixelChannel].standard_deviation/=(double)
     GetImageChannels(image);
   channel_statistics[CompositePixelChannel].entropy/=(double)
diff --git a/MagickCore/statistic.h b/MagickCore/statistic.h
index 9e9a533d7..864b2ac22 100644
--- a/MagickCore/statistic.h
+++ b/MagickCore/statistic.h
@@ -44,7 +44,8 @@ typedef struct _ChannelStatistics
     standard_deviation,
     kurtosis,
     skewness,
-    entropy;
+    entropy,
+    median;
 } ChannelStatistics;

 typedef struct _ChannelMoments
@@ -165,6 +166,7 @@ extern MagickExport MagickBooleanType
   GetImageEntropy(const Image *,double *,ExceptionInfo *),
   GetImageExtrema(const Image *,size_t *,size_t *,ExceptionInfo *),
   GetImageMean(const Image *,double *,double *,ExceptionInfo *),
+  GetImageMedian(const Image *,double *,ExceptionInfo *),
   GetImageKurtosis(const Image *,double *,double *,ExceptionInfo *),
   GetImageRange(const Image *,double *,double *,ExceptionInfo *);