Home | History | Annotate | Download | only in MagickCore
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %              EEEEE  N   N  H   H   AAA   N   N   CCCC  EEEEE                %
      7 %              E      NN  N  H   H  A   A  NN  N  C      E                    %
      8 %              EEE    N N N  HHHHH  AAAAA  N N N  C      EEE                  %
      9 %              E      N  NN  H   H  A   A  N  NN  C      E                    %
     10 %              EEEEE  N   N  H   H  A   A  N   N   CCCC  EEEEE                %
     11 %                                                                             %
     12 %                                                                             %
     13 %                    MagickCore Image Enhancement Methods                     %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                                 July 1992                                   %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    https://imagemagick.org/script/license.php                               %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 %
     38 */
     39 
     40 /*
     42   Include declarations.
     43 */
     44 #include "MagickCore/studio.h"
     45 #include "MagickCore/accelerate-private.h"
     46 #include "MagickCore/artifact.h"
     47 #include "MagickCore/attribute.h"
     48 #include "MagickCore/cache.h"
     49 #include "MagickCore/cache-private.h"
     50 #include "MagickCore/cache-view.h"
     51 #include "MagickCore/channel.h"
     52 #include "MagickCore/color.h"
     53 #include "MagickCore/color-private.h"
     54 #include "MagickCore/colorspace.h"
     55 #include "MagickCore/colorspace-private.h"
     56 #include "MagickCore/composite-private.h"
     57 #include "MagickCore/enhance.h"
     58 #include "MagickCore/exception.h"
     59 #include "MagickCore/exception-private.h"
     60 #include "MagickCore/fx.h"
     61 #include "MagickCore/gem.h"
     62 #include "MagickCore/gem-private.h"
     63 #include "MagickCore/geometry.h"
     64 #include "MagickCore/histogram.h"
     65 #include "MagickCore/image.h"
     66 #include "MagickCore/image-private.h"
     67 #include "MagickCore/memory_.h"
     68 #include "MagickCore/monitor.h"
     69 #include "MagickCore/monitor-private.h"
     70 #include "MagickCore/option.h"
     71 #include "MagickCore/pixel.h"
     72 #include "MagickCore/pixel-accessor.h"
     73 #include "MagickCore/quantum.h"
     74 #include "MagickCore/quantum-private.h"
     75 #include "MagickCore/resample.h"
     76 #include "MagickCore/resample-private.h"
     77 #include "MagickCore/resource_.h"
     78 #include "MagickCore/statistic.h"
     79 #include "MagickCore/string_.h"
     80 #include "MagickCore/string-private.h"
     81 #include "MagickCore/thread-private.h"
     82 #include "MagickCore/threshold.h"
     83 #include "MagickCore/token.h"
     84 #include "MagickCore/xml-tree.h"
     85 #include "MagickCore/xml-tree-private.h"
     86 
     87 /*
     89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     90 %                                                                             %
     91 %                                                                             %
     92 %                                                                             %
     93 %     A u t o G a m m a I m a g e                                             %
     94 %                                                                             %
     95 %                                                                             %
     96 %                                                                             %
     97 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     98 %
     99 %  AutoGammaImage() extract the 'mean' from the image and adjust the image
    100 %  to try make set its gamma appropriatally.
    101 %
    102 %  The format of the AutoGammaImage method is:
    103 %
    104 %      MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception)
    105 %
    106 %  A description of each parameter follows:
    107 %
    108 %    o image: The image to auto-level
    109 %
    110 %    o exception: return any errors or warnings in this structure.
    111 %
    112 */
    113 MagickExport MagickBooleanType AutoGammaImage(Image *image,
    114   ExceptionInfo *exception)
    115 {
    116   double
    117     gamma,
    118     log_mean,
    119     mean,
    120     sans;
    121 
    122   MagickStatusType
    123     status;
    124 
    125   register ssize_t
    126     i;
    127 
    128   log_mean=log(0.5);
    129   if (image->channel_mask == DefaultChannels)
    130     {
    131       /*
    132         Apply gamma correction equally across all given channels.
    133       */
    134       (void) GetImageMean(image,&mean,&sans,exception);
    135       gamma=log(mean*QuantumScale)/log_mean;
    136       return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception));
    137     }
    138   /*
    139     Auto-gamma each channel separately.
    140   */
    141   status=MagickTrue;
    142   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    143   {
    144     ChannelType
    145       channel_mask;
    146 
    147     PixelChannel channel = GetPixelChannelChannel(image,i);
    148     PixelTrait traits = GetPixelChannelTraits(image,channel);
    149     if ((traits & UpdatePixelTrait) == 0)
    150       continue;
    151     channel_mask=SetImageChannelMask(image,(ChannelType) (1UL << i));
    152     status=GetImageMean(image,&mean,&sans,exception);
    153     gamma=log(mean*QuantumScale)/log_mean;
    154     status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception);
    155     (void) SetImageChannelMask(image,channel_mask);
    156     if (status == MagickFalse)
    157       break;
    158   }
    159   return(status != 0 ? MagickTrue : MagickFalse);
    160 }
    161 
    162 /*
    164 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    165 %                                                                             %
    166 %                                                                             %
    167 %                                                                             %
    168 %     A u t o L e v e l I m a g e                                             %
    169 %                                                                             %
    170 %                                                                             %
    171 %                                                                             %
    172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    173 %
    174 %  AutoLevelImage() adjusts the levels of a particular image channel by
    175 %  scaling the minimum and maximum values to the full quantum range.
    176 %
    177 %  The format of the LevelImage method is:
    178 %
    179 %      MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception)
    180 %
    181 %  A description of each parameter follows:
    182 %
    183 %    o image: The image to auto-level
    184 %
    185 %    o exception: return any errors or warnings in this structure.
    186 %
    187 */
    188 MagickExport MagickBooleanType AutoLevelImage(Image *image,
    189   ExceptionInfo *exception)
    190 {
    191   return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
    192 }
    193 
    194 /*
    196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    197 %                                                                             %
    198 %                                                                             %
    199 %                                                                             %
    200 %     B r i g h t n e s s C o n t r a s t I m a g e                           %
    201 %                                                                             %
    202 %                                                                             %
    203 %                                                                             %
    204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    205 %
    206 %  BrightnessContrastImage() changes the brightness and/or contrast of an
    207 %  image.  It converts the brightness and contrast parameters into slope and
    208 %  intercept and calls a polynomical function to apply to the image.
    209 %
    210 %  The format of the BrightnessContrastImage method is:
    211 %
    212 %      MagickBooleanType BrightnessContrastImage(Image *image,
    213 %        const double brightness,const double contrast,ExceptionInfo *exception)
    214 %
    215 %  A description of each parameter follows:
    216 %
    217 %    o image: the image.
    218 %
    219 %    o brightness: the brightness percent (-100 .. 100).
    220 %
    221 %    o contrast: the contrast percent (-100 .. 100).
    222 %
    223 %    o exception: return any errors or warnings in this structure.
    224 %
    225 */
    226 MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
    227   const double brightness,const double contrast,ExceptionInfo *exception)
    228 {
    229 #define BrightnessContastImageTag  "BrightnessContast/Image"
    230 
    231   double
    232     alpha,
    233     coefficients[2],
    234     intercept,
    235     slope;
    236 
    237   MagickBooleanType
    238     status;
    239 
    240   /*
    241     Compute slope and intercept.
    242   */
    243   assert(image != (Image *) NULL);
    244   assert(image->signature == MagickCoreSignature);
    245   if (image->debug != MagickFalse)
    246     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    247   alpha=contrast;
    248   slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
    249   if (slope < 0.0)
    250     slope=0.0;
    251   intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
    252   coefficients[0]=slope;
    253   coefficients[1]=intercept;
    254   status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
    255   return(status);
    256 }
    257 
    258 /*
    260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    261 %                                                                             %
    262 %                                                                             %
    263 %                                                                             %
    264 %     C L A H E I m a g e                                                     %
    265 %                                                                             %
    266 %                                                                             %
    267 %                                                                             %
    268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    269 %
    270 %  CLAHEImage() is a variant of adaptive histogram equalization in which the
    271 %  contrast amplification is limited, so as to reduce this problem of noise
    272 %  amplification.
    273 %
    274 %  Adapted from implementation by Karel Zuiderveld, karel (at) cv.ruu.nl in
    275 %  "Graphics Gems IV", Academic Press, 1994.
    276 %
    277 %  The format of the CLAHEImage method is:
    278 %
    279 %      MagickBooleanType CLAHEImage(Image *image,const size_t width,
    280 %        const size_t height,const size_t number_bins,const double clip_limit,
    281 %        ExceptionInfo *exception)
    282 %
    283 %  A description of each parameter follows:
    284 %
    285 %    o image: the image.
    286 %
    287 %    o width: the width of the tile divisions to use in horizontal direction.
    288 %
    289 %    o height: the height of the tile divisions to use in vertical direction.
    290 %
    291 %    o number_bins: number of bins for histogram ("dynamic range").
    292 %
    293 %    o clip_limit: contrast limit for localised changes in contrast. A limit
    294 %      less than 1 results in standard non-contrast limited AHE.
    295 %
    296 %    o exception: return any errors or warnings in this structure.
    297 %
    298 */
    299 
    300 typedef struct _RangeInfo
    301 {
    302   unsigned short
    303     min,
    304     max;
    305 } RangeInfo;
    306 
    307 static void ClipCLAHEHistogram(const double clip_limit,const size_t number_bins,
    308   size_t *histogram)
    309 {
    310 #define NumberCLAHEGrays  (65536)
    311 
    312   register ssize_t
    313     i;
    314 
    315   size_t
    316     cumulative_excess,
    317     previous_excess,
    318     step;
    319 
    320   ssize_t
    321     excess;
    322 
    323   /*
    324     Compute total number of excess pixels.
    325   */
    326   cumulative_excess=0;
    327   for (i=0; i < (ssize_t) number_bins; i++)
    328   {
    329     excess=(ssize_t) histogram[i]-(ssize_t) clip_limit;
    330     if (excess > 0)
    331       cumulative_excess+=excess;
    332   }
    333   /*
    334     Clip histogram and redistribute excess pixels across all bins.
    335   */
    336   step=cumulative_excess/number_bins;
    337   excess=(ssize_t) (clip_limit-step);
    338   for (i=0; i < (ssize_t) number_bins; i++)
    339   {
    340     if ((double) histogram[i] > clip_limit)
    341       histogram[i]=(size_t) clip_limit;
    342     else
    343       if ((ssize_t) histogram[i] > excess)
    344         {
    345           cumulative_excess-=histogram[i]-excess;
    346           histogram[i]=(size_t) clip_limit;
    347         }
    348       else
    349         {
    350           cumulative_excess-=step;
    351           histogram[i]+=step;
    352         }
    353   }
    354   /*
    355     Redistribute remaining excess.
    356   */
    357   do
    358   {
    359     register size_t
    360       *p;
    361 
    362     size_t
    363       *q;
    364 
    365     previous_excess=cumulative_excess;
    366     p=histogram;
    367     q=histogram+number_bins;
    368     while ((cumulative_excess != 0) && (p < q))
    369     {
    370       step=number_bins/cumulative_excess;
    371       if (step < 1)
    372         step=1;
    373       for (p=histogram; (p < q) && (cumulative_excess != 0); p+=step)
    374         if ((double) *p < clip_limit)
    375           {
    376             (*p)++;
    377             cumulative_excess--;
    378           }
    379       p++;
    380     }
    381   } while ((cumulative_excess != 0) && (cumulative_excess < previous_excess));
    382 }
    383 
    384 static void GenerateCLAHEHistogram(const RectangleInfo *clahe_info,
    385   const RectangleInfo *tile_info,const size_t number_bins,
    386   const unsigned short *lut,const unsigned short *pixels,size_t *histogram)
    387 {
    388   register const unsigned short
    389     *p;
    390 
    391   register ssize_t
    392     i;
    393 
    394   /*
    395     Classify the pixels into a gray histogram.
    396   */
    397   for (i=0; i < (ssize_t) number_bins; i++)
    398     histogram[i]=0L;
    399   p=pixels;
    400   for (i=0; i < (ssize_t) tile_info->height; i++)
    401   {
    402     const unsigned short
    403       *q;
    404 
    405     q=p+tile_info->width;
    406     while (p < q)
    407       histogram[lut[*p++]]++;
    408     q+=clahe_info->width;
    409     p=q-tile_info->width;
    410   }
    411 }
    412 
    413 static void InterpolateCLAHE(const RectangleInfo *clahe_info,const size_t *Q12,
    414   const size_t *Q22,const size_t *Q11,const size_t *Q21,
    415   const RectangleInfo *tile,const unsigned short *lut,unsigned short *pixels)
    416 {
    417   ssize_t
    418     y;
    419 
    420   unsigned short
    421     intensity;
    422 
    423   /*
    424     Bilinear interpolate four tiles to eliminate boundary artifacts.
    425   */
    426   for (y=(ssize_t) tile->height; y > 0; y--)
    427   {
    428     register ssize_t
    429       x;
    430 
    431     for (x=(ssize_t) tile->width; x > 0; x--)
    432     {
    433       intensity=lut[*pixels];
    434       *pixels++=(unsigned short ) (PerceptibleReciprocal((double) tile->width*
    435         tile->height)*(y*(x*Q12[intensity]+(tile->width-x)*Q22[intensity])+
    436         (tile->height-y)*(x*Q11[intensity]+(tile->width-x)*Q21[intensity])));
    437     }
    438     pixels+=(clahe_info->width-tile->width);
    439   }
    440 }
    441 
    442 static void GenerateCLAHELut(const RangeInfo *range_info,
    443   const size_t number_bins,unsigned short *lut)
    444 {
    445   ssize_t
    446     i;
    447 
    448   unsigned short
    449     delta;
    450 
    451   /*
    452     Scale input image [intensity min,max] to [0,number_bins-1].
    453   */
    454   delta=(unsigned short) ((range_info->max-range_info->min)/number_bins+1);
    455   for (i=(ssize_t) range_info->min; i <= (ssize_t) range_info->max; i++)
    456     lut[i]=(unsigned short) ((i-range_info->min)/delta);
    457 }
    458 
    459 static void MapCLAHEHistogram(const RangeInfo *range_info,
    460   const size_t number_bins,const size_t number_pixels,size_t *histogram)
    461 {
    462   double
    463     scale,
    464     sum;
    465 
    466   register ssize_t
    467     i;
    468 
    469   /*
    470     Rescale histogram to range [min-intensity .. max-intensity].
    471   */
    472   scale=(double) (range_info->max-range_info->min)/number_pixels;
    473   sum=0.0;
    474   for (i=0; i < (ssize_t) number_bins; i++)
    475   {
    476     sum+=histogram[i];
    477     histogram[i]=(size_t) (range_info->min+scale*sum);
    478     if (histogram[i] > range_info->max)
    479       histogram[i]=range_info->max;
    480   }
    481 }
    482 
    483 static MagickBooleanType CLAHE(const RectangleInfo *clahe_info,
    484   const RectangleInfo *tile_info,const RangeInfo *range_info,
    485   const size_t number_bins,const double clip_limit,unsigned short *pixels)
    486 {
    487   MemoryInfo
    488     *tile_cache;
    489 
    490   register unsigned short
    491     *p;
    492 
    493   size_t
    494     limit,
    495     *tiles;
    496 
    497   ssize_t
    498     y;
    499 
    500   unsigned short
    501     lut[NumberCLAHEGrays];
    502 
    503   /*
    504     Constrast limited adapted histogram equalization.
    505   */
    506   if (clip_limit == 1.0)
    507     return(MagickTrue);
    508   tile_cache=AcquireVirtualMemory((size_t) clahe_info->x*clahe_info->y,
    509     number_bins*sizeof(*tiles));
    510   if (tile_cache == (MemoryInfo *) NULL)
    511     return(MagickFalse);
    512   tiles=(size_t *) GetVirtualMemoryBlob(tile_cache);
    513   limit=(size_t) (clip_limit*(tile_info->width*tile_info->height)/number_bins);
    514   if (limit < 1UL)
    515     limit=1UL;
    516   /*
    517     Generate greylevel mappings for each tile.
    518   */
    519   GenerateCLAHELut(range_info,number_bins,lut);
    520   p=pixels;
    521   for (y=0; y < (ssize_t) clahe_info->y; y++)
    522   {
    523     register ssize_t
    524       x;
    525 
    526     for (x=0; x < (ssize_t) clahe_info->x; x++)
    527     {
    528       size_t
    529         *histogram;
    530 
    531       histogram=tiles+(number_bins*(y*clahe_info->x+x));
    532       GenerateCLAHEHistogram(clahe_info,tile_info,number_bins,lut,p,histogram);
    533       ClipCLAHEHistogram((double) limit,number_bins,histogram);
    534       MapCLAHEHistogram(range_info,number_bins,tile_info->width*
    535         tile_info->height,histogram);
    536       p+=tile_info->width;
    537     }
    538     p+=clahe_info->width*(tile_info->height-1);
    539   }
    540   /*
    541     Interpolate greylevel mappings to get CLAHE image.
    542   */
    543   p=pixels;
    544   for (y=0; y <= (ssize_t) clahe_info->y; y++)
    545   {
    546     OffsetInfo
    547       offset;
    548 
    549     RectangleInfo
    550       tile;
    551 
    552     register ssize_t
    553       x;
    554 
    555     tile.height=tile_info->height;
    556     tile.y=y-1;
    557     offset.y=tile.y+1;
    558     if (y == 0)
    559       {
    560         /*
    561           Top row.
    562         */
    563         tile.height=tile_info->height >> 1;
    564         tile.y=0;
    565         offset.y=0;
    566       }
    567     else
    568       if (y == (ssize_t) clahe_info->y)
    569         {
    570           /*
    571             Bottom row.
    572           */
    573           tile.height=(tile_info->height+1) >> 1;
    574           tile.y=clahe_info->y-1;
    575           offset.y=tile.y;
    576         }
    577     for (x=0; x <= (ssize_t) clahe_info->x; x++)
    578     {
    579       tile.width=tile_info->width;
    580       tile.x=x-1;
    581       offset.x=tile.x+1;
    582       if (x == 0)
    583         {
    584           /*
    585             Left column.
    586           */
    587           tile.width=tile_info->width >> 1;
    588           tile.x=0;
    589           offset.x=0;
    590         }
    591       else
    592         if (x == (ssize_t) clahe_info->x)
    593           {
    594             /*
    595               Right column.
    596             */
    597             tile.width=(tile_info->width+1) >> 1;
    598             tile.x=clahe_info->x-1;
    599             offset.x=tile.x;
    600           }
    601       InterpolateCLAHE(clahe_info,
    602         tiles+(number_bins*(tile.y*clahe_info->x+tile.x)),     /* Q12 */
    603         tiles+(number_bins*(tile.y*clahe_info->x+offset.x)),   /* Q22 */
    604         tiles+(number_bins*(offset.y*clahe_info->x+tile.x)),   /* Q11 */
    605         tiles+(number_bins*(offset.y*clahe_info->x+offset.x)), /* Q21 */
    606         &tile,lut,p);
    607       p+=tile.width;
    608     }
    609     p+=clahe_info->width*(tile.height-1);
    610   }
    611   tile_cache=RelinquishVirtualMemory(tile_cache);
    612   return(MagickTrue);
    613 }
    614 
    615 MagickExport MagickBooleanType CLAHEImage(Image *image,const size_t width,
    616   const size_t height,const size_t number_bins,const double clip_limit,
    617   ExceptionInfo *exception)
    618 {
    619 #define CLAHEImageTag  "CLAHE/Image"
    620 
    621   CacheView
    622     *image_view;
    623 
    624   ColorspaceType
    625     colorspace;
    626 
    627   MagickBooleanType
    628     status;
    629 
    630   MagickOffsetType
    631     progress;
    632 
    633   MemoryInfo
    634     *pixel_cache;
    635 
    636   RangeInfo
    637     range_info;
    638 
    639   RectangleInfo
    640     clahe_info,
    641     tile_info;
    642 
    643   size_t
    644     n;
    645 
    646   ssize_t
    647     y;
    648 
    649   unsigned short
    650     *pixels;
    651 
    652   /*
    653     Configure CLAHE parameters.
    654   */
    655   assert(image != (Image *) NULL);
    656   assert(image->signature == MagickCoreSignature);
    657   if (image->debug != MagickFalse)
    658     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    659   range_info.min=0;
    660   range_info.max=NumberCLAHEGrays-1;
    661   tile_info.width=width;
    662   if (tile_info.width == 0)
    663     tile_info.width=image->columns >> 3;
    664   tile_info.height=height;
    665   if (tile_info.height == 0)
    666     tile_info.height=image->rows >> 3;
    667   tile_info.x=0;
    668   if ((image->columns % tile_info.width) != 0)
    669     tile_info.x=(ssize_t) tile_info.width-(image->columns % tile_info.width);
    670   tile_info.y=0;
    671   if ((image->rows % tile_info.height) != 0)
    672     tile_info.y=(ssize_t) tile_info.height-(image->rows % tile_info.height);
    673   clahe_info.width=image->columns+tile_info.x;
    674   clahe_info.height=image->rows+tile_info.y;
    675   clahe_info.x=(ssize_t) clahe_info.width/tile_info.width;
    676   clahe_info.y=(ssize_t) clahe_info.height/tile_info.height;
    677   pixel_cache=AcquireVirtualMemory(clahe_info.width,clahe_info.height*
    678     sizeof(*pixels));
    679   if (pixel_cache == (MemoryInfo *) NULL)
    680     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
    681       image->filename);
    682   pixels=(unsigned short *) GetVirtualMemoryBlob(pixel_cache);
    683   colorspace=image->colorspace;
    684   if (TransformImageColorspace(image,LabColorspace,exception) == MagickFalse)
    685     {
    686       pixel_cache=RelinquishVirtualMemory(pixel_cache);
    687       return(MagickFalse);
    688     }
    689   /*
    690     Initialize CLAHE pixels.
    691   */
    692   image_view=AcquireVirtualCacheView(image,exception);
    693   progress=0;
    694   status=MagickTrue;
    695   n=0;
    696   for (y=0; y < (ssize_t) clahe_info.height; y++)
    697   {
    698     register const Quantum
    699       *magick_restrict p;
    700 
    701     register ssize_t
    702       x;
    703 
    704     if (status == MagickFalse)
    705       continue;
    706     p=GetCacheViewVirtualPixels(image_view,-(tile_info.x >> 1),y-
    707       (tile_info.y >> 1),clahe_info.width,1,exception);
    708     if (p == (const Quantum *) NULL)
    709       {
    710         status=MagickFalse;
    711         continue;
    712       }
    713     for (x=0; x < (ssize_t) clahe_info.width; x++)
    714     {
    715       pixels[n++]=ScaleQuantumToShort(p[0]);
    716       p+=GetPixelChannels(image);
    717     }
    718     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    719       {
    720         MagickBooleanType
    721           proceed;
    722 
    723 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    724         #pragma omp atomic
    725 #endif
    726         progress++;
    727         proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
    728           GetPixelChannels(image));
    729         if (proceed == MagickFalse)
    730           status=MagickFalse;
    731       }
    732   }
    733   image_view=DestroyCacheView(image_view);
    734   status=CLAHE(&clahe_info,&tile_info,&range_info,number_bins == 0 ?
    735     (size_t) 128 : MagickMin(number_bins,256),clip_limit,pixels);
    736   if (status == MagickFalse)
    737     (void) ThrowMagickException(exception,GetMagickModule(),
    738       ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
    739   /*
    740     Push CLAHE pixels to CLAHE image.
    741   */
    742   image_view=AcquireAuthenticCacheView(image,exception);
    743   n=clahe_info.width*(tile_info.y >> 1);
    744   for (y=0; y < (ssize_t) image->rows; y++)
    745   {
    746     register Quantum
    747       *magick_restrict q;
    748 
    749     register ssize_t
    750       x;
    751 
    752     if (status == MagickFalse)
    753       continue;
    754     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    755     if (q == (Quantum *) NULL)
    756       {
    757         status=MagickFalse;
    758         continue;
    759       }
    760     n+=tile_info.x >> 1;
    761     for (x=0; x < (ssize_t) image->columns; x++)
    762     {
    763       q[0]=ScaleShortToQuantum(pixels[n++]);
    764       q+=GetPixelChannels(image);
    765     }
    766     n+=(clahe_info.width-image->columns-(tile_info.x >> 1));
    767     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
    768       status=MagickFalse;
    769     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    770       {
    771         MagickBooleanType
    772           proceed;
    773 
    774 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    775         #pragma omp atomic
    776 #endif
    777         progress++;
    778         proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
    779           GetPixelChannels(image));
    780         if (proceed == MagickFalse)
    781           status=MagickFalse;
    782       }
    783   }
    784   image_view=DestroyCacheView(image_view);
    785   pixel_cache=RelinquishVirtualMemory(pixel_cache);
    786   if (TransformImageColorspace(image,colorspace,exception) == MagickFalse)
    787     status=MagickFalse;
    788   return(status);
    789 }
    790 
    791 /*
    793 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    794 %                                                                             %
    795 %                                                                             %
    796 %                                                                             %
    797 %     C l u t I m a g e                                                       %
    798 %                                                                             %
    799 %                                                                             %
    800 %                                                                             %
    801 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    802 %
    803 %  ClutImage() replaces each color value in the given image, by using it as an
    804 %  index to lookup a replacement color value in a Color Look UP Table in the
    805 %  form of an image.  The values are extracted along a diagonal of the CLUT
    806 %  image so either a horizontal or vertial gradient image can be used.
    807 %
    808 %  Typically this is used to either re-color a gray-scale image according to a
    809 %  color gradient in the CLUT image, or to perform a freeform histogram
    810 %  (level) adjustment according to the (typically gray-scale) gradient in the
    811 %  CLUT image.
    812 %
    813 %  When the 'channel' mask includes the matte/alpha transparency channel but
    814 %  one image has no such channel it is assumed that that image is a simple
    815 %  gray-scale image that will effect the alpha channel values, either for
    816 %  gray-scale coloring (with transparent or semi-transparent colors), or
    817 %  a histogram adjustment of existing alpha channel values.   If both images
    818 %  have matte channels, direct and normal indexing is applied, which is rarely
    819 %  used.
    820 %
    821 %  The format of the ClutImage method is:
    822 %
    823 %      MagickBooleanType ClutImage(Image *image,Image *clut_image,
    824 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
    825 %
    826 %  A description of each parameter follows:
    827 %
    828 %    o image: the image, which is replaced by indexed CLUT values
    829 %
    830 %    o clut_image: the color lookup table image for replacement color values.
    831 %
    832 %    o method: the pixel interpolation method.
    833 %
    834 %    o exception: return any errors or warnings in this structure.
    835 %
    836 */
    837 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image,
    838   const PixelInterpolateMethod method,ExceptionInfo *exception)
    839 {
    840 #define ClutImageTag  "Clut/Image"
    841 
    842   CacheView
    843     *clut_view,
    844     *image_view;
    845 
    846   MagickBooleanType
    847     status;
    848 
    849   MagickOffsetType
    850     progress;
    851 
    852   PixelInfo
    853     *clut_map;
    854 
    855   register ssize_t
    856     i;
    857 
    858   ssize_t adjust,
    859     y;
    860 
    861   assert(image != (Image *) NULL);
    862   assert(image->signature == MagickCoreSignature);
    863   if (image->debug != MagickFalse)
    864     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    865   assert(clut_image != (Image *) NULL);
    866   assert(clut_image->signature == MagickCoreSignature);
    867   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
    868     return(MagickFalse);
    869   if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
    870       (IsGrayColorspace(clut_image->colorspace) == MagickFalse))
    871     (void) SetImageColorspace(image,sRGBColorspace,exception);
    872   clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*clut_map));
    873   if (clut_map == (PixelInfo *) NULL)
    874     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
    875       image->filename);
    876   /*
    877     Clut image.
    878   */
    879   status=MagickTrue;
    880   progress=0;
    881   adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
    882   clut_view=AcquireVirtualCacheView(clut_image,exception);
    883   for (i=0; i <= (ssize_t) MaxMap; i++)
    884   {
    885     GetPixelInfo(clut_image,clut_map+i);
    886     status=InterpolatePixelInfo(clut_image,clut_view,method,
    887       (double) i*(clut_image->columns-adjust)/MaxMap,(double) i*
    888       (clut_image->rows-adjust)/MaxMap,clut_map+i,exception);
    889     if (status == MagickFalse)
    890       break;
    891   }
    892   clut_view=DestroyCacheView(clut_view);
    893   image_view=AcquireAuthenticCacheView(image,exception);
    894 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    895   #pragma omp parallel for schedule(static) shared(progress,status) \
    896     magick_number_threads(image,image,image->rows,1)
    897 #endif
    898   for (y=0; y < (ssize_t) image->rows; y++)
    899   {
    900     PixelInfo
    901       pixel;
    902 
    903     register Quantum
    904       *magick_restrict q;
    905 
    906     register ssize_t
    907       x;
    908 
    909     if (status == MagickFalse)
    910       continue;
    911     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    912     if (q == (Quantum *) NULL)
    913       {
    914         status=MagickFalse;
    915         continue;
    916       }
    917     GetPixelInfo(image,&pixel);
    918     for (x=0; x < (ssize_t) image->columns; x++)
    919     {
    920       PixelTrait
    921         traits;
    922 
    923       GetPixelInfoPixel(image,q,&pixel);
    924       traits=GetPixelChannelTraits(image,RedPixelChannel);
    925       if ((traits & UpdatePixelTrait) != 0)
    926         pixel.red=clut_map[ScaleQuantumToMap(ClampToQuantum(
    927           pixel.red))].red;
    928       traits=GetPixelChannelTraits(image,GreenPixelChannel);
    929       if ((traits & UpdatePixelTrait) != 0)
    930         pixel.green=clut_map[ScaleQuantumToMap(ClampToQuantum(
    931           pixel.green))].green;
    932       traits=GetPixelChannelTraits(image,BluePixelChannel);
    933       if ((traits & UpdatePixelTrait) != 0)
    934         pixel.blue=clut_map[ScaleQuantumToMap(ClampToQuantum(
    935           pixel.blue))].blue;
    936       traits=GetPixelChannelTraits(image,BlackPixelChannel);
    937       if ((traits & UpdatePixelTrait) != 0)
    938         pixel.black=clut_map[ScaleQuantumToMap(ClampToQuantum(
    939           pixel.black))].black;
    940       traits=GetPixelChannelTraits(image,AlphaPixelChannel);
    941       if ((traits & UpdatePixelTrait) != 0)
    942         pixel.alpha=clut_map[ScaleQuantumToMap(ClampToQuantum(
    943           pixel.alpha))].alpha;
    944       SetPixelViaPixelInfo(image,&pixel,q);
    945       q+=GetPixelChannels(image);
    946     }
    947     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
    948       status=MagickFalse;
    949     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    950       {
    951         MagickBooleanType
    952           proceed;
    953 
    954 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    955         #pragma omp atomic
    956 #endif
    957         progress++;
    958         proceed=SetImageProgress(image,ClutImageTag,progress,image->rows);
    959         if (proceed == MagickFalse)
    960           status=MagickFalse;
    961       }
    962   }
    963   image_view=DestroyCacheView(image_view);
    964   clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
    965   if ((clut_image->alpha_trait != UndefinedPixelTrait) &&
    966       ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
    967     (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
    968   return(status);
    969 }
    970 
    971 /*
    973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    974 %                                                                             %
    975 %                                                                             %
    976 %                                                                             %
    977 %     C o l o r D e c i s i o n L i s t I m a g e                             %
    978 %                                                                             %
    979 %                                                                             %
    980 %                                                                             %
    981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    982 %
    983 %  ColorDecisionListImage() accepts a lightweight Color Correction Collection
    984 %  (CCC) file which solely contains one or more color corrections and applies
    985 %  the correction to the image.  Here is a sample CCC file:
    986 %
    987 %    <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
    988 %          <ColorCorrection id="cc03345">
    989 %                <SOPNode>
    990 %                     <Slope> 0.9 1.2 0.5 </Slope>
    991 %                     <Offset> 0.4 -0.5 0.6 </Offset>
    992 %                     <Power> 1.0 0.8 1.5 </Power>
    993 %                </SOPNode>
    994 %                <SATNode>
    995 %                     <Saturation> 0.85 </Saturation>
    996 %                </SATNode>
    997 %          </ColorCorrection>
    998 %    </ColorCorrectionCollection>
    999 %
   1000 %  which includes the slop, offset, and power for each of the RGB channels
   1001 %  as well as the saturation.
   1002 %
   1003 %  The format of the ColorDecisionListImage method is:
   1004 %
   1005 %      MagickBooleanType ColorDecisionListImage(Image *image,
   1006 %        const char *color_correction_collection,ExceptionInfo *exception)
   1007 %
   1008 %  A description of each parameter follows:
   1009 %
   1010 %    o image: the image.
   1011 %
   1012 %    o color_correction_collection: the color correction collection in XML.
   1013 %
   1014 %    o exception: return any errors or warnings in this structure.
   1015 %
   1016 */
   1017 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
   1018   const char *color_correction_collection,ExceptionInfo *exception)
   1019 {
   1020 #define ColorDecisionListCorrectImageTag  "ColorDecisionList/Image"
   1021 
   1022   typedef struct _Correction
   1023   {
   1024     double
   1025       slope,
   1026       offset,
   1027       power;
   1028   } Correction;
   1029 
   1030   typedef struct _ColorCorrection
   1031   {
   1032     Correction
   1033       red,
   1034       green,
   1035       blue;
   1036 
   1037     double
   1038       saturation;
   1039   } ColorCorrection;
   1040 
   1041   CacheView
   1042     *image_view;
   1043 
   1044   char
   1045     token[MagickPathExtent];
   1046 
   1047   ColorCorrection
   1048     color_correction;
   1049 
   1050   const char
   1051     *content,
   1052     *p;
   1053 
   1054   MagickBooleanType
   1055     status;
   1056 
   1057   MagickOffsetType
   1058     progress;
   1059 
   1060   PixelInfo
   1061     *cdl_map;
   1062 
   1063   register ssize_t
   1064     i;
   1065 
   1066   ssize_t
   1067     y;
   1068 
   1069   XMLTreeInfo
   1070     *cc,
   1071     *ccc,
   1072     *sat,
   1073     *sop;
   1074 
   1075   /*
   1076     Allocate and initialize cdl maps.
   1077   */
   1078   assert(image != (Image *) NULL);
   1079   assert(image->signature == MagickCoreSignature);
   1080   if (image->debug != MagickFalse)
   1081     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1082   if (color_correction_collection == (const char *) NULL)
   1083     return(MagickFalse);
   1084   ccc=NewXMLTree((const char *) color_correction_collection,exception);
   1085   if (ccc == (XMLTreeInfo *) NULL)
   1086     return(MagickFalse);
   1087   cc=GetXMLTreeChild(ccc,"ColorCorrection");
   1088   if (cc == (XMLTreeInfo *) NULL)
   1089     {
   1090       ccc=DestroyXMLTree(ccc);
   1091       return(MagickFalse);
   1092     }
   1093   color_correction.red.slope=1.0;
   1094   color_correction.red.offset=0.0;
   1095   color_correction.red.power=1.0;
   1096   color_correction.green.slope=1.0;
   1097   color_correction.green.offset=0.0;
   1098   color_correction.green.power=1.0;
   1099   color_correction.blue.slope=1.0;
   1100   color_correction.blue.offset=0.0;
   1101   color_correction.blue.power=1.0;
   1102   color_correction.saturation=0.0;
   1103   sop=GetXMLTreeChild(cc,"SOPNode");
   1104   if (sop != (XMLTreeInfo *) NULL)
   1105     {
   1106       XMLTreeInfo
   1107         *offset,
   1108         *power,
   1109         *slope;
   1110 
   1111       slope=GetXMLTreeChild(sop,"Slope");
   1112       if (slope != (XMLTreeInfo *) NULL)
   1113         {
   1114           content=GetXMLTreeContent(slope);
   1115           p=(const char *) content;
   1116           for (i=0; (*p != '\0') && (i < 3); i++)
   1117           {
   1118             GetNextToken(p,&p,MagickPathExtent,token);
   1119             if (*token == ',')
   1120               GetNextToken(p,&p,MagickPathExtent,token);
   1121             switch (i)
   1122             {
   1123               case 0:
   1124               {
   1125                 color_correction.red.slope=StringToDouble(token,(char **) NULL);
   1126                 break;
   1127               }
   1128               case 1:
   1129               {
   1130                 color_correction.green.slope=StringToDouble(token,
   1131                   (char **) NULL);
   1132                 break;
   1133               }
   1134               case 2:
   1135               {
   1136                 color_correction.blue.slope=StringToDouble(token,
   1137                   (char **) NULL);
   1138                 break;
   1139               }
   1140             }
   1141           }
   1142         }
   1143       offset=GetXMLTreeChild(sop,"Offset");
   1144       if (offset != (XMLTreeInfo *) NULL)
   1145         {
   1146           content=GetXMLTreeContent(offset);
   1147           p=(const char *) content;
   1148           for (i=0; (*p != '\0') && (i < 3); i++)
   1149           {
   1150             GetNextToken(p,&p,MagickPathExtent,token);
   1151             if (*token == ',')
   1152               GetNextToken(p,&p,MagickPathExtent,token);
   1153             switch (i)
   1154             {
   1155               case 0:
   1156               {
   1157                 color_correction.red.offset=StringToDouble(token,
   1158                   (char **) NULL);
   1159                 break;
   1160               }
   1161               case 1:
   1162               {
   1163                 color_correction.green.offset=StringToDouble(token,
   1164                   (char **) NULL);
   1165                 break;
   1166               }
   1167               case 2:
   1168               {
   1169                 color_correction.blue.offset=StringToDouble(token,
   1170                   (char **) NULL);
   1171                 break;
   1172               }
   1173             }
   1174           }
   1175         }
   1176       power=GetXMLTreeChild(sop,"Power");
   1177       if (power != (XMLTreeInfo *) NULL)
   1178         {
   1179           content=GetXMLTreeContent(power);
   1180           p=(const char *) content;
   1181           for (i=0; (*p != '\0') && (i < 3); i++)
   1182           {
   1183             GetNextToken(p,&p,MagickPathExtent,token);
   1184             if (*token == ',')
   1185               GetNextToken(p,&p,MagickPathExtent,token);
   1186             switch (i)
   1187             {
   1188               case 0:
   1189               {
   1190                 color_correction.red.power=StringToDouble(token,(char **) NULL);
   1191                 break;
   1192               }
   1193               case 1:
   1194               {
   1195                 color_correction.green.power=StringToDouble(token,
   1196                   (char **) NULL);
   1197                 break;
   1198               }
   1199               case 2:
   1200               {
   1201                 color_correction.blue.power=StringToDouble(token,
   1202                   (char **) NULL);
   1203                 break;
   1204               }
   1205             }
   1206           }
   1207         }
   1208     }
   1209   sat=GetXMLTreeChild(cc,"SATNode");
   1210   if (sat != (XMLTreeInfo *) NULL)
   1211     {
   1212       XMLTreeInfo
   1213         *saturation;
   1214 
   1215       saturation=GetXMLTreeChild(sat,"Saturation");
   1216       if (saturation != (XMLTreeInfo *) NULL)
   1217         {
   1218           content=GetXMLTreeContent(saturation);
   1219           p=(const char *) content;
   1220           GetNextToken(p,&p,MagickPathExtent,token);
   1221           color_correction.saturation=StringToDouble(token,(char **) NULL);
   1222         }
   1223     }
   1224   ccc=DestroyXMLTree(ccc);
   1225   if (image->debug != MagickFalse)
   1226     {
   1227       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1228         "  Color Correction Collection:");
   1229       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1230         "  color_correction.red.slope: %g",color_correction.red.slope);
   1231       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1232         "  color_correction.red.offset: %g",color_correction.red.offset);
   1233       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1234         "  color_correction.red.power: %g",color_correction.red.power);
   1235       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1236         "  color_correction.green.slope: %g",color_correction.green.slope);
   1237       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1238         "  color_correction.green.offset: %g",color_correction.green.offset);
   1239       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1240         "  color_correction.green.power: %g",color_correction.green.power);
   1241       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1242         "  color_correction.blue.slope: %g",color_correction.blue.slope);
   1243       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1244         "  color_correction.blue.offset: %g",color_correction.blue.offset);
   1245       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1246         "  color_correction.blue.power: %g",color_correction.blue.power);
   1247       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
   1248         "  color_correction.saturation: %g",color_correction.saturation);
   1249     }
   1250   cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
   1251   if (cdl_map == (PixelInfo *) NULL)
   1252     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
   1253       image->filename);
   1254   for (i=0; i <= (ssize_t) MaxMap; i++)
   1255   {
   1256     cdl_map[i].red=(double) ScaleMapToQuantum((double)
   1257       (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
   1258       color_correction.red.offset,color_correction.red.power))));
   1259     cdl_map[i].green=(double) ScaleMapToQuantum((double)
   1260       (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
   1261       color_correction.green.offset,color_correction.green.power))));
   1262     cdl_map[i].blue=(double) ScaleMapToQuantum((double)
   1263       (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
   1264       color_correction.blue.offset,color_correction.blue.power))));
   1265   }
   1266   if (image->storage_class == PseudoClass)
   1267     for (i=0; i < (ssize_t) image->colors; i++)
   1268     {
   1269       /*
   1270         Apply transfer function to colormap.
   1271       */
   1272       double
   1273         luma;
   1274 
   1275       luma=0.21267f*image->colormap[i].red+0.71526*image->colormap[i].green+
   1276         0.07217f*image->colormap[i].blue;
   1277       image->colormap[i].red=luma+color_correction.saturation*cdl_map[
   1278         ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
   1279       image->colormap[i].green=luma+color_correction.saturation*cdl_map[
   1280         ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
   1281       image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
   1282         ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
   1283     }
   1284   /*
   1285     Apply transfer function to image.
   1286   */
   1287   status=MagickTrue;
   1288   progress=0;
   1289   image_view=AcquireAuthenticCacheView(image,exception);
   1290 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1291   #pragma omp parallel for schedule(static) shared(progress,status) \
   1292     magick_number_threads(image,image,image->rows,1)
   1293 #endif
   1294   for (y=0; y < (ssize_t) image->rows; y++)
   1295   {
   1296     double
   1297       luma;
   1298 
   1299     register Quantum
   1300       *magick_restrict q;
   1301 
   1302     register ssize_t
   1303       x;
   1304 
   1305     if (status == MagickFalse)
   1306       continue;
   1307     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   1308     if (q == (Quantum *) NULL)
   1309       {
   1310         status=MagickFalse;
   1311         continue;
   1312       }
   1313     for (x=0; x < (ssize_t) image->columns; x++)
   1314     {
   1315       luma=0.21267f*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+
   1316         0.07217f*GetPixelBlue(image,q);
   1317       SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
   1318         (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
   1319       SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
   1320         (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
   1321       SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
   1322         (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
   1323       q+=GetPixelChannels(image);
   1324     }
   1325     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   1326       status=MagickFalse;
   1327     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1328       {
   1329         MagickBooleanType
   1330           proceed;
   1331 
   1332 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1333         #pragma omp atomic
   1334 #endif
   1335         progress++;
   1336         proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
   1337           progress,image->rows);
   1338         if (proceed == MagickFalse)
   1339           status=MagickFalse;
   1340       }
   1341   }
   1342   image_view=DestroyCacheView(image_view);
   1343   cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
   1344   return(status);
   1345 }
   1346 
   1347 /*
   1349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1350 %                                                                             %
   1351 %                                                                             %
   1352 %                                                                             %
   1353 %     C o n t r a s t I m a g e                                               %
   1354 %                                                                             %
   1355 %                                                                             %
   1356 %                                                                             %
   1357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1358 %
   1359 %  ContrastImage() enhances the intensity differences between the lighter and
   1360 %  darker elements of the image.  Set sharpen to a MagickTrue to increase the
   1361 %  image contrast otherwise the contrast is reduced.
   1362 %
   1363 %  The format of the ContrastImage method is:
   1364 %
   1365 %      MagickBooleanType ContrastImage(Image *image,
   1366 %        const MagickBooleanType sharpen,ExceptionInfo *exception)
   1367 %
   1368 %  A description of each parameter follows:
   1369 %
   1370 %    o image: the image.
   1371 %
   1372 %    o sharpen: Increase or decrease image contrast.
   1373 %
   1374 %    o exception: return any errors or warnings in this structure.
   1375 %
   1376 */
   1377 
   1378 static void Contrast(const int sign,double *red,double *green,double *blue)
   1379 {
   1380   double
   1381     brightness,
   1382     hue,
   1383     saturation;
   1384 
   1385   /*
   1386     Enhance contrast: dark color become darker, light color become lighter.
   1387   */
   1388   assert(red != (double *) NULL);
   1389   assert(green != (double *) NULL);
   1390   assert(blue != (double *) NULL);
   1391   hue=0.0;
   1392   saturation=0.0;
   1393   brightness=0.0;
   1394   ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
   1395   brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
   1396     brightness);
   1397   if (brightness > 1.0)
   1398     brightness=1.0;
   1399   else
   1400     if (brightness < 0.0)
   1401       brightness=0.0;
   1402   ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
   1403 }
   1404 
   1405 MagickExport MagickBooleanType ContrastImage(Image *image,
   1406   const MagickBooleanType sharpen,ExceptionInfo *exception)
   1407 {
   1408 #define ContrastImageTag  "Contrast/Image"
   1409 
   1410   CacheView
   1411     *image_view;
   1412 
   1413   int
   1414     sign;
   1415 
   1416   MagickBooleanType
   1417     status;
   1418 
   1419   MagickOffsetType
   1420     progress;
   1421 
   1422   register ssize_t
   1423     i;
   1424 
   1425   ssize_t
   1426     y;
   1427 
   1428   assert(image != (Image *) NULL);
   1429   assert(image->signature == MagickCoreSignature);
   1430 #if defined(MAGICKCORE_OPENCL_SUPPORT)
   1431   if (AccelerateContrastImage(image,sharpen,exception) != MagickFalse)
   1432     return(MagickTrue);
   1433 #endif
   1434   if (image->debug != MagickFalse)
   1435     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1436   sign=sharpen != MagickFalse ? 1 : -1;
   1437   if (image->storage_class == PseudoClass)
   1438     {
   1439       /*
   1440         Contrast enhance colormap.
   1441       */
   1442       for (i=0; i < (ssize_t) image->colors; i++)
   1443       {
   1444         double
   1445           blue,
   1446           green,
   1447           red;
   1448 
   1449         red=(double) image->colormap[i].red;
   1450         green=(double) image->colormap[i].green;
   1451         blue=(double) image->colormap[i].blue;
   1452         Contrast(sign,&red,&green,&blue);
   1453         image->colormap[i].red=(MagickRealType) red;
   1454         image->colormap[i].green=(MagickRealType) green;
   1455         image->colormap[i].blue=(MagickRealType) blue;
   1456       }
   1457     }
   1458   /*
   1459     Contrast enhance image.
   1460   */
   1461   status=MagickTrue;
   1462   progress=0;
   1463   image_view=AcquireAuthenticCacheView(image,exception);
   1464 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1465   #pragma omp parallel for schedule(static) shared(progress,status) \
   1466     magick_number_threads(image,image,image->rows,1)
   1467 #endif
   1468   for (y=0; y < (ssize_t) image->rows; y++)
   1469   {
   1470     double
   1471       blue,
   1472       green,
   1473       red;
   1474 
   1475     register Quantum
   1476       *magick_restrict q;
   1477 
   1478     register ssize_t
   1479       x;
   1480 
   1481     if (status == MagickFalse)
   1482       continue;
   1483     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   1484     if (q == (Quantum *) NULL)
   1485       {
   1486         status=MagickFalse;
   1487         continue;
   1488       }
   1489     for (x=0; x < (ssize_t) image->columns; x++)
   1490     {
   1491       red=(double) GetPixelRed(image,q);
   1492       green=(double) GetPixelGreen(image,q);
   1493       blue=(double) GetPixelBlue(image,q);
   1494       Contrast(sign,&red,&green,&blue);
   1495       SetPixelRed(image,ClampToQuantum(red),q);
   1496       SetPixelGreen(image,ClampToQuantum(green),q);
   1497       SetPixelBlue(image,ClampToQuantum(blue),q);
   1498       q+=GetPixelChannels(image);
   1499     }
   1500     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   1501       status=MagickFalse;
   1502     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1503       {
   1504         MagickBooleanType
   1505           proceed;
   1506 
   1507 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1508         #pragma omp atomic
   1509 #endif
   1510         progress++;
   1511         proceed=SetImageProgress(image,ContrastImageTag,progress,image->rows);
   1512         if (proceed == MagickFalse)
   1513           status=MagickFalse;
   1514       }
   1515   }
   1516   image_view=DestroyCacheView(image_view);
   1517   return(status);
   1518 }
   1519 
   1520 /*
   1522 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1523 %                                                                             %
   1524 %                                                                             %
   1525 %                                                                             %
   1526 %     C o n t r a s t S t r e t c h I m a g e                                 %
   1527 %                                                                             %
   1528 %                                                                             %
   1529 %                                                                             %
   1530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1531 %
   1532 %  ContrastStretchImage() is a simple image enhancement technique that attempts
   1533 %  to improve the contrast in an image by 'stretching' the range of intensity
   1534 %  values it contains to span a desired range of values. It differs from the
   1535 %  more sophisticated histogram equalization in that it can only apply a
   1536 %  linear scaling function to the image pixel values.  As a result the
   1537 %  'enhancement' is less harsh.
   1538 %
   1539 %  The format of the ContrastStretchImage method is:
   1540 %
   1541 %      MagickBooleanType ContrastStretchImage(Image *image,
   1542 %        const char *levels,ExceptionInfo *exception)
   1543 %
   1544 %  A description of each parameter follows:
   1545 %
   1546 %    o image: the image.
   1547 %
   1548 %    o black_point: the black point.
   1549 %
   1550 %    o white_point: the white point.
   1551 %
   1552 %    o levels: Specify the levels where the black and white points have the
   1553 %      range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
   1554 %
   1555 %    o exception: return any errors or warnings in this structure.
   1556 %
   1557 */
   1558 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
   1559   const double black_point,const double white_point,ExceptionInfo *exception)
   1560 {
   1561 #define MaxRange(color)  ((double) ScaleQuantumToMap((Quantum) (color)))
   1562 #define ContrastStretchImageTag  "ContrastStretch/Image"
   1563 
   1564   CacheView
   1565     *image_view;
   1566 
   1567   double
   1568     *black,
   1569     *histogram,
   1570     *stretch_map,
   1571     *white;
   1572 
   1573   MagickBooleanType
   1574     status;
   1575 
   1576   MagickOffsetType
   1577     progress;
   1578 
   1579   register ssize_t
   1580     i;
   1581 
   1582   ssize_t
   1583     y;
   1584 
   1585   /*
   1586     Allocate histogram and stretch map.
   1587   */
   1588   assert(image != (Image *) NULL);
   1589   assert(image->signature == MagickCoreSignature);
   1590   if (image->debug != MagickFalse)
   1591     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1592   if (SetImageGray(image,exception) != MagickFalse)
   1593     (void) SetImageColorspace(image,GRAYColorspace,exception);
   1594   black=(double *) AcquireQuantumMemory(MaxPixelChannels,sizeof(*black));
   1595   white=(double *) AcquireQuantumMemory(MaxPixelChannels,sizeof(*white));
   1596   histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
   1597     sizeof(*histogram));
   1598   stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
   1599     sizeof(*stretch_map));
   1600   if ((black == (double *) NULL) || (white == (double *) NULL) ||
   1601       (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
   1602     {
   1603       if (stretch_map != (double *) NULL)
   1604         stretch_map=(double *) RelinquishMagickMemory(stretch_map);
   1605       if (histogram != (double *) NULL)
   1606         histogram=(double *) RelinquishMagickMemory(histogram);
   1607       if (white != (double *) NULL)
   1608         white=(double *) RelinquishMagickMemory(white);
   1609       if (black != (double *) NULL)
   1610         black=(double *) RelinquishMagickMemory(black);
   1611       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
   1612         image->filename);
   1613     }
   1614   /*
   1615     Form histogram.
   1616   */
   1617   status=MagickTrue;
   1618   (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
   1619     sizeof(*histogram));
   1620   image_view=AcquireVirtualCacheView(image,exception);
   1621   for (y=0; y < (ssize_t) image->rows; y++)
   1622   {
   1623     register const Quantum
   1624       *magick_restrict p;
   1625 
   1626     register ssize_t
   1627       x;
   1628 
   1629     if (status == MagickFalse)
   1630       continue;
   1631     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   1632     if (p == (const Quantum *) NULL)
   1633       {
   1634         status=MagickFalse;
   1635         continue;
   1636       }
   1637     for (x=0; x < (ssize_t) image->columns; x++)
   1638     {
   1639       double
   1640         pixel;
   1641 
   1642       pixel=GetPixelIntensity(image,p);
   1643       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1644       {
   1645         if (image->channel_mask != DefaultChannels)
   1646           pixel=(double) p[i];
   1647         histogram[GetPixelChannels(image)*ScaleQuantumToMap(
   1648           ClampToQuantum(pixel))+i]++;
   1649       }
   1650       p+=GetPixelChannels(image);
   1651     }
   1652   }
   1653   image_view=DestroyCacheView(image_view);
   1654   /*
   1655     Find the histogram boundaries by locating the black/white levels.
   1656   */
   1657   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1658   {
   1659     double
   1660       intensity;
   1661 
   1662     register ssize_t
   1663       j;
   1664 
   1665     black[i]=0.0;
   1666     white[i]=MaxRange(QuantumRange);
   1667     intensity=0.0;
   1668     for (j=0; j <= (ssize_t) MaxMap; j++)
   1669     {
   1670       intensity+=histogram[GetPixelChannels(image)*j+i];
   1671       if (intensity > black_point)
   1672         break;
   1673     }
   1674     black[i]=(double) j;
   1675     intensity=0.0;
   1676     for (j=(ssize_t) MaxMap; j != 0; j--)
   1677     {
   1678       intensity+=histogram[GetPixelChannels(image)*j+i];
   1679       if (intensity > ((double) image->columns*image->rows-white_point))
   1680         break;
   1681     }
   1682     white[i]=(double) j;
   1683   }
   1684   histogram=(double *) RelinquishMagickMemory(histogram);
   1685   /*
   1686     Stretch the histogram to create the stretched image mapping.
   1687   */
   1688   (void) memset(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
   1689     sizeof(*stretch_map));
   1690   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1691   {
   1692     register ssize_t
   1693       j;
   1694 
   1695     for (j=0; j <= (ssize_t) MaxMap; j++)
   1696     {
   1697       double
   1698         gamma;
   1699 
   1700       gamma=PerceptibleReciprocal(white[i]-black[i]);
   1701       if (j < (ssize_t) black[i])
   1702         stretch_map[GetPixelChannels(image)*j+i]=0.0;
   1703       else
   1704         if (j > (ssize_t) white[i])
   1705           stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange;
   1706         else
   1707           if (black[i] != white[i])
   1708             stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum(
   1709               (double) (MaxMap*gamma*(j-black[i])));
   1710     }
   1711   }
   1712   if (image->storage_class == PseudoClass)
   1713     {
   1714       register ssize_t
   1715         j;
   1716 
   1717       /*
   1718         Stretch-contrast colormap.
   1719       */
   1720       for (j=0; j < (ssize_t) image->colors; j++)
   1721       {
   1722         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   1723           {
   1724             i=GetPixelChannelOffset(image,RedPixelChannel);
   1725             image->colormap[j].red=stretch_map[GetPixelChannels(image)*
   1726               ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+i];
   1727           }
   1728         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   1729           {
   1730             i=GetPixelChannelOffset(image,GreenPixelChannel);
   1731             image->colormap[j].green=stretch_map[GetPixelChannels(image)*
   1732               ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+i];
   1733           }
   1734         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   1735           {
   1736             i=GetPixelChannelOffset(image,BluePixelChannel);
   1737             image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
   1738               ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+i];
   1739           }
   1740         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
   1741           {
   1742             i=GetPixelChannelOffset(image,AlphaPixelChannel);
   1743             image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
   1744               ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+i];
   1745           }
   1746       }
   1747     }
   1748   /*
   1749     Stretch-contrast image.
   1750   */
   1751   status=MagickTrue;
   1752   progress=0;
   1753   image_view=AcquireAuthenticCacheView(image,exception);
   1754 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1755   #pragma omp parallel for schedule(static) shared(progress,status) \
   1756     magick_number_threads(image,image,image->rows,1)
   1757 #endif
   1758   for (y=0; y < (ssize_t) image->rows; y++)
   1759   {
   1760     register Quantum
   1761       *magick_restrict q;
   1762 
   1763     register ssize_t
   1764       x;
   1765 
   1766     if (status == MagickFalse)
   1767       continue;
   1768     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   1769     if (q == (Quantum *) NULL)
   1770       {
   1771         status=MagickFalse;
   1772         continue;
   1773       }
   1774     for (x=0; x < (ssize_t) image->columns; x++)
   1775     {
   1776       register ssize_t
   1777         j;
   1778 
   1779       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
   1780       {
   1781         PixelChannel channel = GetPixelChannelChannel(image,j);
   1782         PixelTrait traits = GetPixelChannelTraits(image,channel);
   1783         if ((traits & UpdatePixelTrait) == 0)
   1784           continue;
   1785         if (black[j] == white[j])
   1786           continue;
   1787         q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
   1788           ScaleQuantumToMap(q[j])+j]);
   1789       }
   1790       q+=GetPixelChannels(image);
   1791     }
   1792     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   1793       status=MagickFalse;
   1794     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1795       {
   1796         MagickBooleanType
   1797           proceed;
   1798 
   1799 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1800         #pragma omp atomic
   1801 #endif
   1802         progress++;
   1803         proceed=SetImageProgress(image,ContrastStretchImageTag,progress,
   1804           image->rows);
   1805         if (proceed == MagickFalse)
   1806           status=MagickFalse;
   1807       }
   1808   }
   1809   image_view=DestroyCacheView(image_view);
   1810   stretch_map=(double *) RelinquishMagickMemory(stretch_map);
   1811   white=(double *) RelinquishMagickMemory(white);
   1812   black=(double *) RelinquishMagickMemory(black);
   1813   return(status);
   1814 }
   1815 
   1816 /*
   1818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1819 %                                                                             %
   1820 %                                                                             %
   1821 %                                                                             %
   1822 %     E n h a n c e I m a g e                                                 %
   1823 %                                                                             %
   1824 %                                                                             %
   1825 %                                                                             %
   1826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1827 %
   1828 %  EnhanceImage() applies a digital filter that improves the quality of a
   1829 %  noisy image.
   1830 %
   1831 %  The format of the EnhanceImage method is:
   1832 %
   1833 %      Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
   1834 %
   1835 %  A description of each parameter follows:
   1836 %
   1837 %    o image: the image.
   1838 %
   1839 %    o exception: return any errors or warnings in this structure.
   1840 %
   1841 */
   1842 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
   1843 {
   1844 #define EnhanceImageTag  "Enhance/Image"
   1845 #define EnhancePixel(weight) \
   1846   mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
   1847   distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
   1848   distance_squared=(4.0+mean)*distance*distance; \
   1849   mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
   1850   distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
   1851   distance_squared+=(7.0-mean)*distance*distance; \
   1852   mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
   1853   distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
   1854   distance_squared+=(5.0-mean)*distance*distance; \
   1855   mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
   1856   distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
   1857   distance_squared+=(5.0-mean)*distance*distance; \
   1858   mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
   1859   distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
   1860   distance_squared+=(5.0-mean)*distance*distance; \
   1861   if (distance_squared < 0.069) \
   1862     { \
   1863       aggregate.red+=(weight)*GetPixelRed(image,r); \
   1864       aggregate.green+=(weight)*GetPixelGreen(image,r); \
   1865       aggregate.blue+=(weight)*GetPixelBlue(image,r); \
   1866       aggregate.black+=(weight)*GetPixelBlack(image,r); \
   1867       aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \
   1868       total_weight+=(weight); \
   1869     } \
   1870   r+=GetPixelChannels(image);
   1871 
   1872   CacheView
   1873     *enhance_view,
   1874     *image_view;
   1875 
   1876   Image
   1877     *enhance_image;
   1878 
   1879   MagickBooleanType
   1880     status;
   1881 
   1882   MagickOffsetType
   1883     progress;
   1884 
   1885   ssize_t
   1886     y;
   1887 
   1888   /*
   1889     Initialize enhanced image attributes.
   1890   */
   1891   assert(image != (const Image *) NULL);
   1892   assert(image->signature == MagickCoreSignature);
   1893   if (image->debug != MagickFalse)
   1894     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1895   assert(exception != (ExceptionInfo *) NULL);
   1896   assert(exception->signature == MagickCoreSignature);
   1897   enhance_image=CloneImage(image,0,0,MagickTrue,
   1898     exception);
   1899   if (enhance_image == (Image *) NULL)
   1900     return((Image *) NULL);
   1901   if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
   1902     {
   1903       enhance_image=DestroyImage(enhance_image);
   1904       return((Image *) NULL);
   1905     }
   1906   /*
   1907     Enhance image.
   1908   */
   1909   status=MagickTrue;
   1910   progress=0;
   1911   image_view=AcquireVirtualCacheView(image,exception);
   1912   enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
   1913 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1914   #pragma omp parallel for schedule(static) shared(progress,status) \
   1915     magick_number_threads(image,enhance_image,image->rows,1)
   1916 #endif
   1917   for (y=0; y < (ssize_t) image->rows; y++)
   1918   {
   1919     PixelInfo
   1920       pixel;
   1921 
   1922     register const Quantum
   1923       *magick_restrict p;
   1924 
   1925     register Quantum
   1926       *magick_restrict q;
   1927 
   1928     register ssize_t
   1929       x;
   1930 
   1931     ssize_t
   1932       center;
   1933 
   1934     if (status == MagickFalse)
   1935       continue;
   1936     p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
   1937     q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
   1938       exception);
   1939     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   1940       {
   1941         status=MagickFalse;
   1942         continue;
   1943       }
   1944     center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
   1945     GetPixelInfo(image,&pixel);
   1946     for (x=0; x < (ssize_t) image->columns; x++)
   1947     {
   1948       double
   1949         distance,
   1950         distance_squared,
   1951         mean,
   1952         total_weight;
   1953 
   1954       PixelInfo
   1955         aggregate;
   1956 
   1957       register const Quantum
   1958         *magick_restrict r;
   1959 
   1960       GetPixelInfo(image,&aggregate);
   1961       total_weight=0.0;
   1962       GetPixelInfoPixel(image,p+center,&pixel);
   1963       r=p;
   1964       EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
   1965         EnhancePixel(8.0); EnhancePixel(5.0);
   1966       r=p+GetPixelChannels(image)*(image->columns+4);
   1967       EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
   1968         EnhancePixel(20.0); EnhancePixel(8.0);
   1969       r=p+2*GetPixelChannels(image)*(image->columns+4);
   1970       EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
   1971         EnhancePixel(40.0); EnhancePixel(10.0);
   1972       r=p+3*GetPixelChannels(image)*(image->columns+4);
   1973       EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
   1974         EnhancePixel(20.0); EnhancePixel(8.0);
   1975       r=p+4*GetPixelChannels(image)*(image->columns+4);
   1976       EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
   1977         EnhancePixel(8.0); EnhancePixel(5.0);
   1978       if (total_weight > MagickEpsilon)
   1979         {
   1980           pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
   1981           pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
   1982           pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
   1983           pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
   1984           pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
   1985         }
   1986       SetPixelViaPixelInfo(image,&pixel,q);
   1987       p+=GetPixelChannels(image);
   1988       q+=GetPixelChannels(enhance_image);
   1989     }
   1990     if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
   1991       status=MagickFalse;
   1992     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1993       {
   1994         MagickBooleanType
   1995           proceed;
   1996 
   1997 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1998         #pragma omp atomic
   1999 #endif
   2000         progress++;
   2001         proceed=SetImageProgress(image,EnhanceImageTag,progress,image->rows);
   2002         if (proceed == MagickFalse)
   2003           status=MagickFalse;
   2004       }
   2005   }
   2006   enhance_view=DestroyCacheView(enhance_view);
   2007   image_view=DestroyCacheView(image_view);
   2008   if (status == MagickFalse)
   2009     enhance_image=DestroyImage(enhance_image);
   2010   return(enhance_image);
   2011 }
   2012 
   2013 /*
   2015 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2016 %                                                                             %
   2017 %                                                                             %
   2018 %                                                                             %
   2019 %     E q u a l i z e I m a g e                                               %
   2020 %                                                                             %
   2021 %                                                                             %
   2022 %                                                                             %
   2023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2024 %
   2025 %  EqualizeImage() applies a histogram equalization to the image.
   2026 %
   2027 %  The format of the EqualizeImage method is:
   2028 %
   2029 %      MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
   2030 %
   2031 %  A description of each parameter follows:
   2032 %
   2033 %    o image: the image.
   2034 %
   2035 %    o exception: return any errors or warnings in this structure.
   2036 %
   2037 */
   2038 MagickExport MagickBooleanType EqualizeImage(Image *image,
   2039   ExceptionInfo *exception)
   2040 {
   2041 #define EqualizeImageTag  "Equalize/Image"
   2042 
   2043   CacheView
   2044     *image_view;
   2045 
   2046   double
   2047     black[CompositePixelChannel+1],
   2048     *equalize_map,
   2049     *histogram,
   2050     *map,
   2051     white[CompositePixelChannel+1];
   2052 
   2053   MagickBooleanType
   2054     status;
   2055 
   2056   MagickOffsetType
   2057     progress;
   2058 
   2059   register ssize_t
   2060     i;
   2061 
   2062   ssize_t
   2063     y;
   2064 
   2065   /*
   2066     Allocate and initialize histogram arrays.
   2067   */
   2068   assert(image != (Image *) NULL);
   2069   assert(image->signature == MagickCoreSignature);
   2070 #if defined(MAGICKCORE_OPENCL_SUPPORT)
   2071   if (AccelerateEqualizeImage(image,exception) != MagickFalse)
   2072     return(MagickTrue);
   2073 #endif
   2074   if (image->debug != MagickFalse)
   2075     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2076   equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
   2077     sizeof(*equalize_map));
   2078   histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
   2079     sizeof(*histogram));
   2080   map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*sizeof(*map));
   2081   if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
   2082       (map == (double *) NULL))
   2083     {
   2084       if (map != (double *) NULL)
   2085         map=(double *) RelinquishMagickMemory(map);
   2086       if (histogram != (double *) NULL)
   2087         histogram=(double *) RelinquishMagickMemory(histogram);
   2088       if (equalize_map != (double *) NULL)
   2089         equalize_map=(double *) RelinquishMagickMemory(equalize_map);
   2090       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
   2091         image->filename);
   2092     }
   2093   /*
   2094     Form histogram.
   2095   */
   2096   status=MagickTrue;
   2097   (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
   2098     sizeof(*histogram));
   2099   image_view=AcquireVirtualCacheView(image,exception);
   2100   for (y=0; y < (ssize_t) image->rows; y++)
   2101   {
   2102     register const Quantum
   2103       *magick_restrict p;
   2104 
   2105     register ssize_t
   2106       x;
   2107 
   2108     if (status == MagickFalse)
   2109       continue;
   2110     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   2111     if (p == (const Quantum *) NULL)
   2112       {
   2113         status=MagickFalse;
   2114         continue;
   2115       }
   2116     for (x=0; x < (ssize_t) image->columns; x++)
   2117     {
   2118       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2119       {
   2120         double
   2121           intensity;
   2122 
   2123         intensity=(double) p[i];
   2124         if ((image->channel_mask & SyncChannels) != 0)
   2125           intensity=GetPixelIntensity(image,p);
   2126         histogram[GetPixelChannels(image)*ScaleQuantumToMap(
   2127           ClampToQuantum(intensity))+i]++;
   2128       }
   2129       p+=GetPixelChannels(image);
   2130     }
   2131   }
   2132   image_view=DestroyCacheView(image_view);
   2133   /*
   2134     Integrate the histogram to get the equalization map.
   2135   */
   2136   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2137   {
   2138     double
   2139       intensity;
   2140 
   2141     register ssize_t
   2142       j;
   2143 
   2144     intensity=0.0;
   2145     for (j=0; j <= (ssize_t) MaxMap; j++)
   2146     {
   2147       intensity+=histogram[GetPixelChannels(image)*j+i];
   2148       map[GetPixelChannels(image)*j+i]=intensity;
   2149     }
   2150   }
   2151   (void) memset(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
   2152     sizeof(*equalize_map));
   2153   (void) memset(black,0,sizeof(*black));
   2154   (void) memset(white,0,sizeof(*white));
   2155   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2156   {
   2157     register ssize_t
   2158       j;
   2159 
   2160     black[i]=map[i];
   2161     white[i]=map[GetPixelChannels(image)*MaxMap+i];
   2162     if (black[i] != white[i])
   2163       for (j=0; j <= (ssize_t) MaxMap; j++)
   2164         equalize_map[GetPixelChannels(image)*j+i]=(double)
   2165           ScaleMapToQuantum((double) ((MaxMap*(map[
   2166           GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
   2167   }
   2168   histogram=(double *) RelinquishMagickMemory(histogram);
   2169   map=(double *) RelinquishMagickMemory(map);
   2170   if (image->storage_class == PseudoClass)
   2171     {
   2172       register ssize_t
   2173         j;
   2174 
   2175       /*
   2176         Equalize colormap.
   2177       */
   2178       for (j=0; j < (ssize_t) image->colors; j++)
   2179       {
   2180         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   2181           {
   2182             PixelChannel channel = GetPixelChannelChannel(image,
   2183               RedPixelChannel);
   2184             if (black[channel] != white[channel])
   2185               image->colormap[j].red=equalize_map[GetPixelChannels(image)*
   2186                 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+
   2187                 channel];
   2188           }
   2189         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   2190           {
   2191             PixelChannel channel = GetPixelChannelChannel(image,
   2192               GreenPixelChannel);
   2193             if (black[channel] != white[channel])
   2194               image->colormap[j].green=equalize_map[GetPixelChannels(image)*
   2195                 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+
   2196                 channel];
   2197           }
   2198         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   2199           {
   2200             PixelChannel channel = GetPixelChannelChannel(image,
   2201               BluePixelChannel);
   2202             if (black[channel] != white[channel])
   2203               image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
   2204                 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+
   2205                 channel];
   2206           }
   2207         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
   2208           {
   2209             PixelChannel channel = GetPixelChannelChannel(image,
   2210               AlphaPixelChannel);
   2211             if (black[channel] != white[channel])
   2212               image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
   2213                 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+
   2214                 channel];
   2215           }
   2216       }
   2217     }
   2218   /*
   2219     Equalize image.
   2220   */
   2221   progress=0;
   2222   image_view=AcquireAuthenticCacheView(image,exception);
   2223 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2224   #pragma omp parallel for schedule(static) shared(progress,status) \
   2225     magick_number_threads(image,image,image->rows,1)
   2226 #endif
   2227   for (y=0; y < (ssize_t) image->rows; y++)
   2228   {
   2229     register Quantum
   2230       *magick_restrict q;
   2231 
   2232     register ssize_t
   2233       x;
   2234 
   2235     if (status == MagickFalse)
   2236       continue;
   2237     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   2238     if (q == (Quantum *) NULL)
   2239       {
   2240         status=MagickFalse;
   2241         continue;
   2242       }
   2243     for (x=0; x < (ssize_t) image->columns; x++)
   2244     {
   2245       register ssize_t
   2246         j;
   2247 
   2248       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
   2249       {
   2250         PixelChannel channel = GetPixelChannelChannel(image,j);
   2251         PixelTrait traits = GetPixelChannelTraits(image,channel);
   2252         if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
   2253           continue;
   2254         q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
   2255           ScaleQuantumToMap(q[j])+j]);
   2256       }
   2257       q+=GetPixelChannels(image);
   2258     }
   2259     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   2260       status=MagickFalse;
   2261     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2262       {
   2263         MagickBooleanType
   2264           proceed;
   2265 
   2266 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2267         #pragma omp atomic
   2268 #endif
   2269         progress++;
   2270         proceed=SetImageProgress(image,EqualizeImageTag,progress,image->rows);
   2271         if (proceed == MagickFalse)
   2272           status=MagickFalse;
   2273       }
   2274   }
   2275   image_view=DestroyCacheView(image_view);
   2276   equalize_map=(double *) RelinquishMagickMemory(equalize_map);
   2277   return(status);
   2278 }
   2279 
   2280 /*
   2282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2283 %                                                                             %
   2284 %                                                                             %
   2285 %                                                                             %
   2286 %     G a m m a I m a g e                                                     %
   2287 %                                                                             %
   2288 %                                                                             %
   2289 %                                                                             %
   2290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2291 %
   2292 %  GammaImage() gamma-corrects a particular image channel.  The same
   2293 %  image viewed on different devices will have perceptual differences in the
   2294 %  way the image's intensities are represented on the screen.  Specify
   2295 %  individual gamma levels for the red, green, and blue channels, or adjust
   2296 %  all three with the gamma parameter.  Values typically range from 0.8 to 2.3.
   2297 %
   2298 %  You can also reduce the influence of a particular channel with a gamma
   2299 %  value of 0.
   2300 %
   2301 %  The format of the GammaImage method is:
   2302 %
   2303 %      MagickBooleanType GammaImage(Image *image,const double gamma,
   2304 %        ExceptionInfo *exception)
   2305 %
   2306 %  A description of each parameter follows:
   2307 %
   2308 %    o image: the image.
   2309 %
   2310 %    o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
   2311 %
   2312 %    o gamma: the image gamma.
   2313 %
   2314 */
   2315 
   2316 static inline double gamma_pow(const double value,const double gamma)
   2317 {
   2318   return(value < 0.0 ? value : pow(value,gamma));
   2319 }
   2320 
   2321 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
   2322   ExceptionInfo *exception)
   2323 {
   2324 #define GammaCorrectImageTag  "GammaCorrect/Image"
   2325 
   2326   CacheView
   2327     *image_view;
   2328 
   2329   MagickBooleanType
   2330     status;
   2331 
   2332   MagickOffsetType
   2333     progress;
   2334 
   2335   Quantum
   2336     *gamma_map;
   2337 
   2338   register ssize_t
   2339     i;
   2340 
   2341   ssize_t
   2342     y;
   2343 
   2344   /*
   2345     Allocate and initialize gamma maps.
   2346   */
   2347   assert(image != (Image *) NULL);
   2348   assert(image->signature == MagickCoreSignature);
   2349   if (image->debug != MagickFalse)
   2350     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2351   if (gamma == 1.0)
   2352     return(MagickTrue);
   2353   gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
   2354   if (gamma_map == (Quantum *) NULL)
   2355     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
   2356       image->filename);
   2357   (void) memset(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
   2358   if (gamma != 0.0)
   2359     for (i=0; i <= (ssize_t) MaxMap; i++)
   2360       gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
   2361         MaxMap,1.0/gamma)));
   2362   if (image->storage_class == PseudoClass)
   2363     for (i=0; i < (ssize_t) image->colors; i++)
   2364     {
   2365       /*
   2366         Gamma-correct colormap.
   2367       */
   2368       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   2369         image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
   2370           ClampToQuantum(image->colormap[i].red))];
   2371       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   2372         image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
   2373           ClampToQuantum(image->colormap[i].green))];
   2374       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   2375         image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
   2376           ClampToQuantum(image->colormap[i].blue))];
   2377       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
   2378         image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
   2379           ClampToQuantum(image->colormap[i].alpha))];
   2380     }
   2381   /*
   2382     Gamma-correct image.
   2383   */
   2384   status=MagickTrue;
   2385   progress=0;
   2386   image_view=AcquireAuthenticCacheView(image,exception);
   2387 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2388   #pragma omp parallel for schedule(static) shared(progress,status) \
   2389     magick_number_threads(image,image,image->rows,1)
   2390 #endif
   2391   for (y=0; y < (ssize_t) image->rows; y++)
   2392   {
   2393     register Quantum
   2394       *magick_restrict q;
   2395 
   2396     register ssize_t
   2397       x;
   2398 
   2399     if (status == MagickFalse)
   2400       continue;
   2401     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   2402     if (q == (Quantum *) NULL)
   2403       {
   2404         status=MagickFalse;
   2405         continue;
   2406       }
   2407     for (x=0; x < (ssize_t) image->columns; x++)
   2408     {
   2409       register ssize_t
   2410         j;
   2411 
   2412       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
   2413       {
   2414         PixelChannel channel = GetPixelChannelChannel(image,j);
   2415         PixelTrait traits = GetPixelChannelTraits(image,channel);
   2416         if ((traits & UpdatePixelTrait) == 0)
   2417           continue;
   2418         q[j]=gamma_map[ScaleQuantumToMap(ClampToQuantum((MagickRealType)
   2419           q[j]))];
   2420       }
   2421       q+=GetPixelChannels(image);
   2422     }
   2423     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   2424       status=MagickFalse;
   2425     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2426       {
   2427         MagickBooleanType
   2428           proceed;
   2429 
   2430 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2431         #pragma omp atomic
   2432 #endif
   2433         progress++;
   2434         proceed=SetImageProgress(image,GammaCorrectImageTag,progress, image->rows);
   2435         if (proceed == MagickFalse)
   2436           status=MagickFalse;
   2437       }
   2438   }
   2439   image_view=DestroyCacheView(image_view);
   2440   gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
   2441   if (image->gamma != 0.0)
   2442     image->gamma*=gamma;
   2443   return(status);
   2444 }
   2445 
   2446 /*
   2448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2449 %                                                                             %
   2450 %                                                                             %
   2451 %                                                                             %
   2452 %     G r a y s c a l e I m a g e                                             %
   2453 %                                                                             %
   2454 %                                                                             %
   2455 %                                                                             %
   2456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2457 %
   2458 %  GrayscaleImage() converts the image to grayscale.
   2459 %
   2460 %  The format of the GrayscaleImage method is:
   2461 %
   2462 %      MagickBooleanType GrayscaleImage(Image *image,
   2463 %        const PixelIntensityMethod method ,ExceptionInfo *exception)
   2464 %
   2465 %  A description of each parameter follows:
   2466 %
   2467 %    o image: the image.
   2468 %
   2469 %    o method: the pixel intensity method.
   2470 %
   2471 %    o exception: return any errors or warnings in this structure.
   2472 %
   2473 */
   2474 MagickExport MagickBooleanType GrayscaleImage(Image *image,
   2475   const PixelIntensityMethod method,ExceptionInfo *exception)
   2476 {
   2477 #define GrayscaleImageTag  "Grayscale/Image"
   2478 
   2479   CacheView
   2480     *image_view;
   2481 
   2482   MagickBooleanType
   2483     status;
   2484 
   2485   MagickOffsetType
   2486     progress;
   2487 
   2488   ssize_t
   2489     y;
   2490 
   2491   assert(image != (Image *) NULL);
   2492   assert(image->signature == MagickCoreSignature);
   2493   if (image->debug != MagickFalse)
   2494     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2495   if (image->storage_class == PseudoClass)
   2496     {
   2497       if (SyncImage(image,exception) == MagickFalse)
   2498         return(MagickFalse);
   2499       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
   2500         return(MagickFalse);
   2501     }
   2502 #if defined(MAGICKCORE_OPENCL_SUPPORT)
   2503   if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
   2504     {
   2505       image->intensity=method;
   2506       image->type=GrayscaleType;
   2507       if ((method == Rec601LuminancePixelIntensityMethod) ||
   2508           (method == Rec709LuminancePixelIntensityMethod))
   2509         return(SetImageColorspace(image,LinearGRAYColorspace,exception));
   2510       return(SetImageColorspace(image,GRAYColorspace,exception));
   2511     }
   2512 #endif
   2513   /*
   2514     Grayscale image.
   2515   */
   2516   status=MagickTrue;
   2517   progress=0;
   2518   image_view=AcquireAuthenticCacheView(image,exception);
   2519 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2520   #pragma omp parallel for schedule(static) shared(progress,status) \
   2521     magick_number_threads(image,image,image->rows,1)
   2522 #endif
   2523   for (y=0; y < (ssize_t) image->rows; y++)
   2524   {
   2525     register Quantum
   2526       *magick_restrict q;
   2527 
   2528     register ssize_t
   2529       x;
   2530 
   2531     if (status == MagickFalse)
   2532       continue;
   2533     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   2534     if (q == (Quantum *) NULL)
   2535       {
   2536         status=MagickFalse;
   2537         continue;
   2538       }
   2539     for (x=0; x < (ssize_t) image->columns; x++)
   2540     {
   2541       MagickRealType
   2542         blue,
   2543         green,
   2544         red,
   2545         intensity;
   2546 
   2547       red=(MagickRealType) GetPixelRed(image,q);
   2548       green=(MagickRealType) GetPixelGreen(image,q);
   2549       blue=(MagickRealType) GetPixelBlue(image,q);
   2550       intensity=0.0;
   2551       switch (method)
   2552       {
   2553         case AveragePixelIntensityMethod:
   2554         {
   2555           intensity=(red+green+blue)/3.0;
   2556           break;
   2557         }
   2558         case BrightnessPixelIntensityMethod:
   2559         {
   2560           intensity=MagickMax(MagickMax(red,green),blue);
   2561           break;
   2562         }
   2563         case LightnessPixelIntensityMethod:
   2564         {
   2565           intensity=(MagickMin(MagickMin(red,green),blue)+
   2566             MagickMax(MagickMax(red,green),blue))/2.0;
   2567           break;
   2568         }
   2569         case MSPixelIntensityMethod:
   2570         {
   2571           intensity=(MagickRealType) (((double) red*red+green*green+
   2572             blue*blue)/3.0);
   2573           break;
   2574         }
   2575         case Rec601LumaPixelIntensityMethod:
   2576         {
   2577           if (image->colorspace == RGBColorspace)
   2578             {
   2579               red=EncodePixelGamma(red);
   2580               green=EncodePixelGamma(green);
   2581               blue=EncodePixelGamma(blue);
   2582             }
   2583           intensity=0.298839*red+0.586811*green+0.114350*blue;
   2584           break;
   2585         }
   2586         case Rec601LuminancePixelIntensityMethod:
   2587         {
   2588           if (image->colorspace == sRGBColorspace)
   2589             {
   2590               red=DecodePixelGamma(red);
   2591               green=DecodePixelGamma(green);
   2592               blue=DecodePixelGamma(blue);
   2593             }
   2594           intensity=0.298839*red+0.586811*green+0.114350*blue;
   2595           break;
   2596         }
   2597         case Rec709LumaPixelIntensityMethod:
   2598         default:
   2599         {
   2600           if (image->colorspace == RGBColorspace)
   2601             {
   2602               red=EncodePixelGamma(red);
   2603               green=EncodePixelGamma(green);
   2604               blue=EncodePixelGamma(blue);
   2605             }
   2606           intensity=0.212656*red+0.715158*green+0.072186*blue;
   2607           break;
   2608         }
   2609         case Rec709LuminancePixelIntensityMethod:
   2610         {
   2611           if (image->colorspace == sRGBColorspace)
   2612             {
   2613               red=DecodePixelGamma(red);
   2614               green=DecodePixelGamma(green);
   2615               blue=DecodePixelGamma(blue);
   2616             }
   2617           intensity=0.212656*red+0.715158*green+0.072186*blue;
   2618           break;
   2619         }
   2620         case RMSPixelIntensityMethod:
   2621         {
   2622           intensity=(MagickRealType) (sqrt((double) red*red+green*green+
   2623             blue*blue)/sqrt(3.0));
   2624           break;
   2625         }
   2626       }
   2627       SetPixelGray(image,ClampToQuantum(intensity),q);
   2628       q+=GetPixelChannels(image);
   2629     }
   2630     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   2631       status=MagickFalse;
   2632     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2633       {
   2634         MagickBooleanType
   2635           proceed;
   2636 
   2637 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2638         #pragma omp atomic
   2639 #endif
   2640         progress++;
   2641         proceed=SetImageProgress(image,GrayscaleImageTag,progress,image->rows);
   2642         if (proceed == MagickFalse)
   2643           status=MagickFalse;
   2644       }
   2645   }
   2646   image_view=DestroyCacheView(image_view);
   2647   image->intensity=method;
   2648   image->type=GrayscaleType;
   2649   if ((method == Rec601LuminancePixelIntensityMethod) ||
   2650       (method == Rec709LuminancePixelIntensityMethod))
   2651     return(SetImageColorspace(image,LinearGRAYColorspace,exception));
   2652   return(SetImageColorspace(image,GRAYColorspace,exception));
   2653 }
   2654 
   2655 /*
   2657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2658 %                                                                             %
   2659 %                                                                             %
   2660 %                                                                             %
   2661 %     H a l d C l u t I m a g e                                               %
   2662 %                                                                             %
   2663 %                                                                             %
   2664 %                                                                             %
   2665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2666 %
   2667 %  HaldClutImage() applies a Hald color lookup table to the image.  A Hald
   2668 %  color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
   2669 %  Create it with the HALD coder.  You can apply any color transformation to
   2670 %  the Hald image and then use this method to apply the transform to the
   2671 %  image.
   2672 %
   2673 %  The format of the HaldClutImage method is:
   2674 %
   2675 %      MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
   2676 %        ExceptionInfo *exception)
   2677 %
   2678 %  A description of each parameter follows:
   2679 %
   2680 %    o image: the image, which is replaced by indexed CLUT values
   2681 %
   2682 %    o hald_image: the color lookup table image for replacement color values.
   2683 %
   2684 %    o exception: return any errors or warnings in this structure.
   2685 %
   2686 */
   2687 MagickExport MagickBooleanType HaldClutImage(Image *image,
   2688   const Image *hald_image,ExceptionInfo *exception)
   2689 {
   2690 #define HaldClutImageTag  "Clut/Image"
   2691 
   2692   typedef struct _HaldInfo
   2693   {
   2694     double
   2695       x,
   2696       y,
   2697       z;
   2698   } HaldInfo;
   2699 
   2700   CacheView
   2701     *hald_view,
   2702     *image_view;
   2703 
   2704   double
   2705     width;
   2706 
   2707   MagickBooleanType
   2708     status;
   2709 
   2710   MagickOffsetType
   2711     progress;
   2712 
   2713   PixelInfo
   2714     zero;
   2715 
   2716   size_t
   2717     cube_size,
   2718     length,
   2719     level;
   2720 
   2721   ssize_t
   2722     y;
   2723 
   2724   assert(image != (Image *) NULL);
   2725   assert(image->signature == MagickCoreSignature);
   2726   if (image->debug != MagickFalse)
   2727     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2728   assert(hald_image != (Image *) NULL);
   2729   assert(hald_image->signature == MagickCoreSignature);
   2730   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
   2731     return(MagickFalse);
   2732   if (image->alpha_trait == UndefinedPixelTrait)
   2733     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
   2734   /*
   2735     Hald clut image.
   2736   */
   2737   status=MagickTrue;
   2738   progress=0;
   2739   length=(size_t) MagickMin((MagickRealType) hald_image->columns,
   2740     (MagickRealType) hald_image->rows);
   2741   for (level=2; (level*level*level) < length; level++) ;
   2742   level*=level;
   2743   cube_size=level*level;
   2744   width=(double) hald_image->columns;
   2745   GetPixelInfo(hald_image,&zero);
   2746   hald_view=AcquireVirtualCacheView(hald_image,exception);
   2747   image_view=AcquireAuthenticCacheView(image,exception);
   2748 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2749   #pragma omp parallel for schedule(static) shared(progress,status) \
   2750     magick_number_threads(image,image,image->rows,1)
   2751 #endif
   2752   for (y=0; y < (ssize_t) image->rows; y++)
   2753   {
   2754     register Quantum
   2755       *magick_restrict q;
   2756 
   2757     register ssize_t
   2758       x;
   2759 
   2760     if (status == MagickFalse)
   2761       continue;
   2762     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   2763     if (q == (Quantum *) NULL)
   2764       {
   2765         status=MagickFalse;
   2766         continue;
   2767       }
   2768     for (x=0; x < (ssize_t) image->columns; x++)
   2769     {
   2770       double
   2771         offset;
   2772 
   2773       HaldInfo
   2774         point;
   2775 
   2776       PixelInfo
   2777         pixel,
   2778         pixel1,
   2779         pixel2,
   2780         pixel3,
   2781         pixel4;
   2782 
   2783       point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
   2784       point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
   2785       point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
   2786       offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
   2787       point.x-=floor(point.x);
   2788       point.y-=floor(point.y);
   2789       point.z-=floor(point.z);
   2790       pixel1=zero;
   2791       status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
   2792         fmod(offset,width),floor(offset/width),&pixel1,exception);
   2793       if (status == MagickFalse)
   2794         break;
   2795       pixel2=zero;
   2796       status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
   2797         fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
   2798       if (status == MagickFalse)
   2799         break;
   2800       pixel3=zero;
   2801       CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
   2802         point.y,&pixel3);
   2803       offset+=cube_size;
   2804       status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
   2805         fmod(offset,width),floor(offset/width),&pixel1,exception);
   2806       if (status == MagickFalse)
   2807         break;
   2808       status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
   2809         fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
   2810       if (status == MagickFalse)
   2811         break;
   2812       pixel4=zero;
   2813       CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
   2814         point.y,&pixel4);
   2815       pixel=zero;
   2816       CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
   2817         point.z,&pixel);
   2818       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   2819         SetPixelRed(image,ClampToQuantum(pixel.red),q);
   2820       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   2821         SetPixelGreen(image,ClampToQuantum(pixel.green),q);
   2822       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   2823         SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
   2824       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
   2825           (image->colorspace == CMYKColorspace))
   2826         SetPixelBlack(image,ClampToQuantum(pixel.black),q);
   2827       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
   2828           (image->alpha_trait != UndefinedPixelTrait))
   2829         SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
   2830       q+=GetPixelChannels(image);
   2831     }
   2832     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   2833       status=MagickFalse;
   2834     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2835       {
   2836         MagickBooleanType
   2837           proceed;
   2838 
   2839 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2840         #pragma omp atomic
   2841 #endif
   2842         progress++;
   2843         proceed=SetImageProgress(image,HaldClutImageTag,progress,image->rows);
   2844         if (proceed == MagickFalse)
   2845           status=MagickFalse;
   2846       }
   2847   }
   2848   hald_view=DestroyCacheView(hald_view);
   2849   image_view=DestroyCacheView(image_view);
   2850   return(status);
   2851 }
   2852 
   2853 /*
   2855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2856 %                                                                             %
   2857 %                                                                             %
   2858 %                                                                             %
   2859 %     L e v e l I m a g e                                                     %
   2860 %                                                                             %
   2861 %                                                                             %
   2862 %                                                                             %
   2863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2864 %
   2865 %  LevelImage() adjusts the levels of a particular image channel by
   2866 %  scaling the colors falling between specified white and black points to
   2867 %  the full available quantum range.
   2868 %
   2869 %  The parameters provided represent the black, and white points.  The black
   2870 %  point specifies the darkest color in the image. Colors darker than the
   2871 %  black point are set to zero.  White point specifies the lightest color in
   2872 %  the image.  Colors brighter than the white point are set to the maximum
   2873 %  quantum value.
   2874 %
   2875 %  If a '!' flag is given, map black and white colors to the given levels
   2876 %  rather than mapping those levels to black and white.  See
   2877 %  LevelizeImage() below.
   2878 %
   2879 %  Gamma specifies a gamma correction to apply to the image.
   2880 %
   2881 %  The format of the LevelImage method is:
   2882 %
   2883 %      MagickBooleanType LevelImage(Image *image,const double black_point,
   2884 %        const double white_point,const double gamma,ExceptionInfo *exception)
   2885 %
   2886 %  A description of each parameter follows:
   2887 %
   2888 %    o image: the image.
   2889 %
   2890 %    o black_point: The level to map zero (black) to.
   2891 %
   2892 %    o white_point: The level to map QuantumRange (white) to.
   2893 %
   2894 %    o exception: return any errors or warnings in this structure.
   2895 %
   2896 */
   2897 
   2898 static inline double LevelPixel(const double black_point,
   2899   const double white_point,const double gamma,const double pixel)
   2900 {
   2901   double
   2902     level_pixel,
   2903     scale;
   2904 
   2905   scale=PerceptibleReciprocal(white_point-black_point);
   2906   level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
   2907     1.0/gamma);
   2908   return(level_pixel);
   2909 }
   2910 
   2911 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
   2912   const double white_point,const double gamma,ExceptionInfo *exception)
   2913 {
   2914 #define LevelImageTag  "Level/Image"
   2915 
   2916   CacheView
   2917     *image_view;
   2918 
   2919   MagickBooleanType
   2920     status;
   2921 
   2922   MagickOffsetType
   2923     progress;
   2924 
   2925   register ssize_t
   2926     i;
   2927 
   2928   ssize_t
   2929     y;
   2930 
   2931   /*
   2932     Allocate and initialize levels map.
   2933   */
   2934   assert(image != (Image *) NULL);
   2935   assert(image->signature == MagickCoreSignature);
   2936   if (image->debug != MagickFalse)
   2937     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2938   if (image->storage_class == PseudoClass)
   2939     for (i=0; i < (ssize_t) image->colors; i++)
   2940     {
   2941       /*
   2942         Level colormap.
   2943       */
   2944       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   2945         image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
   2946           white_point,gamma,image->colormap[i].red));
   2947       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   2948         image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
   2949           white_point,gamma,image->colormap[i].green));
   2950       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   2951         image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
   2952           white_point,gamma,image->colormap[i].blue));
   2953       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
   2954         image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
   2955           white_point,gamma,image->colormap[i].alpha));
   2956     }
   2957   /*
   2958     Level image.
   2959   */
   2960   status=MagickTrue;
   2961   progress=0;
   2962   image_view=AcquireAuthenticCacheView(image,exception);
   2963 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2964   #pragma omp parallel for schedule(static) shared(progress,status) \
   2965     magick_number_threads(image,image,image->rows,1)
   2966 #endif
   2967   for (y=0; y < (ssize_t) image->rows; y++)
   2968   {
   2969     register Quantum
   2970       *magick_restrict q;
   2971 
   2972     register ssize_t
   2973       x;
   2974 
   2975     if (status == MagickFalse)
   2976       continue;
   2977     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   2978     if (q == (Quantum *) NULL)
   2979       {
   2980         status=MagickFalse;
   2981         continue;
   2982       }
   2983     for (x=0; x < (ssize_t) image->columns; x++)
   2984     {
   2985       register ssize_t
   2986         j;
   2987 
   2988       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
   2989       {
   2990         PixelChannel channel = GetPixelChannelChannel(image,j);
   2991         PixelTrait traits = GetPixelChannelTraits(image,channel);
   2992         if ((traits & UpdatePixelTrait) == 0)
   2993           continue;
   2994         q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
   2995           (double) q[j]));
   2996       }
   2997       q+=GetPixelChannels(image);
   2998     }
   2999     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   3000       status=MagickFalse;
   3001     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3002       {
   3003         MagickBooleanType
   3004           proceed;
   3005 
   3006 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3007         #pragma omp atomic
   3008 #endif
   3009         progress++;
   3010         proceed=SetImageProgress(image,LevelImageTag,progress,image->rows);
   3011         if (proceed == MagickFalse)
   3012           status=MagickFalse;
   3013       }
   3014   }
   3015   image_view=DestroyCacheView(image_view);
   3016   (void) ClampImage(image,exception);
   3017   return(status);
   3018 }
   3019 
   3020 /*
   3022 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3023 %                                                                             %
   3024 %                                                                             %
   3025 %                                                                             %
   3026 %     L e v e l i z e I m a g e                                               %
   3027 %                                                                             %
   3028 %                                                                             %
   3029 %                                                                             %
   3030 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3031 %
   3032 %  LevelizeImage() applies the reversed LevelImage() operation to just
   3033 %  the specific channels specified.  It compresses the full range of color
   3034 %  values, so that they lie between the given black and white points. Gamma is
   3035 %  applied before the values are mapped.
   3036 %
   3037 %  LevelizeImage() can be called with by using a +level command line
   3038 %  API option, or using a '!' on a -level or LevelImage() geometry string.
   3039 %
   3040 %  It can be used to de-contrast a greyscale image to the exact levels
   3041 %  specified.  Or by using specific levels for each channel of an image you
   3042 %  can convert a gray-scale image to any linear color gradient, according to
   3043 %  those levels.
   3044 %
   3045 %  The format of the LevelizeImage method is:
   3046 %
   3047 %      MagickBooleanType LevelizeImage(Image *image,const double black_point,
   3048 %        const double white_point,const double gamma,ExceptionInfo *exception)
   3049 %
   3050 %  A description of each parameter follows:
   3051 %
   3052 %    o image: the image.
   3053 %
   3054 %    o black_point: The level to map zero (black) to.
   3055 %
   3056 %    o white_point: The level to map QuantumRange (white) to.
   3057 %
   3058 %    o gamma: adjust gamma by this factor before mapping values.
   3059 %
   3060 %    o exception: return any errors or warnings in this structure.
   3061 %
   3062 */
   3063 MagickExport MagickBooleanType LevelizeImage(Image *image,
   3064   const double black_point,const double white_point,const double gamma,
   3065   ExceptionInfo *exception)
   3066 {
   3067 #define LevelizeImageTag  "Levelize/Image"
   3068 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
   3069   (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
   3070 
   3071   CacheView
   3072     *image_view;
   3073 
   3074   MagickBooleanType
   3075     status;
   3076 
   3077   MagickOffsetType
   3078     progress;
   3079 
   3080   register ssize_t
   3081     i;
   3082 
   3083   ssize_t
   3084     y;
   3085 
   3086   /*
   3087     Allocate and initialize levels map.
   3088   */
   3089   assert(image != (Image *) NULL);
   3090   assert(image->signature == MagickCoreSignature);
   3091   if (image->debug != MagickFalse)
   3092     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3093   if (image->storage_class == PseudoClass)
   3094     for (i=0; i < (ssize_t) image->colors; i++)
   3095     {
   3096       /*
   3097         Level colormap.
   3098       */
   3099       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   3100         image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
   3101       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   3102         image->colormap[i].green=(double) LevelizeValue(
   3103           image->colormap[i].green);
   3104       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   3105         image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
   3106       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
   3107         image->colormap[i].alpha=(double) LevelizeValue(
   3108           image->colormap[i].alpha);
   3109     }
   3110   /*
   3111     Level image.
   3112   */
   3113   status=MagickTrue;
   3114   progress=0;
   3115   image_view=AcquireAuthenticCacheView(image,exception);
   3116 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3117   #pragma omp parallel for schedule(static) shared(progress,status) \
   3118     magick_number_threads(image,image,image->rows,1)
   3119 #endif
   3120   for (y=0; y < (ssize_t) image->rows; y++)
   3121   {
   3122     register Quantum
   3123       *magick_restrict q;
   3124 
   3125     register ssize_t
   3126       x;
   3127 
   3128     if (status == MagickFalse)
   3129       continue;
   3130     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   3131     if (q == (Quantum *) NULL)
   3132       {
   3133         status=MagickFalse;
   3134         continue;
   3135       }
   3136     for (x=0; x < (ssize_t) image->columns; x++)
   3137     {
   3138       register ssize_t
   3139         j;
   3140 
   3141       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
   3142       {
   3143         PixelChannel channel = GetPixelChannelChannel(image,j);
   3144         PixelTrait traits = GetPixelChannelTraits(image,channel);
   3145         if ((traits & UpdatePixelTrait) == 0)
   3146           continue;
   3147         q[j]=LevelizeValue(q[j]);
   3148       }
   3149       q+=GetPixelChannels(image);
   3150     }
   3151     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   3152       status=MagickFalse;
   3153     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3154       {
   3155         MagickBooleanType
   3156           proceed;
   3157 
   3158 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3159         #pragma omp atomic
   3160 #endif
   3161         progress++;
   3162         proceed=SetImageProgress(image,LevelizeImageTag,progress,image->rows);
   3163         if (proceed == MagickFalse)
   3164           status=MagickFalse;
   3165       }
   3166   }
   3167   image_view=DestroyCacheView(image_view);
   3168   return(status);
   3169 }
   3170 
   3171 /*
   3173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3174 %                                                                             %
   3175 %                                                                             %
   3176 %                                                                             %
   3177 %     L e v e l I m a g e C o l o r s                                         %
   3178 %                                                                             %
   3179 %                                                                             %
   3180 %                                                                             %
   3181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3182 %
   3183 %  LevelImageColors() maps the given color to "black" and "white" values,
   3184 %  linearly spreading out the colors, and level values on a channel by channel
   3185 %  bases, as per LevelImage().  The given colors allows you to specify
   3186 %  different level ranges for each of the color channels separately.
   3187 %
   3188 %  If the boolean 'invert' is set true the image values will modifyed in the
   3189 %  reverse direction. That is any existing "black" and "white" colors in the
   3190 %  image will become the color values given, with all other values compressed
   3191 %  appropriatally.  This effectivally maps a greyscale gradient into the given
   3192 %  color gradient.
   3193 %
   3194 %  The format of the LevelImageColors method is:
   3195 %
   3196 %    MagickBooleanType LevelImageColors(Image *image,
   3197 %      const PixelInfo *black_color,const PixelInfo *white_color,
   3198 %      const MagickBooleanType invert,ExceptionInfo *exception)
   3199 %
   3200 %  A description of each parameter follows:
   3201 %
   3202 %    o image: the image.
   3203 %
   3204 %    o black_color: The color to map black to/from
   3205 %
   3206 %    o white_point: The color to map white to/from
   3207 %
   3208 %    o invert: if true map the colors (levelize), rather than from (level)
   3209 %
   3210 %    o exception: return any errors or warnings in this structure.
   3211 %
   3212 */
   3213 MagickExport MagickBooleanType LevelImageColors(Image *image,
   3214   const PixelInfo *black_color,const PixelInfo *white_color,
   3215   const MagickBooleanType invert,ExceptionInfo *exception)
   3216 {
   3217   ChannelType
   3218     channel_mask;
   3219 
   3220   MagickStatusType
   3221     status;
   3222 
   3223   /*
   3224     Allocate and initialize levels map.
   3225   */
   3226   assert(image != (Image *) NULL);
   3227   assert(image->signature == MagickCoreSignature);
   3228   if (image->debug != MagickFalse)
   3229     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3230   if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
   3231       ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
   3232        (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
   3233     (void) SetImageColorspace(image,sRGBColorspace,exception);
   3234   status=MagickTrue;
   3235   if (invert == MagickFalse)
   3236     {
   3237       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   3238         {
   3239           channel_mask=SetImageChannelMask(image,RedChannel);
   3240           status&=LevelImage(image,black_color->red,white_color->red,1.0,
   3241             exception);
   3242           (void) SetImageChannelMask(image,channel_mask);
   3243         }
   3244       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   3245         {
   3246           channel_mask=SetImageChannelMask(image,GreenChannel);
   3247           status&=LevelImage(image,black_color->green,white_color->green,1.0,
   3248             exception);
   3249           (void) SetImageChannelMask(image,channel_mask);
   3250         }
   3251       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   3252         {
   3253           channel_mask=SetImageChannelMask(image,BlueChannel);
   3254           status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
   3255             exception);
   3256           (void) SetImageChannelMask(image,channel_mask);
   3257         }
   3258       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
   3259           (image->colorspace == CMYKColorspace))
   3260         {
   3261           channel_mask=SetImageChannelMask(image,BlackChannel);
   3262           status&=LevelImage(image,black_color->black,white_color->black,1.0,
   3263             exception);
   3264           (void) SetImageChannelMask(image,channel_mask);
   3265         }
   3266       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
   3267           (image->alpha_trait != UndefinedPixelTrait))
   3268         {
   3269           channel_mask=SetImageChannelMask(image,AlphaChannel);
   3270           status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
   3271             exception);
   3272           (void) SetImageChannelMask(image,channel_mask);
   3273         }
   3274     }
   3275   else
   3276     {
   3277       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   3278         {
   3279           channel_mask=SetImageChannelMask(image,RedChannel);
   3280           status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
   3281             exception);
   3282           (void) SetImageChannelMask(image,channel_mask);
   3283         }
   3284       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   3285         {
   3286           channel_mask=SetImageChannelMask(image,GreenChannel);
   3287           status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
   3288             exception);
   3289           (void) SetImageChannelMask(image,channel_mask);
   3290         }
   3291       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   3292         {
   3293           channel_mask=SetImageChannelMask(image,BlueChannel);
   3294           status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
   3295             exception);
   3296           (void) SetImageChannelMask(image,channel_mask);
   3297         }
   3298       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
   3299           (image->colorspace == CMYKColorspace))
   3300         {
   3301           channel_mask=SetImageChannelMask(image,BlackChannel);
   3302           status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
   3303             exception);
   3304           (void) SetImageChannelMask(image,channel_mask);
   3305         }
   3306       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
   3307           (image->alpha_trait != UndefinedPixelTrait))
   3308         {
   3309           channel_mask=SetImageChannelMask(image,AlphaChannel);
   3310           status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
   3311             exception);
   3312           (void) SetImageChannelMask(image,channel_mask);
   3313         }
   3314     }
   3315   return(status != 0 ? MagickTrue : MagickFalse);
   3316 }
   3317 
   3318 /*
   3320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3321 %                                                                             %
   3322 %                                                                             %
   3323 %                                                                             %
   3324 %     L i n e a r S t r e t c h I m a g e                                     %
   3325 %                                                                             %
   3326 %                                                                             %
   3327 %                                                                             %
   3328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3329 %
   3330 %  LinearStretchImage() discards any pixels below the black point and above
   3331 %  the white point and levels the remaining pixels.
   3332 %
   3333 %  The format of the LinearStretchImage method is:
   3334 %
   3335 %      MagickBooleanType LinearStretchImage(Image *image,
   3336 %        const double black_point,const double white_point,
   3337 %        ExceptionInfo *exception)
   3338 %
   3339 %  A description of each parameter follows:
   3340 %
   3341 %    o image: the image.
   3342 %
   3343 %    o black_point: the black point.
   3344 %
   3345 %    o white_point: the white point.
   3346 %
   3347 %    o exception: return any errors or warnings in this structure.
   3348 %
   3349 */
   3350 MagickExport MagickBooleanType LinearStretchImage(Image *image,
   3351   const double black_point,const double white_point,ExceptionInfo *exception)
   3352 {
   3353 #define LinearStretchImageTag  "LinearStretch/Image"
   3354 
   3355   CacheView
   3356     *image_view;
   3357 
   3358   double
   3359     *histogram,
   3360     intensity;
   3361 
   3362   MagickBooleanType
   3363     status;
   3364 
   3365   ssize_t
   3366     black,
   3367     white,
   3368     y;
   3369 
   3370   /*
   3371     Allocate histogram and linear map.
   3372   */
   3373   assert(image != (Image *) NULL);
   3374   assert(image->signature == MagickCoreSignature);
   3375   histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
   3376   if (histogram == (double *) NULL)
   3377     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
   3378       image->filename);
   3379   /*
   3380     Form histogram.
   3381   */
   3382   (void) memset(histogram,0,(MaxMap+1)*sizeof(*histogram));
   3383   image_view=AcquireVirtualCacheView(image,exception);
   3384   for (y=0; y < (ssize_t) image->rows; y++)
   3385   {
   3386     register const Quantum
   3387       *magick_restrict p;
   3388 
   3389     register ssize_t
   3390       x;
   3391 
   3392     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   3393     if (p == (const Quantum *) NULL)
   3394       break;
   3395     for (x=0; x < (ssize_t) image->columns; x++)
   3396     {
   3397       intensity=GetPixelIntensity(image,p);
   3398       histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
   3399       p+=GetPixelChannels(image);
   3400     }
   3401   }
   3402   image_view=DestroyCacheView(image_view);
   3403   /*
   3404     Find the histogram boundaries by locating the black and white point levels.
   3405   */
   3406   intensity=0.0;
   3407   for (black=0; black < (ssize_t) MaxMap; black++)
   3408   {
   3409     intensity+=histogram[black];
   3410     if (intensity >= black_point)
   3411       break;
   3412   }
   3413   intensity=0.0;
   3414   for (white=(ssize_t) MaxMap; white != 0; white--)
   3415   {
   3416     intensity+=histogram[white];
   3417     if (intensity >= white_point)
   3418       break;
   3419   }
   3420   histogram=(double *) RelinquishMagickMemory(histogram);
   3421   status=LevelImage(image,(double) ScaleMapToQuantum((MagickRealType) black),
   3422     (double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
   3423   return(status);
   3424 }
   3425 
   3426 
   3427 /*
   3428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3429 %                                                                             %
   3430 %                                                                             %
   3431 %                                                                             %
   3432 %     M o d u l a t e I m a g e                                               %
   3433 %                                                                             %
   3434 %                                                                             %
   3435 %                                                                             %
   3436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3437 %
   3438 %  ModulateImage() lets you control the brightness, saturation, and hue
   3439 %  of an image.  Modulate represents the brightness, saturation, and hue
   3440 %  as one parameter (e.g. 90,150,100).  If the image colorspace is HSL, the
   3441 %  modulation is lightness, saturation, and hue.  For HWB, use blackness,
   3442 %  whiteness, and hue. And for HCL, use chrome, luma, and hue.
   3443 %
   3444 %  The format of the ModulateImage method is:
   3445 %
   3446 %      MagickBooleanType ModulateImage(Image *image,const char *modulate,
   3447 %        ExceptionInfo *exception)
   3448 %
   3449 %  A description of each parameter follows:
   3450 %
   3451 %    o image: the image.
   3452 %
   3453 %    o modulate: Define the percent change in brightness, saturation, and hue.
   3454 %
   3455 %    o exception: return any errors or warnings in this structure.
   3456 %
   3457 */
   3458 
   3459 static inline void ModulateHCL(const double percent_hue,
   3460   const double percent_chroma,const double percent_luma,double *red,
   3461   double *green,double *blue)
   3462 {
   3463   double
   3464     hue,
   3465     luma,
   3466     chroma;
   3467 
   3468   /*
   3469     Increase or decrease color luma, chroma, or hue.
   3470   */
   3471   ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
   3472   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3473   chroma*=0.01*percent_chroma;
   3474   luma*=0.01*percent_luma;
   3475   ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
   3476 }
   3477 
   3478 static inline void ModulateHCLp(const double percent_hue,
   3479   const double percent_chroma,const double percent_luma,double *red,
   3480   double *green,double *blue)
   3481 {
   3482   double
   3483     hue,
   3484     luma,
   3485     chroma;
   3486 
   3487   /*
   3488     Increase or decrease color luma, chroma, or hue.
   3489   */
   3490   ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
   3491   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3492   chroma*=0.01*percent_chroma;
   3493   luma*=0.01*percent_luma;
   3494   ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
   3495 }
   3496 
   3497 static inline void ModulateHSB(const double percent_hue,
   3498   const double percent_saturation,const double percent_brightness,double *red,
   3499   double *green,double *blue)
   3500 {
   3501   double
   3502     brightness,
   3503     hue,
   3504     saturation;
   3505 
   3506   /*
   3507     Increase or decrease color brightness, saturation, or hue.
   3508   */
   3509   ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
   3510   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3511   saturation*=0.01*percent_saturation;
   3512   brightness*=0.01*percent_brightness;
   3513   ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
   3514 }
   3515 
   3516 static inline void ModulateHSI(const double percent_hue,
   3517   const double percent_saturation,const double percent_intensity,double *red,
   3518   double *green,double *blue)
   3519 {
   3520   double
   3521     intensity,
   3522     hue,
   3523     saturation;
   3524 
   3525   /*
   3526     Increase or decrease color intensity, saturation, or hue.
   3527   */
   3528   ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
   3529   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3530   saturation*=0.01*percent_saturation;
   3531   intensity*=0.01*percent_intensity;
   3532   ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
   3533 }
   3534 
   3535 static inline void ModulateHSL(const double percent_hue,
   3536   const double percent_saturation,const double percent_lightness,double *red,
   3537   double *green,double *blue)
   3538 {
   3539   double
   3540     hue,
   3541     lightness,
   3542     saturation;
   3543 
   3544   /*
   3545     Increase or decrease color lightness, saturation, or hue.
   3546   */
   3547   ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
   3548   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3549   saturation*=0.01*percent_saturation;
   3550   lightness*=0.01*percent_lightness;
   3551   ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
   3552 }
   3553 
   3554 static inline void ModulateHSV(const double percent_hue,
   3555   const double percent_saturation,const double percent_value,double *red,
   3556   double *green,double *blue)
   3557 {
   3558   double
   3559     hue,
   3560     saturation,
   3561     value;
   3562 
   3563   /*
   3564     Increase or decrease color value, saturation, or hue.
   3565   */
   3566   ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
   3567   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3568   saturation*=0.01*percent_saturation;
   3569   value*=0.01*percent_value;
   3570   ConvertHSVToRGB(hue,saturation,value,red,green,blue);
   3571 }
   3572 
   3573 static inline void ModulateHWB(const double percent_hue,
   3574   const double percent_whiteness,const double percent_blackness,double *red,
   3575   double *green,double *blue)
   3576 {
   3577   double
   3578     blackness,
   3579     hue,
   3580     whiteness;
   3581 
   3582   /*
   3583     Increase or decrease color blackness, whiteness, or hue.
   3584   */
   3585   ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
   3586   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3587   blackness*=0.01*percent_blackness;
   3588   whiteness*=0.01*percent_whiteness;
   3589   ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
   3590 }
   3591 
   3592 static inline void ModulateLCHab(const double percent_luma,
   3593   const double percent_chroma,const double percent_hue,double *red,
   3594   double *green,double *blue)
   3595 {
   3596   double
   3597     hue,
   3598     luma,
   3599     chroma;
   3600 
   3601   /*
   3602     Increase or decrease color luma, chroma, or hue.
   3603   */
   3604   ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
   3605   luma*=0.01*percent_luma;
   3606   chroma*=0.01*percent_chroma;
   3607   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3608   ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
   3609 }
   3610 
   3611 static inline void ModulateLCHuv(const double percent_luma,
   3612   const double percent_chroma,const double percent_hue,double *red,
   3613   double *green,double *blue)
   3614 {
   3615   double
   3616     hue,
   3617     luma,
   3618     chroma;
   3619 
   3620   /*
   3621     Increase or decrease color luma, chroma, or hue.
   3622   */
   3623   ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
   3624   luma*=0.01*percent_luma;
   3625   chroma*=0.01*percent_chroma;
   3626   hue+=fmod((percent_hue-100.0),200.0)/200.0;
   3627   ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
   3628 }
   3629 
   3630 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
   3631   ExceptionInfo *exception)
   3632 {
   3633 #define ModulateImageTag  "Modulate/Image"
   3634 
   3635   CacheView
   3636     *image_view;
   3637 
   3638   ColorspaceType
   3639     colorspace;
   3640 
   3641   const char
   3642     *artifact;
   3643 
   3644   double
   3645     percent_brightness,
   3646     percent_hue,
   3647     percent_saturation;
   3648 
   3649   GeometryInfo
   3650     geometry_info;
   3651 
   3652   MagickBooleanType
   3653     status;
   3654 
   3655   MagickOffsetType
   3656     progress;
   3657 
   3658   MagickStatusType
   3659     flags;
   3660 
   3661   register ssize_t
   3662     i;
   3663 
   3664   ssize_t
   3665     y;
   3666 
   3667   /*
   3668     Initialize modulate table.
   3669   */
   3670   assert(image != (Image *) NULL);
   3671   assert(image->signature == MagickCoreSignature);
   3672   if (image->debug != MagickFalse)
   3673     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3674   if (modulate == (char *) NULL)
   3675     return(MagickFalse);
   3676   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
   3677     (void) SetImageColorspace(image,sRGBColorspace,exception);
   3678   flags=ParseGeometry(modulate,&geometry_info);
   3679   percent_brightness=geometry_info.rho;
   3680   percent_saturation=geometry_info.sigma;
   3681   if ((flags & SigmaValue) == 0)
   3682     percent_saturation=100.0;
   3683   percent_hue=geometry_info.xi;
   3684   if ((flags & XiValue) == 0)
   3685     percent_hue=100.0;
   3686   colorspace=UndefinedColorspace;
   3687   artifact=GetImageArtifact(image,"modulate:colorspace");
   3688   if (artifact != (const char *) NULL)
   3689     colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
   3690       MagickFalse,artifact);
   3691   if (image->storage_class == PseudoClass)
   3692     for (i=0; i < (ssize_t) image->colors; i++)
   3693     {
   3694       double
   3695         blue,
   3696         green,
   3697         red;
   3698 
   3699       /*
   3700         Modulate image colormap.
   3701       */
   3702       red=(double) image->colormap[i].red;
   3703       green=(double) image->colormap[i].green;
   3704       blue=(double) image->colormap[i].blue;
   3705       switch (colorspace)
   3706       {
   3707         case HCLColorspace:
   3708         {
   3709           ModulateHCL(percent_hue,percent_saturation,percent_brightness,
   3710             &red,&green,&blue);
   3711           break;
   3712         }
   3713         case HCLpColorspace:
   3714         {
   3715           ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
   3716             &red,&green,&blue);
   3717           break;
   3718         }
   3719         case HSBColorspace:
   3720         {
   3721           ModulateHSB(percent_hue,percent_saturation,percent_brightness,
   3722             &red,&green,&blue);
   3723           break;
   3724         }
   3725         case HSIColorspace:
   3726         {
   3727           ModulateHSI(percent_hue,percent_saturation,percent_brightness,
   3728             &red,&green,&blue);
   3729           break;
   3730         }
   3731         case HSLColorspace:
   3732         default:
   3733         {
   3734           ModulateHSL(percent_hue,percent_saturation,percent_brightness,
   3735             &red,&green,&blue);
   3736           break;
   3737         }
   3738         case HSVColorspace:
   3739         {
   3740           ModulateHSV(percent_hue,percent_saturation,percent_brightness,
   3741             &red,&green,&blue);
   3742           break;
   3743         }
   3744         case HWBColorspace:
   3745         {
   3746           ModulateHWB(percent_hue,percent_saturation,percent_brightness,
   3747             &red,&green,&blue);
   3748           break;
   3749         }
   3750         case LCHColorspace:
   3751         case LCHabColorspace:
   3752         {
   3753           ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
   3754             &red,&green,&blue);
   3755           break;
   3756         }
   3757         case LCHuvColorspace:
   3758         {
   3759           ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
   3760             &red,&green,&blue);
   3761           break;
   3762         }
   3763       }
   3764       image->colormap[i].red=red;
   3765       image->colormap[i].green=green;
   3766       image->colormap[i].blue=blue;
   3767     }
   3768   /*
   3769     Modulate image.
   3770   */
   3771 #if defined(MAGICKCORE_OPENCL_SUPPORT)
   3772   if (AccelerateModulateImage(image,percent_brightness,percent_hue,
   3773         percent_saturation,colorspace,exception) != MagickFalse)
   3774     return(MagickTrue);
   3775 #endif
   3776   status=MagickTrue;
   3777   progress=0;
   3778   image_view=AcquireAuthenticCacheView(image,exception);
   3779 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3780   #pragma omp parallel for schedule(static) shared(progress,status) \
   3781     magick_number_threads(image,image,image->rows,1)
   3782 #endif
   3783   for (y=0; y < (ssize_t) image->rows; y++)
   3784   {
   3785     register Quantum
   3786       *magick_restrict q;
   3787 
   3788     register ssize_t
   3789       x;
   3790 
   3791     if (status == MagickFalse)
   3792       continue;
   3793     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   3794     if (q == (Quantum *) NULL)
   3795       {
   3796         status=MagickFalse;
   3797         continue;
   3798       }
   3799     for (x=0; x < (ssize_t) image->columns; x++)
   3800     {
   3801       double
   3802         blue,
   3803         green,
   3804         red;
   3805 
   3806       red=(double) GetPixelRed(image,q);
   3807       green=(double) GetPixelGreen(image,q);
   3808       blue=(double) GetPixelBlue(image,q);
   3809       switch (colorspace)
   3810       {
   3811         case HCLColorspace:
   3812         {
   3813           ModulateHCL(percent_hue,percent_saturation,percent_brightness,
   3814             &red,&green,&blue);
   3815           break;
   3816         }
   3817         case HCLpColorspace:
   3818         {
   3819           ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
   3820             &red,&green,&blue);
   3821           break;
   3822         }
   3823         case HSBColorspace:
   3824         {
   3825           ModulateHSB(percent_hue,percent_saturation,percent_brightness,
   3826             &red,&green,&blue);
   3827           break;
   3828         }
   3829         case HSLColorspace:
   3830         default:
   3831         {
   3832           ModulateHSL(percent_hue,percent_saturation,percent_brightness,
   3833             &red,&green,&blue);
   3834           break;
   3835         }
   3836         case HSVColorspace:
   3837         {
   3838           ModulateHSV(percent_hue,percent_saturation,percent_brightness,
   3839             &red,&green,&blue);
   3840           break;
   3841         }
   3842         case HWBColorspace:
   3843         {
   3844           ModulateHWB(percent_hue,percent_saturation,percent_brightness,
   3845             &red,&green,&blue);
   3846           break;
   3847         }
   3848         case LCHabColorspace:
   3849         {
   3850           ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
   3851             &red,&green,&blue);
   3852           break;
   3853         }
   3854         case LCHColorspace:
   3855         case LCHuvColorspace:
   3856         {
   3857           ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
   3858             &red,&green,&blue);
   3859           break;
   3860         }
   3861       }
   3862       SetPixelRed(image,ClampToQuantum(red),q);
   3863       SetPixelGreen(image,ClampToQuantum(green),q);
   3864       SetPixelBlue(image,ClampToQuantum(blue),q);
   3865       q+=GetPixelChannels(image);
   3866     }
   3867     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   3868       status=MagickFalse;
   3869     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3870       {
   3871         MagickBooleanType
   3872           proceed;
   3873 
   3874 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3875         #pragma omp atomic
   3876 #endif
   3877         progress++;
   3878         proceed=SetImageProgress(image,ModulateImageTag,progress,image->rows);
   3879         if (proceed == MagickFalse)
   3880           status=MagickFalse;
   3881       }
   3882   }
   3883   image_view=DestroyCacheView(image_view);
   3884   return(status);
   3885 }
   3886 
   3887 /*
   3889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3890 %                                                                             %
   3891 %                                                                             %
   3892 %                                                                             %
   3893 %     N e g a t e I m a g e                                                   %
   3894 %                                                                             %
   3895 %                                                                             %
   3896 %                                                                             %
   3897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3898 %
   3899 %  NegateImage() negates the colors in the reference image.  The grayscale
   3900 %  option means that only grayscale values within the image are negated.
   3901 %
   3902 %  The format of the NegateImage method is:
   3903 %
   3904 %      MagickBooleanType NegateImage(Image *image,
   3905 %        const MagickBooleanType grayscale,ExceptionInfo *exception)
   3906 %
   3907 %  A description of each parameter follows:
   3908 %
   3909 %    o image: the image.
   3910 %
   3911 %    o grayscale: If MagickTrue, only negate grayscale pixels within the image.
   3912 %
   3913 %    o exception: return any errors or warnings in this structure.
   3914 %
   3915 */
   3916 MagickExport MagickBooleanType NegateImage(Image *image,
   3917   const MagickBooleanType grayscale,ExceptionInfo *exception)
   3918 {
   3919 #define NegateImageTag  "Negate/Image"
   3920 
   3921   CacheView
   3922     *image_view;
   3923 
   3924   MagickBooleanType
   3925     status;
   3926 
   3927   MagickOffsetType
   3928     progress;
   3929 
   3930   register ssize_t
   3931     i;
   3932 
   3933   ssize_t
   3934     y;
   3935 
   3936   assert(image != (Image *) NULL);
   3937   assert(image->signature == MagickCoreSignature);
   3938   if (image->debug != MagickFalse)
   3939     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3940   if (image->storage_class == PseudoClass)
   3941     for (i=0; i < (ssize_t) image->colors; i++)
   3942     {
   3943       /*
   3944         Negate colormap.
   3945       */
   3946       if( grayscale != MagickFalse )
   3947         if ((image->colormap[i].red != image->colormap[i].green) ||
   3948             (image->colormap[i].green != image->colormap[i].blue))
   3949           continue;
   3950       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   3951         image->colormap[i].red=QuantumRange-image->colormap[i].red;
   3952       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   3953         image->colormap[i].green=QuantumRange-image->colormap[i].green;
   3954       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   3955         image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
   3956     }
   3957   /*
   3958     Negate image.
   3959   */
   3960   status=MagickTrue;
   3961   progress=0;
   3962   image_view=AcquireAuthenticCacheView(image,exception);
   3963   if( grayscale != MagickFalse )
   3964     {
   3965       for (y=0; y < (ssize_t) image->rows; y++)
   3966       {
   3967         MagickBooleanType
   3968           sync;
   3969 
   3970         register Quantum
   3971           *magick_restrict q;
   3972 
   3973         register ssize_t
   3974           x;
   3975 
   3976         if (status == MagickFalse)
   3977           continue;
   3978         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
   3979           exception);
   3980         if (q == (Quantum *) NULL)
   3981           {
   3982             status=MagickFalse;
   3983             continue;
   3984           }
   3985         for (x=0; x < (ssize_t) image->columns; x++)
   3986         {
   3987           register ssize_t
   3988             j;
   3989 
   3990           if (IsPixelGray(image,q) != MagickFalse)
   3991             {
   3992               q+=GetPixelChannels(image);
   3993               continue;
   3994             }
   3995           for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
   3996           {
   3997             PixelChannel channel = GetPixelChannelChannel(image,j);
   3998             PixelTrait traits = GetPixelChannelTraits(image,channel);
   3999             if ((traits & UpdatePixelTrait) == 0)
   4000               continue;
   4001             q[j]=QuantumRange-q[j];
   4002           }
   4003           q+=GetPixelChannels(image);
   4004         }
   4005         sync=SyncCacheViewAuthenticPixels(image_view,exception);
   4006         if (sync == MagickFalse)
   4007           status=MagickFalse;
   4008         if (image->progress_monitor != (MagickProgressMonitor) NULL)
   4009           {
   4010             MagickBooleanType
   4011               proceed;
   4012 
   4013 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4014             #pragma omp atomic
   4015 #endif
   4016             progress++;
   4017             proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
   4018             if (proceed == MagickFalse)
   4019               status=MagickFalse;
   4020           }
   4021       }
   4022       image_view=DestroyCacheView(image_view);
   4023       return(MagickTrue);
   4024     }
   4025   /*
   4026     Negate image.
   4027   */
   4028 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4029   #pragma omp parallel for schedule(static) shared(progress,status) \
   4030     magick_number_threads(image,image,image->rows,1)
   4031 #endif
   4032   for (y=0; y < (ssize_t) image->rows; y++)
   4033   {
   4034     register Quantum
   4035       *magick_restrict q;
   4036 
   4037     register ssize_t
   4038       x;
   4039 
   4040     if (status == MagickFalse)
   4041       continue;
   4042     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   4043     if (q == (Quantum *) NULL)
   4044       {
   4045         status=MagickFalse;
   4046         continue;
   4047       }
   4048     for (x=0; x < (ssize_t) image->columns; x++)
   4049     {
   4050       register ssize_t
   4051         j;
   4052 
   4053       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
   4054       {
   4055         PixelChannel channel = GetPixelChannelChannel(image,j);
   4056         PixelTrait traits = GetPixelChannelTraits(image,channel);
   4057         if ((traits & UpdatePixelTrait) == 0)
   4058           continue;
   4059         q[j]=QuantumRange-q[j];
   4060       }
   4061       q+=GetPixelChannels(image);
   4062     }
   4063     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   4064       status=MagickFalse;
   4065     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   4066       {
   4067         MagickBooleanType
   4068           proceed;
   4069 
   4070 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4071         #pragma omp atomic
   4072 #endif
   4073         progress++;
   4074         proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
   4075         if (proceed == MagickFalse)
   4076           status=MagickFalse;
   4077       }
   4078   }
   4079   image_view=DestroyCacheView(image_view);
   4080   return(status);
   4081 }
   4082 
   4083 /*
   4085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4086 %                                                                             %
   4087 %                                                                             %
   4088 %                                                                             %
   4089 %     N o r m a l i z e I m a g e                                             %
   4090 %                                                                             %
   4091 %                                                                             %
   4092 %                                                                             %
   4093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4094 %
   4095 %  The NormalizeImage() method enhances the contrast of a color image by
   4096 %  mapping the darkest 2 percent of all pixel to black and the brightest
   4097 %  1 percent to white.
   4098 %
   4099 %  The format of the NormalizeImage method is:
   4100 %
   4101 %      MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
   4102 %
   4103 %  A description of each parameter follows:
   4104 %
   4105 %    o image: the image.
   4106 %
   4107 %    o exception: return any errors or warnings in this structure.
   4108 %
   4109 */
   4110 MagickExport MagickBooleanType NormalizeImage(Image *image,
   4111   ExceptionInfo *exception)
   4112 {
   4113   double
   4114     black_point,
   4115     white_point;
   4116 
   4117   black_point=(double) image->columns*image->rows*0.0015;
   4118   white_point=(double) image->columns*image->rows*0.9995;
   4119   return(ContrastStretchImage(image,black_point,white_point,exception));
   4120 }
   4121 
   4122 /*
   4124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4125 %                                                                             %
   4126 %                                                                             %
   4127 %                                                                             %
   4128 %     S i g m o i d a l C o n t r a s t I m a g e                             %
   4129 %                                                                             %
   4130 %                                                                             %
   4131 %                                                                             %
   4132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4133 %
   4134 %  SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
   4135 %  sigmoidal contrast algorithm.  Increase the contrast of the image using a
   4136 %  sigmoidal transfer function without saturating highlights or shadows.
   4137 %  Contrast indicates how much to increase the contrast (0 is none; 3 is
   4138 %  typical; 20 is pushing it); mid-point indicates where midtones fall in the
   4139 %  resultant image (0 is white; 50% is middle-gray; 100% is black).  Set
   4140 %  sharpen to MagickTrue to increase the image contrast otherwise the contrast
   4141 %  is reduced.
   4142 %
   4143 %  The format of the SigmoidalContrastImage method is:
   4144 %
   4145 %      MagickBooleanType SigmoidalContrastImage(Image *image,
   4146 %        const MagickBooleanType sharpen,const char *levels,
   4147 %        ExceptionInfo *exception)
   4148 %
   4149 %  A description of each parameter follows:
   4150 %
   4151 %    o image: the image.
   4152 %
   4153 %    o sharpen: Increase or decrease image contrast.
   4154 %
   4155 %    o contrast: strength of the contrast, the larger the number the more
   4156 %      'threshold-like' it becomes.
   4157 %
   4158 %    o midpoint: midpoint of the function as a color value 0 to QuantumRange.
   4159 %
   4160 %    o exception: return any errors or warnings in this structure.
   4161 %
   4162 */
   4163 
   4164 /*
   4165   ImageMagick 6 has a version of this function which uses LUTs.
   4166 */
   4167 
   4168 /*
   4169   Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
   4170   constant" set to a.
   4171 
   4172   The first version, based on the hyperbolic tangent tanh, when combined with
   4173   the scaling step, is an exact arithmetic clone of the the sigmoid function
   4174   based on the logistic curve. The equivalence is based on the identity
   4175 
   4176     1/(1+exp(-t)) = (1+tanh(t/2))/2
   4177 
   4178   (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
   4179   scaled sigmoidal derivation is invariant under affine transformations of
   4180   the ordinate.
   4181 
   4182   The tanh version is almost certainly more accurate and cheaper.  The 0.5
   4183   factor in the argument is to clone the legacy ImageMagick behavior. The
   4184   reason for making the define depend on atanh even though it only uses tanh
   4185   has to do with the construction of the inverse of the scaled sigmoidal.
   4186 */
   4187 #if defined(MAGICKCORE_HAVE_ATANH)
   4188 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
   4189 #else
   4190 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
   4191 #endif
   4192 /*
   4193   Scaled sigmoidal function:
   4194 
   4195     ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
   4196     ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
   4197 
   4198   See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
   4199   http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf.  The limit
   4200   of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
   4201   zero. This is fixed below by exiting immediately when contrast is small,
   4202   leaving the image (or colormap) unmodified. This appears to be safe because
   4203   the series expansion of the logistic sigmoidal function around x=b is
   4204 
   4205   1/2-a*(b-x)/4+...
   4206 
   4207   so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
   4208 */
   4209 #define ScaledSigmoidal(a,b,x) (                    \
   4210   (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
   4211   (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
   4212 /*
   4213   Inverse of ScaledSigmoidal, used for +sigmoidal-contrast.  Because b
   4214   may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
   4215   sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
   4216   when creating a LUT from in gamut values, hence the branching.  In
   4217   addition, HDRI may have out of gamut values.
   4218   InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
   4219   It is only a right inverse. This is unavoidable.
   4220 */
   4221 static inline double InverseScaledSigmoidal(const double a,const double b,
   4222   const double x)
   4223 {
   4224   const double sig0=Sigmoidal(a,b,0.0);
   4225   const double sig1=Sigmoidal(a,b,1.0);
   4226   const double argument=(sig1-sig0)*x+sig0;
   4227   const double clamped=
   4228     (
   4229 #if defined(MAGICKCORE_HAVE_ATANH)
   4230       argument < -1+MagickEpsilon
   4231       ?
   4232       -1+MagickEpsilon
   4233       :
   4234       ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
   4235     );
   4236   return(b+(2.0/a)*atanh(clamped));
   4237 #else
   4238       argument < MagickEpsilon
   4239       ?
   4240       MagickEpsilon
   4241       :
   4242       ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
   4243     );
   4244   return(b-log(1.0/clamped-1.0)/a);
   4245 #endif
   4246 }
   4247 
   4248 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
   4249   const MagickBooleanType sharpen,const double contrast,const double midpoint,
   4250   ExceptionInfo *exception)
   4251 {
   4252 #define SigmoidalContrastImageTag  "SigmoidalContrast/Image"
   4253 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
   4254   ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
   4255 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
   4256   InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
   4257 
   4258   CacheView
   4259     *image_view;
   4260 
   4261   MagickBooleanType
   4262     status;
   4263 
   4264   MagickOffsetType
   4265     progress;
   4266 
   4267   ssize_t
   4268     y;
   4269 
   4270   /*
   4271     Convenience macros.
   4272   */
   4273   assert(image != (Image *) NULL);
   4274   assert(image->signature == MagickCoreSignature);
   4275   if (image->debug != MagickFalse)
   4276     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   4277   /*
   4278     Side effect: may clamp values unless contrast<MagickEpsilon, in which
   4279     case nothing is done.
   4280   */
   4281   if (contrast < MagickEpsilon)
   4282     return(MagickTrue);
   4283   /*
   4284     Sigmoidal-contrast enhance colormap.
   4285   */
   4286   if (image->storage_class == PseudoClass)
   4287     {
   4288       register ssize_t
   4289         i;
   4290 
   4291       if( sharpen != MagickFalse )
   4292         for (i=0; i < (ssize_t) image->colors; i++)
   4293         {
   4294           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   4295             image->colormap[i].red=(MagickRealType) ScaledSig(
   4296               image->colormap[i].red);
   4297           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   4298             image->colormap[i].green=(MagickRealType) ScaledSig(
   4299               image->colormap[i].green);
   4300           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   4301             image->colormap[i].blue=(MagickRealType) ScaledSig(
   4302               image->colormap[i].blue);
   4303           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
   4304             image->colormap[i].alpha=(MagickRealType) ScaledSig(
   4305               image->colormap[i].alpha);
   4306         }
   4307       else
   4308         for (i=0; i < (ssize_t) image->colors; i++)
   4309         {
   4310           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
   4311             image->colormap[i].red=(MagickRealType) InverseScaledSig(
   4312               image->colormap[i].red);
   4313           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
   4314             image->colormap[i].green=(MagickRealType) InverseScaledSig(
   4315               image->colormap[i].green);
   4316           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
   4317             image->colormap[i].blue=(MagickRealType) InverseScaledSig(
   4318               image->colormap[i].blue);
   4319           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
   4320             image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
   4321               image->colormap[i].alpha);
   4322         }
   4323     }
   4324   /*
   4325     Sigmoidal-contrast enhance image.
   4326   */
   4327   status=MagickTrue;
   4328   progress=0;
   4329   image_view=AcquireAuthenticCacheView(image,exception);
   4330 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4331   #pragma omp parallel for schedule(static) shared(progress,status) \
   4332     magick_number_threads(image,image,image->rows,1)
   4333 #endif
   4334   for (y=0; y < (ssize_t) image->rows; y++)
   4335   {
   4336     register Quantum
   4337       *magick_restrict q;
   4338 
   4339     register ssize_t
   4340       x;
   4341 
   4342     if (status == MagickFalse)
   4343       continue;
   4344     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   4345     if (q == (Quantum *) NULL)
   4346       {
   4347         status=MagickFalse;
   4348         continue;
   4349       }
   4350     for (x=0; x < (ssize_t) image->columns; x++)
   4351     {
   4352       register ssize_t
   4353         i;
   4354 
   4355       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   4356       {
   4357         PixelChannel channel = GetPixelChannelChannel(image,i);
   4358         PixelTrait traits = GetPixelChannelTraits(image,channel);
   4359         if ((traits & UpdatePixelTrait) == 0)
   4360           continue;
   4361         if( sharpen != MagickFalse )
   4362           q[i]=ScaledSig(q[i]);
   4363         else
   4364           q[i]=InverseScaledSig(q[i]);
   4365       }
   4366       q+=GetPixelChannels(image);
   4367     }
   4368     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   4369       status=MagickFalse;
   4370     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   4371       {
   4372         MagickBooleanType
   4373           proceed;
   4374 
   4375 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4376         #pragma omp atomic
   4377 #endif
   4378         progress++;
   4379         proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress,
   4380           image->rows);
   4381         if (proceed == MagickFalse)
   4382           status=MagickFalse;
   4383       }
   4384   }
   4385   image_view=DestroyCacheView(image_view);
   4386   return(status);
   4387 }
   4388