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