]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/enhance.c
(no commit message)
[imagemagick] / MagickCore / enhance.c
index aad412875d2c64b9bc3a1b1d4c73c6464c40452e..4437c79eff11296b257ddd67d613d69bb96c7dab 100644 (file)
@@ -718,15 +718,15 @@ MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
     for (i=0; i < (ssize_t) image->colors; i++)
     {
       /*
-       Apply transfer function to colormap.
+        Apply transfer function to colormap.
       */
       double
-       luma;
+        luma;
 
       luma=0.21267*image->colormap[i].red+0.71526*image->colormap[i].green+
-       0.07217*image->colormap[i].blue;
+        0.07217*image->colormap[i].blue;
       image->colormap[i].red=luma+color_correction.saturation*cdl_map[
-       ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
+        ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
       image->colormap[i].green=luma+color_correction.saturation*cdl_map[
         ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
       image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
@@ -885,8 +885,17 @@ MagickExport MagickBooleanType ContrastImage(Image *image,
         Contrast enhance colormap.
       */
       for (i=0; i < (ssize_t) image->colors; i++)
-        Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
-          &image->colormap[i].blue);
+      {
+        double
+          blue,
+          green,
+          red;
+
+        Contrast(sign,&red,&green,&blue);
+        image->colormap[i].red=(MagickRealType) red;
+        image->colormap[i].red=(MagickRealType) red;
+        image->colormap[i].red=(MagickRealType) red;
+      }
     }
   /*
     Contrast enhance image.
@@ -1289,12 +1298,10 @@ MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
 {
 #define EnhancePixel(weight) \
   mean=((double) r[i]+GetPixelChannel(enhance_image,channel,q))/2.0; \
-  distance=(double) r[i]-(double) GetPixelChannel( \
-    enhance_image,channel,q); \
-  distance_squared=QuantumScale*(2.0*((double) QuantumRange+1.0)+ \
-    mean)*distance*distance; \
-  if (distance_squared < ((double) QuantumRange*(double) \
-      QuantumRange/25.0f)) \
+  distance=(double) r[i]-(double) GetPixelChannel(enhance_image,channel,q); \
+  distance_squared=QuantumScale*(2.0*((double) QuantumRange+1.0)+mean)* \
+    distance*distance; \
+  if (distance_squared < ((double) QuantumRange*(double) QuantumRange/25.0f)) \
     { \
       aggregate+=(weight)*r[i]; \
       total_weight+=(weight); \
@@ -1806,10 +1813,10 @@ MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
     for (i=0; i < (ssize_t) image->colors; i++)
     {
       /*
-       Gamma-correct colormap.
+        Gamma-correct colormap.
       */
       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-       image->colormap[i].red=(double) gamma_map[
+        image->colormap[i].red=(double) gamma_map[
           ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))];
       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
         image->colormap[i].green=(double) gamma_map[
@@ -3058,18 +3065,18 @@ MagickExport MagickBooleanType NegateImage(Image *image,
     for (i=0; i < (ssize_t) image->colors; i++)
     {
       /*
-       Negate colormap.
+        Negate colormap.
       */
       if (grayscale != MagickFalse)
-       if ((image->colormap[i].red != image->colormap[i].green) ||
-         (image->colormap[i].green != image->colormap[i].blue))
-         continue;
+        if ((image->colormap[i].red != image->colormap[i].green) ||
+            (image->colormap[i].green != image->colormap[i].blue))
+          continue;
       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-       image->colormap[i].red=QuantumRange-image->colormap[i].red;
+        image->colormap[i].red=QuantumRange-image->colormap[i].red;
       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-       image->colormap[i].green=QuantumRange-image->colormap[i].green;
+        image->colormap[i].green=QuantumRange-image->colormap[i].green;
       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-       image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
+        image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
     }
   /*
     Negate image.
@@ -3150,7 +3157,7 @@ MagickExport MagickBooleanType NegateImage(Image *image,
     Negate image.
   */
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(static) shared(progress,status) \
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
     dynamic_number_threads(image,image->columns,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
@@ -3292,147 +3299,158 @@ MagickExport MagickBooleanType NormalizeImage(Image *image,
 %    o exception: return any errors or warnings in this structure.
 %
 */
-MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
-  const MagickBooleanType sharpen,const double contrast,const double midpoint,
-  ExceptionInfo *exception)
-{
-#define SigmoidalContrastImageTag  "SigmoidalContrast/Image"
 
-  CacheView
-    *image_view;
+/*
+  ImageMagick 6 has a version of this function which uses LUTs.
+*/
 
-  MagickBooleanType
-    status;
+/*
+  Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
+  constant" set to a.
 
-  MagickOffsetType
-    progress;
+  The first version, based on the hyperbolic tangent tanh, when combined with
+  the scaling step, is an exact arithmetic clone of the the sigmoid function
+  based on the logistic curve. The equivalence is based on the identity
 
-  ssize_t
-    y;
+    1/(1+exp(-t)) = (1+tanh(t/2))/2
 
-  /*
-    Side effect: clamps values unless contrast<MagickEpsilon, in which
-    case nothing is done.
-  */
-  if (contrast<MagickEpsilon)
-    return(MagickTrue);
+  (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
+  scaled sigmoidal derivation is invariant under affine transformations of
+  the ordinate.
 
-  /*
-    Sigmoidal function with inflexion point moved to b and "slope
-    constant" set to a.
-    The first version, based on the hyperbolic tangent tanh, when
-    combined with the scaling step, is an exact arithmetic clone of the
-    the sigmoid function based on the logistic curve. The equivalence is
-    based on the identity
-    1/(1+exp(-t)) = (1+tanh(t/2))/2
-    (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
-    scaled sigmoidal derivation is invariant under affine transformations
-    of the ordinate.
-    The tanh version is almost certainly more accurate and cheaper.
-    The 0.5 factor in its argument is to clone the legacy ImageMagick
-    behavior.  The reason for making the define depend on atanh even
-    though it only uses tanh has to do with the construction of the
-    inverse of the scaled sigmoidal.
-  */
+  The tanh version is almost certainly more accurate and cheaper.  The 0.5
+  factor in the argument is to clone the legacy ImageMagick behavior. The
+  reason for making the define depend on atanh even though it only uses tanh
+  has to do with the construction of the inverse of the scaled sigmoidal.
+*/
 #if defined(MAGICKCORE_HAVE_ATANH)
 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
 #else
 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
 #endif
-  /*
-    Scaled sigmoidal formula:
+/*
+  Scaled sigmoidal function:
+
     ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
     ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
-    See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html
-    and http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf.
-    The limit of ScaledSigmoidal as a->0 is the identity, but a=0 gives a
-    division by zero. This is fixed above by exiting immediately when
-    contrast is small, leaving the image (or colormap) unmodified. This
-    appears to be safe because the series expansion of the logistic
-    sigmoidal function around x=b is 1/2-a*(b-x)/4+... so that the key
-    denominator s(1)-s(0) is about a/4 (a/2 with tanh).
-  */
+
+  See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
+  http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf.  The limit
+  of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
+  zero. This is fixed below by exiting immediately when contrast is small,
+  leaving the image (or colormap) unmodified. This appears to be safe because
+  the series expansion of the logistic sigmoidal function around x=b is
+
+  1/2-a*(b-x)/4+...
+
+  so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
+*/
 #define ScaledSigmoidal(a,b,x) (                    \
   (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
   (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
-  /*
-    Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. In HDRI,
-    the argument of the hyperbolic tangent or the logistic sigmoidal
-    may be outside of the interval (-1,1), hence the branching.
-    InverseScaledSigmoidal is not a two-side inverse of
-    ScaledSigmoidal: It is only a right inverse. This is unavoidable.
-  */
+/*
+  Inverse of ScaledSigmoidal, used for +sigmoidal-contrast.  Because b
+  may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
+  sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
+  when creating a LUT from in gamut values, hence the branching.  In
+  addition, HDRI may have out of gamut values.
+  InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
+  It is only a right inverse. This is unavoidable.
+*/
+static inline double InverseScaledSigmoidal(const double a,const double b,
+  const double x)
+{
+  const double sig0=Sigmoidal(a,b,0.0);
+  const double sig1=Sigmoidal(a,b,1.0);
+  const double argument=(sig1-sig0)*x+sig0;
+  const double clamped=
+    (
 #if defined(MAGICKCORE_HAVE_ATANH)
-#define InverseScaledSigmoidal(a,b,x) ({                             \
-  const double _argument =                                           \
-    (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) * (x) +          \
-    Sigmoidal((a),(b),0.0);                                          \
-  const double _clamped_argument =                                   \
-    ( _argument < -1+MagickEpsilon ? -1+MagickEpsilon :              \
-    ( _argument > 1-MagickEpsilon ? 1-MagickEpsilon : _argument ) ); \
-  (b) + (2.0/(a)) * atanh(_clamped_argument); })
+      argument < -1+MagickEpsilon
+      ?
+      -1+MagickEpsilon
+      :
+      ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
+    );
+  return(b+(2.0/a)*atanh(clamped));
 #else
-#define InverseScaledSigmoidal(a,b,x) ({                             \
-  const double _argument =                                           \
-    (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) * (x) +          \
-    Sigmoidal((a),(b),0.0);                                          \
-  const double _clamped_argument =                                   \
-    ( _argument < MagickEpsilon ? MagickEpsilon :                    \
-    ( _argument > 1-MagickEpsilon ? 1-MagickEpsilon : _argument ) ); \
-  (b) + (-1.0/(a)) * log(1.0/_clamped_argument+-1.0); })
+      argument < MagickEpsilon
+      ?
+      MagickEpsilon
+      :
+      ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
+    );
+  return(b-log(1.0/clamped-1.0)/a);
 #endif
-  /*
-    Convenience macros.
-  */
+}
+
+MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
+  const MagickBooleanType sharpen,const double contrast,const double midpoint,
+  ExceptionInfo *exception)
+{
+#define SigmoidalContrastImageTag  "SigmoidalContrast/Image"
 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
-  ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )  
+  ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
-  InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )  
+  InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
+
+  CacheView
+    *image_view;
 
+  MagickBooleanType
+    status;
+
+  MagickOffsetType
+    progress;
+
+  ssize_t
+    y;
+
+  /*
+    Convenience macros.
+  */
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  /*
+    Side effect: may clamp values unless contrast<MagickEpsilon, in which
+    case nothing is done.
+  */
+  if (contrast < MagickEpsilon)
+    return(MagickTrue);
   /*
     Sigmoidal-contrast enhance colormap.
   */
   if (image->storage_class == PseudoClass)
     {
       register ssize_t
-       i;
+        i;
 
       if (sharpen != MagickFalse)
-       {
-         for (i=0; i < (ssize_t) image->colors; i++)
-         {
-           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-             image->colormap[i].red=ScaledSig(image->colormap[i].red);
-           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-             image->colormap[i].green=ScaledSig(image->colormap[i].green);
-           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-             image->colormap[i].blue=ScaledSig(image->colormap[i].blue);
-           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
-             image->colormap[i].alpha=ScaledSig(image->colormap[i].alpha);
-         }
-       }
+        for (i=0; i < (ssize_t) image->colors; i++)
+        {
+          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
+            image->colormap[i].red=ScaledSig(image->colormap[i].red);
+          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
+            image->colormap[i].green=ScaledSig(image->colormap[i].green);
+          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
+            image->colormap[i].blue=ScaledSig(image->colormap[i].blue);
+          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
+            image->colormap[i].alpha=ScaledSig(image->colormap[i].alpha);
+        }
       else
-       {
-         for (i=0; i < (ssize_t) image->colors; i++)
-         {
-           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-             image->colormap[i].red=
-               InverseScaledSig(image->colormap[i].red);
-           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-             image->colormap[i].green=
-               InverseScaledSig(image->colormap[i].green);
-           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-             image->colormap[i].blue=
-               InverseScaledSig(image->colormap[i].blue);
-           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
-             image->colormap[i].alpha=
-               InverseScaledSig(image->colormap[i].alpha);
-         }
-       }
+        for (i=0; i < (ssize_t) image->colors; i++)
+        {
+          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
+            image->colormap[i].red=InverseScaledSig(image->colormap[i].red);
+          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
+            image->colormap[i].green=InverseScaledSig(image->colormap[i].green);
+          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
+            image->colormap[i].blue=InverseScaledSig(image->colormap[i].blue);
+          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
+            image->colormap[i].alpha=InverseScaledSig(image->colormap[i].alpha);
+        }
     }
   /*
     Sigmoidal-contrast enhance image.
@@ -3463,7 +3481,7 @@ MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
     for (x=0; x < (ssize_t) image->columns; x++)
     {
       register ssize_t
-       i;
+        i;
 
       if (GetPixelMask(image,q) != 0)
         {
@@ -3482,10 +3500,10 @@ MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
         traits=GetPixelChannelTraits(image,channel);
         if ((traits & UpdatePixelTrait) == 0)
           continue;
-       if (sharpen != MagickFalse)
-         q[i]=ScaledSig(q[i]);
-       else
-         q[i]=InverseScaledSig(q[i]);
+        if (sharpen != MagickFalse)
+          q[i]=ScaledSig(q[i]);
+        else
+          q[i]=InverseScaledSig(q[i]);
       }
       q+=GetPixelChannels(image);
     }