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