]> granicus.if.org Git - imagemagick/blob - MagickCore/enhance.c
9bdd80443a6447f7f923bc00fe4f2b6b0855a23b
[imagemagick] / MagickCore / enhance.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %              EEEEE  N   N  H   H   AAA   N   N   CCCC  EEEEE                %
7 %              E      NN  N  H   H  A   A  NN  N  C      E                    %
8 %              EEE    N N N  HHHHH  AAAAA  N N N  C      EEE                  %
9 %              E      N  NN  H   H  A   A  N  NN  C      E                    %
10 %              EEEEE  N   N  H   H  A   A  N   N   CCCC  EEEEE                %
11 %                                                                             %
12 %                                                                             %
13 %                    MagickCore Image Enhancement Methods                     %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/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       *magick_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       *magick_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       *magick_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       *magick_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       pixel=GetPixelIntensity(image,p);
1096       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1097       {
1098         if (image->channel_mask != DefaultChannels)
1099           pixel=(double) p[i];
1100         histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1101           ClampToQuantum(pixel))+i]++;
1102       }
1103       p+=GetPixelChannels(image);
1104     }
1105   }
1106   image_view=DestroyCacheView(image_view);
1107   /*
1108     Find the histogram boundaries by locating the black/white levels.
1109   */
1110   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1111   {
1112     double
1113       intensity;
1114
1115     register ssize_t
1116       j;
1117
1118     black[i]=0.0;
1119     white[i]=MaxRange(QuantumRange);
1120     intensity=0.0;
1121     for (j=0; j <= (ssize_t) MaxMap; j++)
1122     {
1123       intensity+=histogram[GetPixelChannels(image)*j+i];
1124       if (intensity > black_point)
1125         break;
1126     }
1127     black[i]=(double) j;
1128     intensity=0.0;
1129     for (j=(ssize_t) MaxMap; j != 0; j--)
1130     {
1131       intensity+=histogram[GetPixelChannels(image)*j+i];
1132       if (intensity > ((double) image->columns*image->rows-white_point))
1133         break;
1134     }
1135     white[i]=(double) j;
1136   }
1137   histogram=(double *) RelinquishMagickMemory(histogram);
1138   /*
1139     Stretch the histogram to create the stretched image mapping.
1140   */
1141   (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1142     sizeof(*stretch_map));
1143   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1144   {
1145     register ssize_t
1146       j;
1147
1148     for (j=0; j <= (ssize_t) MaxMap; j++)
1149     {
1150       double
1151         gamma;
1152
1153       gamma=PerceptibleReciprocal(white[i]-black[i]);
1154       if (j < (ssize_t) black[i])
1155         stretch_map[GetPixelChannels(image)*j+i]=0.0;
1156       else
1157         if (j > (ssize_t) white[i])
1158           stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange;
1159         else
1160           stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum(
1161             (double) (MaxMap*gamma*(j-black[i])));
1162     }
1163   }
1164   if (image->storage_class == PseudoClass)
1165     {
1166       register ssize_t
1167         j;
1168
1169       /*
1170         Stretch-contrast colormap.
1171       */
1172       for (j=0; j < (ssize_t) image->colors; j++)
1173       {
1174         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1175           {
1176             i=GetPixelChannelOffset(image,RedPixelChannel);
1177             image->colormap[j].red=stretch_map[GetPixelChannels(image)*
1178               ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+i];
1179           }
1180         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1181           {
1182             i=GetPixelChannelOffset(image,GreenPixelChannel);
1183             image->colormap[j].green=stretch_map[GetPixelChannels(image)*
1184               ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+i];
1185           }
1186         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1187           {
1188             i=GetPixelChannelOffset(image,BluePixelChannel);
1189             image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
1190               ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+i];
1191           }
1192         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1193           {
1194             i=GetPixelChannelOffset(image,AlphaPixelChannel);
1195             image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
1196               ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+i];
1197           }
1198       }
1199     }
1200   /*
1201     Stretch-contrast image.
1202   */
1203   status=MagickTrue;
1204   progress=0;
1205   image_view=AcquireAuthenticCacheView(image,exception);
1206 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1207   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1208     magick_threads(image,image,image->rows,1)
1209 #endif
1210   for (y=0; y < (ssize_t) image->rows; y++)
1211   {
1212     register Quantum
1213       *magick_restrict q;
1214
1215     register ssize_t
1216       x;
1217
1218     if (status == MagickFalse)
1219       continue;
1220     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1221     if (q == (Quantum *) NULL)
1222       {
1223         status=MagickFalse;
1224         continue;
1225       }
1226     for (x=0; x < (ssize_t) image->columns; x++)
1227     {
1228       register ssize_t
1229         j;
1230
1231       if (GetPixelReadMask(image,q) == 0)
1232         {
1233           q+=GetPixelChannels(image);
1234           continue;
1235         }
1236       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1237       {
1238         PixelChannel channel=GetPixelChannelChannel(image,j);
1239         PixelTrait traits=GetPixelChannelTraits(image,channel);
1240         if ((traits & UpdatePixelTrait) == 0)
1241           continue;
1242         q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1243           ScaleQuantumToMap(q[j])+j]);
1244       }
1245       q+=GetPixelChannels(image);
1246     }
1247     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1248       status=MagickFalse;
1249     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1250       {
1251         MagickBooleanType
1252           proceed;
1253
1254 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1255         #pragma omp critical (MagickCore_ContrastStretchImage)
1256 #endif
1257         proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1258           image->rows);
1259         if (proceed == MagickFalse)
1260           status=MagickFalse;
1261       }
1262   }
1263   image_view=DestroyCacheView(image_view);
1264   stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1265   white=(double *) RelinquishMagickMemory(white);
1266   black=(double *) RelinquishMagickMemory(black);
1267   return(status);
1268 }
1269 \f
1270 /*
1271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1272 %                                                                             %
1273 %                                                                             %
1274 %                                                                             %
1275 %     E n h a n c e I m a g e                                                 %
1276 %                                                                             %
1277 %                                                                             %
1278 %                                                                             %
1279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1280 %
1281 %  EnhanceImage() applies a digital filter that improves the quality of a
1282 %  noisy image.
1283 %
1284 %  The format of the EnhanceImage method is:
1285 %
1286 %      Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1287 %
1288 %  A description of each parameter follows:
1289 %
1290 %    o image: the image.
1291 %
1292 %    o exception: return any errors or warnings in this structure.
1293 %
1294 */
1295 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1296 {
1297 #define EnhanceImageTag  "Enhance/Image"
1298 #define EnhancePixel(weight) \
1299   mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1300   distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1301   distance_squared=(4.0+mean)*distance*distance; \
1302   mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1303   distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1304   distance_squared+=(7.0-mean)*distance*distance; \
1305   mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1306   distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1307   distance_squared+=(5.0-mean)*distance*distance; \
1308   mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1309   distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1310   distance_squared+=(5.0-mean)*distance*distance; \
1311   mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1312   distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1313   distance_squared+=(5.0-mean)*distance*distance; \
1314   if (distance_squared < 0.069) \
1315     { \
1316       aggregate.red+=(weight)*GetPixelRed(image,r); \
1317       aggregate.green+=(weight)*GetPixelGreen(image,r); \
1318       aggregate.blue+=(weight)*GetPixelBlue(image,r); \
1319       aggregate.black+=(weight)*GetPixelBlack(image,r); \
1320       aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \
1321       total_weight+=(weight); \
1322     } \
1323   r+=GetPixelChannels(image);
1324
1325   CacheView
1326     *enhance_view,
1327     *image_view;
1328
1329   Image
1330     *enhance_image;
1331
1332   MagickBooleanType
1333     status;
1334
1335   MagickOffsetType
1336     progress;
1337
1338   ssize_t
1339     y;
1340
1341   /*
1342     Initialize enhanced image attributes.
1343   */
1344   assert(image != (const Image *) NULL);
1345   assert(image->signature == MagickCoreSignature);
1346   if (image->debug != MagickFalse)
1347     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1348   assert(exception != (ExceptionInfo *) NULL);
1349   assert(exception->signature == MagickCoreSignature);
1350   enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1351     exception);
1352   if (enhance_image == (Image *) NULL)
1353     return((Image *) NULL);
1354   if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1355     {
1356       enhance_image=DestroyImage(enhance_image);
1357       return((Image *) NULL);
1358     }
1359   /*
1360     Enhance image.
1361   */
1362   status=MagickTrue;
1363   progress=0;
1364   image_view=AcquireVirtualCacheView(image,exception);
1365   enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1366 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1367   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1368     magick_threads(image,enhance_image,image->rows,1)
1369 #endif
1370   for (y=0; y < (ssize_t) image->rows; y++)
1371   {
1372     PixelInfo
1373       pixel;
1374
1375     register const Quantum
1376       *magick_restrict p;
1377
1378     register Quantum
1379       *magick_restrict q;
1380
1381     register ssize_t
1382       x;
1383
1384     ssize_t
1385       center;
1386
1387     if (status == MagickFalse)
1388       continue;
1389     p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1390     q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1391       exception);
1392     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1393       {
1394         status=MagickFalse;
1395         continue;
1396       }
1397     center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1398     GetPixelInfo(image,&pixel);
1399     for (x=0; x < (ssize_t) image->columns; x++)
1400     {
1401       double
1402         distance,
1403         distance_squared,
1404         mean,
1405         total_weight;
1406
1407       PixelInfo
1408         aggregate;
1409
1410       register const Quantum
1411         *magick_restrict r;
1412
1413       if (GetPixelReadMask(image,p) == 0)
1414         {
1415           SetPixelBackgoundColor(enhance_image,q);
1416           p+=GetPixelChannels(image);
1417           q+=GetPixelChannels(enhance_image);
1418           continue;
1419         }
1420       GetPixelInfo(image,&aggregate);
1421       total_weight=0.0;
1422       GetPixelInfoPixel(image,p+center,&pixel);
1423       r=p;
1424       EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1425         EnhancePixel(8.0); EnhancePixel(5.0);
1426       r=p+GetPixelChannels(image)*(image->columns+4);
1427       EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1428         EnhancePixel(20.0); EnhancePixel(8.0);
1429       r=p+2*GetPixelChannels(image)*(image->columns+4);
1430       EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1431         EnhancePixel(40.0); EnhancePixel(10.0);
1432       r=p+3*GetPixelChannels(image)*(image->columns+4);
1433       EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1434         EnhancePixel(20.0); EnhancePixel(8.0);
1435       r=p+4*GetPixelChannels(image)*(image->columns+4);
1436       EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1437         EnhancePixel(8.0); EnhancePixel(5.0);
1438       pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1439       pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1440       pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1441       pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1442       pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
1443       SetPixelViaPixelInfo(image,&pixel,q);
1444       p+=GetPixelChannels(image);
1445       q+=GetPixelChannels(enhance_image);
1446     }
1447     if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1448       status=MagickFalse;
1449     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1450       {
1451         MagickBooleanType
1452           proceed;
1453
1454 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1455         #pragma omp critical (MagickCore_EnhanceImage)
1456 #endif
1457         proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1458         if (proceed == MagickFalse)
1459           status=MagickFalse;
1460       }
1461   }
1462   enhance_view=DestroyCacheView(enhance_view);
1463   image_view=DestroyCacheView(image_view);
1464   if (status == MagickFalse)
1465     enhance_image=DestroyImage(enhance_image);
1466   return(enhance_image);
1467 }
1468 \f
1469 /*
1470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1471 %                                                                             %
1472 %                                                                             %
1473 %                                                                             %
1474 %     E q u a l i z e I m a g e                                               %
1475 %                                                                             %
1476 %                                                                             %
1477 %                                                                             %
1478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1479 %
1480 %  EqualizeImage() applies a histogram equalization to the image.
1481 %
1482 %  The format of the EqualizeImage method is:
1483 %
1484 %      MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
1485 %
1486 %  A description of each parameter follows:
1487 %
1488 %    o image: the image.
1489 %
1490 %    o exception: return any errors or warnings in this structure.
1491 %
1492 */
1493 MagickExport MagickBooleanType EqualizeImage(Image *image,
1494   ExceptionInfo *exception)
1495 {
1496 #define EqualizeImageTag  "Equalize/Image"
1497
1498   CacheView
1499     *image_view;
1500
1501   MagickBooleanType
1502     status;
1503
1504   MagickOffsetType
1505     progress;
1506
1507   double
1508     black[CompositePixelChannel+1],
1509     *equalize_map,
1510     *histogram,
1511     *map,
1512     white[CompositePixelChannel+1];
1513
1514   register ssize_t
1515     i;
1516
1517   ssize_t
1518     y;
1519
1520   /*
1521     Allocate and initialize histogram arrays.
1522   */
1523   assert(image != (Image *) NULL);
1524   assert(image->signature == MagickCoreSignature);
1525   if (image->debug != MagickFalse)
1526     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1527   equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1528     GetPixelChannels(image)*sizeof(*equalize_map));
1529   histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1530     sizeof(*histogram));
1531   map=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1532     sizeof(*map));
1533   if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
1534       (map == (double *) NULL))
1535     {
1536       if (map != (double *) NULL)
1537         map=(double *) RelinquishMagickMemory(map);
1538       if (histogram != (double *) NULL)
1539         histogram=(double *) RelinquishMagickMemory(histogram);
1540       if (equalize_map != (double *) NULL)
1541         equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1542       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1543         image->filename);
1544     }
1545   /*
1546     Form histogram.
1547   */
1548   status=MagickTrue;
1549   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1550     sizeof(*histogram));
1551   image_view=AcquireVirtualCacheView(image,exception);
1552   for (y=0; y < (ssize_t) image->rows; y++)
1553   {
1554     register const Quantum
1555       *magick_restrict p;
1556
1557     register ssize_t
1558       x;
1559
1560     if (status == MagickFalse)
1561       continue;
1562     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1563     if (p == (const Quantum *) NULL)
1564       {
1565         status=MagickFalse;
1566         continue;
1567       }
1568     for (x=0; x < (ssize_t) image->columns; x++)
1569     {
1570       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1571         histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++;
1572       p+=GetPixelChannels(image);
1573     }
1574   }
1575   image_view=DestroyCacheView(image_view);
1576   /*
1577     Integrate the histogram to get the equalization map.
1578   */
1579   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1580   {
1581     double
1582       intensity;
1583
1584     register ssize_t
1585       j;
1586
1587     intensity=0.0;
1588     for (j=0; j <= (ssize_t) MaxMap; j++)
1589     {
1590       intensity+=histogram[GetPixelChannels(image)*j+i];
1591       map[GetPixelChannels(image)*j+i]=intensity;
1592     }
1593   }
1594   (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
1595     sizeof(*equalize_map));
1596   (void) ResetMagickMemory(black,0,sizeof(*black));
1597   (void) ResetMagickMemory(white,0,sizeof(*white));
1598   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1599   {
1600     register ssize_t
1601       j;
1602
1603     black[i]=map[i];
1604     white[i]=map[GetPixelChannels(image)*MaxMap+i];
1605     if (black[i] != white[i])
1606       for (j=0; j <= (ssize_t) MaxMap; j++)
1607         equalize_map[GetPixelChannels(image)*j+i]=(double)
1608           ScaleMapToQuantum((double) ((MaxMap*(map[
1609           GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
1610   }
1611   histogram=(double *) RelinquishMagickMemory(histogram);
1612   map=(double *) RelinquishMagickMemory(map);
1613   if (image->storage_class == PseudoClass)
1614     {
1615       register ssize_t
1616         j;
1617
1618       /*
1619         Equalize colormap.
1620       */
1621       for (j=0; j < (ssize_t) image->colors; j++)
1622       {
1623         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1624           {
1625             PixelChannel channel=GetPixelChannelChannel(image,RedPixelChannel);
1626             if (black[channel] != white[channel])
1627               image->colormap[j].red=equalize_map[GetPixelChannels(image)*
1628                 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+
1629                 channel;
1630           }
1631         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1632           {
1633             PixelChannel channel=GetPixelChannelChannel(image,
1634               GreenPixelChannel);
1635             if (black[channel] != white[channel])
1636               image->colormap[j].green=equalize_map[GetPixelChannels(image)*
1637                 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+
1638                 channel;
1639           }
1640         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1641           {
1642             PixelChannel channel=GetPixelChannelChannel(image,BluePixelChannel);
1643             if (black[channel] != white[channel])
1644               image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
1645                 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+
1646                 channel;
1647           }
1648         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1649           {
1650             PixelChannel channel=GetPixelChannelChannel(image,
1651               AlphaPixelChannel);
1652             if (black[channel] != white[channel])
1653               image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
1654                 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+
1655                 channel;
1656           }
1657       }
1658     }
1659   /*
1660     Equalize image.
1661   */
1662   progress=0;
1663   image_view=AcquireAuthenticCacheView(image,exception);
1664 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1665   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1666     magick_threads(image,image,image->rows,1)
1667 #endif
1668   for (y=0; y < (ssize_t) image->rows; y++)
1669   {
1670     register Quantum
1671       *magick_restrict q;
1672
1673     register ssize_t
1674       x;
1675
1676     if (status == MagickFalse)
1677       continue;
1678     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1679     if (q == (Quantum *) NULL)
1680       {
1681         status=MagickFalse;
1682         continue;
1683       }
1684     for (x=0; x < (ssize_t) image->columns; x++)
1685     {
1686       register ssize_t
1687         j;
1688
1689       if (GetPixelReadMask(image,q) == 0)
1690         {
1691           q+=GetPixelChannels(image);
1692           continue;
1693         }
1694       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1695       {
1696         PixelChannel channel=GetPixelChannelChannel(image,j);
1697         PixelTrait traits=GetPixelChannelTraits(image,channel);
1698         if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
1699           continue;
1700         q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
1701           ScaleQuantumToMap(q[j])+j]);
1702       }
1703       q+=GetPixelChannels(image);
1704     }
1705     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
1706       status=MagickFalse;
1707     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1708       {
1709         MagickBooleanType
1710           proceed;
1711
1712 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1713         #pragma omp critical (MagickCore_EqualizeImage)
1714 #endif
1715         proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1716         if( IfMagickFalse(proceed) )
1717           status=MagickFalse;
1718       }
1719   }
1720   image_view=DestroyCacheView(image_view);
1721   equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1722   return(status);
1723 }
1724 \f
1725 /*
1726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727 %                                                                             %
1728 %                                                                             %
1729 %                                                                             %
1730 %     G a m m a I m a g e                                                     %
1731 %                                                                             %
1732 %                                                                             %
1733 %                                                                             %
1734 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1735 %
1736 %  GammaImage() gamma-corrects a particular image channel.  The same
1737 %  image viewed on different devices will have perceptual differences in the
1738 %  way the image's intensities are represented on the screen.  Specify
1739 %  individual gamma levels for the red, green, and blue channels, or adjust
1740 %  all three with the gamma parameter.  Values typically range from 0.8 to 2.3.
1741 %
1742 %  You can also reduce the influence of a particular channel with a gamma
1743 %  value of 0.
1744 %
1745 %  The format of the GammaImage method is:
1746 %
1747 %      MagickBooleanType GammaImage(Image *image,const double gamma,
1748 %        ExceptionInfo *exception)
1749 %
1750 %  A description of each parameter follows:
1751 %
1752 %    o image: the image.
1753 %
1754 %    o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1755 %
1756 %    o gamma: the image gamma.
1757 %
1758 */
1759
1760 static inline double gamma_pow(const double value,const double gamma)
1761 {
1762   return(value < 0.0 ? value : pow(value,gamma));
1763 }
1764
1765 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
1766   ExceptionInfo *exception)
1767 {
1768 #define GammaCorrectImageTag  "GammaCorrect/Image"
1769
1770   CacheView
1771     *image_view;
1772
1773   MagickBooleanType
1774     status;
1775
1776   MagickOffsetType
1777     progress;
1778
1779   Quantum
1780     *gamma_map;
1781
1782   register ssize_t
1783     i;
1784
1785   ssize_t
1786     y;
1787
1788   /*
1789     Allocate and initialize gamma maps.
1790   */
1791   assert(image != (Image *) NULL);
1792   assert(image->signature == MagickCoreSignature);
1793   if (image->debug != MagickFalse)
1794     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1795   if (gamma == 1.0)
1796     return(MagickTrue);
1797   gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1798   if (gamma_map == (Quantum *) NULL)
1799     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1800       image->filename);
1801   (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1802   if (gamma != 0.0)
1803     for (i=0; i <= (ssize_t) MaxMap; i++)
1804       gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
1805         MaxMap,1.0/gamma)));
1806   if (image->storage_class == PseudoClass)
1807     for (i=0; i < (ssize_t) image->colors; i++)
1808     {
1809       /*
1810         Gamma-correct colormap.
1811       */
1812 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1813       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1814         image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
1815           ClampToQuantum(image->colormap[i].red))];
1816       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1817         image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
1818           ClampToQuantum(image->colormap[i].green))];
1819       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1820         image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
1821           ClampToQuantum(image->colormap[i].blue))];
1822       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1823         image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
1824           ClampToQuantum(image->colormap[i].alpha))];
1825 #else
1826       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1827         image->colormap[i].red=QuantumRange*gamma_pow(QuantumScale*
1828           image->colormap[i].red,1.0/gamma);
1829       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1830         image->colormap[i].green=QuantumRange*gamma_pow(QuantumScale*
1831           image->colormap[i].green,1.0/gamma);
1832       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1833         image->colormap[i].blue=QuantumRange*gamma_pow(QuantumScale*
1834           image->colormap[i].blue,1.0/gamma);
1835       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1836         image->colormap[i].alpha=QuantumRange*gamma_pow(QuantumScale*
1837           image->colormap[i].alpha,1.0/gamma);
1838 #endif
1839     }
1840   /*
1841     Gamma-correct image.
1842   */
1843   status=MagickTrue;
1844   progress=0;
1845   image_view=AcquireAuthenticCacheView(image,exception);
1846 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1847   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1848     magick_threads(image,image,image->rows,1)
1849 #endif
1850   for (y=0; y < (ssize_t) image->rows; y++)
1851   {
1852     register Quantum
1853       *magick_restrict q;
1854
1855     register ssize_t
1856       x;
1857
1858     if (status == MagickFalse)
1859       continue;
1860     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1861     if (q == (Quantum *) NULL)
1862       {
1863         status=MagickFalse;
1864         continue;
1865       }
1866     for (x=0; x < (ssize_t) image->columns; x++)
1867     {
1868       register ssize_t
1869         j;
1870
1871       if (GetPixelReadMask(image,q) == 0)
1872         {
1873           q+=GetPixelChannels(image);
1874           continue;
1875         }
1876       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1877       {
1878         PixelChannel channel=GetPixelChannelChannel(image,j);
1879         PixelTrait traits=GetPixelChannelTraits(image,channel);
1880         if ((traits & UpdatePixelTrait) == 0)
1881           continue;
1882 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1883         q[j]=gamma_map[ScaleQuantumToMap(q[j])];
1884 #else
1885         q[j]=QuantumRange*gamma_pow(QuantumScale*q[j],1.0/gamma);
1886 #endif
1887       }
1888       q+=GetPixelChannels(image);
1889     }
1890     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
1891       status=MagickFalse;
1892     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1893       {
1894         MagickBooleanType
1895           proceed;
1896
1897 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1898         #pragma omp critical (MagickCore_GammaImage)
1899 #endif
1900         proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
1901           image->rows);
1902         if( IfMagickFalse(proceed) )
1903           status=MagickFalse;
1904       }
1905   }
1906   image_view=DestroyCacheView(image_view);
1907   gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
1908   if (image->gamma != 0.0)
1909     image->gamma*=gamma;
1910   return(status);
1911 }
1912 \f
1913 /*
1914 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1915 %                                                                             %
1916 %                                                                             %
1917 %                                                                             %
1918 %     G r a y s c a l e I m a g e                                             %
1919 %                                                                             %
1920 %                                                                             %
1921 %                                                                             %
1922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1923 %
1924 %  GrayscaleImage() converts the image to grayscale.
1925 %
1926 %  The format of the GrayscaleImage method is:
1927 %
1928 %      MagickBooleanType GrayscaleImage(Image *image,
1929 %        const PixelIntensityMethod method ,ExceptionInfo *exception)
1930 %
1931 %  A description of each parameter follows:
1932 %
1933 %    o image: the image.
1934 %
1935 %    o method: the pixel intensity method.
1936 %
1937 %    o exception: return any errors or warnings in this structure.
1938 %
1939 */
1940 MagickExport MagickBooleanType GrayscaleImage(Image *image,
1941   const PixelIntensityMethod method,ExceptionInfo *exception)
1942 {
1943 #define GrayscaleImageTag  "Grayscale/Image"
1944
1945   CacheView
1946     *image_view;
1947
1948   MagickBooleanType
1949     status;
1950
1951   MagickOffsetType
1952     progress;
1953
1954   ssize_t
1955     y;
1956
1957   assert(image != (Image *) NULL);
1958   assert(image->signature == MagickCoreSignature);
1959   if (image->debug != MagickFalse)
1960     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1961   if (image->storage_class == PseudoClass)
1962     {
1963       if( IfMagickFalse(SyncImage(image,exception)) )
1964         return(MagickFalse);
1965       if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
1966         return(MagickFalse);
1967     }
1968   /*
1969     Grayscale image.
1970   */
1971   status=MagickTrue;
1972   progress=0;
1973   image_view=AcquireAuthenticCacheView(image,exception);
1974 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1975   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1976     magick_threads(image,image,image->rows,1)
1977 #endif
1978   for (y=0; y < (ssize_t) image->rows; y++)
1979   {
1980     register Quantum
1981       *magick_restrict q;
1982
1983     register ssize_t
1984       x;
1985
1986     if (status == MagickFalse)
1987       continue;
1988     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1989     if (q == (Quantum *) NULL)
1990       {
1991         status=MagickFalse;
1992         continue;
1993       }
1994     for (x=0; x < (ssize_t) image->columns; x++)
1995     {
1996       MagickRealType
1997         blue,
1998         green,
1999         red,
2000         intensity;
2001
2002       if (GetPixelReadMask(image,q) == 0)
2003         {
2004           q+=GetPixelChannels(image);
2005           continue;
2006         }
2007       red=(MagickRealType) GetPixelRed(image,q);
2008       green=(MagickRealType) GetPixelGreen(image,q);
2009       blue=(MagickRealType) GetPixelBlue(image,q);
2010       intensity=0.0;
2011       switch (method)
2012       {
2013         case AveragePixelIntensityMethod:
2014         {
2015           intensity=(red+green+blue)/3.0;
2016           break;
2017         }
2018         case BrightnessPixelIntensityMethod:
2019         {
2020           intensity=MagickMax(MagickMax(red,green),blue);
2021           break;
2022         }
2023         case LightnessPixelIntensityMethod:
2024         {
2025           intensity=(MagickMin(MagickMin(red,green),blue)+
2026             MagickMax(MagickMax(red,green),blue))/2.0;
2027           break;
2028         }
2029         case MSPixelIntensityMethod:
2030         {
2031           intensity=(MagickRealType) (((double) red*red+green*green+
2032             blue*blue)/3.0);
2033           break;
2034         }
2035         case Rec601LumaPixelIntensityMethod:
2036         {
2037           if (image->colorspace == RGBColorspace)
2038             {
2039               red=EncodePixelGamma(red);
2040               green=EncodePixelGamma(green);
2041               blue=EncodePixelGamma(blue);
2042             }
2043           intensity=0.298839*red+0.586811*green+0.114350*blue;
2044           break;
2045         }
2046         case Rec601LuminancePixelIntensityMethod:
2047         {
2048           if (image->colorspace == sRGBColorspace)
2049             {
2050               red=DecodePixelGamma(red);
2051               green=DecodePixelGamma(green);
2052               blue=DecodePixelGamma(blue);
2053             }
2054           intensity=0.298839*red+0.586811*green+0.114350*blue;
2055           break;
2056         }
2057         case Rec709LumaPixelIntensityMethod:
2058         default:
2059         {
2060           if (image->colorspace == RGBColorspace)
2061             {
2062               red=EncodePixelGamma(red);
2063               green=EncodePixelGamma(green);
2064               blue=EncodePixelGamma(blue);
2065             }
2066           intensity=0.212656*red+0.715158*green+0.072186*blue;
2067           break;
2068         }
2069         case Rec709LuminancePixelIntensityMethod:
2070         {
2071           if (image->colorspace == sRGBColorspace)
2072             {
2073               red=DecodePixelGamma(red);
2074               green=DecodePixelGamma(green);
2075               blue=DecodePixelGamma(blue);
2076             }
2077           intensity=0.212656*red+0.715158*green+0.072186*blue;
2078           break;
2079         }
2080         case RMSPixelIntensityMethod:
2081         {
2082           intensity=(MagickRealType) (sqrt((double) red*red+green*green+
2083             blue*blue)/sqrt(3.0));
2084           break;
2085         }
2086       }
2087       SetPixelGray(image,ClampToQuantum(intensity),q);
2088       q+=GetPixelChannels(image);
2089     }
2090     if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)))
2091       status=MagickFalse;
2092     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2093       {
2094         MagickBooleanType
2095           proceed;
2096
2097 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2098         #pragma omp critical (MagickCore_GrayscaleImage)
2099 #endif
2100         proceed=SetImageProgress(image,GrayscaleImageTag,progress++,
2101            image->rows);
2102         if( IfMagickFalse(proceed) )
2103           status=MagickFalse;
2104       }
2105   }
2106   image_view=DestroyCacheView(image_view);
2107   image->intensity=method;
2108   image->type=GrayscaleType;
2109   return(SetImageColorspace(image,GRAYColorspace,exception));
2110 }
2111 \f
2112 /*
2113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2114 %                                                                             %
2115 %                                                                             %
2116 %                                                                             %
2117 %     H a l d C l u t I m a g e                                               %
2118 %                                                                             %
2119 %                                                                             %
2120 %                                                                             %
2121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2122 %
2123 %  HaldClutImage() applies a Hald color lookup table to the image.  A Hald
2124 %  color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2125 %  Create it with the HALD coder.  You can apply any color transformation to
2126 %  the Hald image and then use this method to apply the transform to the
2127 %  image.
2128 %
2129 %  The format of the HaldClutImage method is:
2130 %
2131 %      MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
2132 %        ExceptionInfo *exception)
2133 %
2134 %  A description of each parameter follows:
2135 %
2136 %    o image: the image, which is replaced by indexed CLUT values
2137 %
2138 %    o hald_image: the color lookup table image for replacement color values.
2139 %
2140 %    o exception: return any errors or warnings in this structure.
2141 %
2142 */
2143 MagickExport MagickBooleanType HaldClutImage(Image *image,
2144   const Image *hald_image,ExceptionInfo *exception)
2145 {
2146 #define HaldClutImageTag  "Clut/Image"
2147
2148   typedef struct _HaldInfo
2149   {
2150     double
2151       x,
2152       y,
2153       z;
2154   } HaldInfo;
2155
2156   CacheView
2157     *hald_view,
2158     *image_view;
2159
2160   double
2161     width;
2162
2163   MagickBooleanType
2164     status;
2165
2166   MagickOffsetType
2167     progress;
2168
2169   PixelInfo
2170     zero;
2171
2172   size_t
2173     cube_size,
2174     length,
2175     level;
2176
2177   ssize_t
2178     y;
2179
2180   assert(image != (Image *) NULL);
2181   assert(image->signature == MagickCoreSignature);
2182   if (image->debug != MagickFalse)
2183     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2184   assert(hald_image != (Image *) NULL);
2185   assert(hald_image->signature == MagickCoreSignature);
2186   if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
2187     return(MagickFalse);
2188   if (image->alpha_trait == UndefinedPixelTrait)
2189     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2190   /*
2191     Hald clut image.
2192   */
2193   status=MagickTrue;
2194   progress=0;
2195   length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2196     (MagickRealType) hald_image->rows);
2197   for (level=2; (level*level*level) < length; level++) ;
2198   level*=level;
2199   cube_size=level*level;
2200   width=(double) hald_image->columns;
2201   GetPixelInfo(hald_image,&zero);
2202   hald_view=AcquireVirtualCacheView(hald_image,exception);
2203   image_view=AcquireAuthenticCacheView(image,exception);
2204 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2205   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2206     magick_threads(image,image,image->rows,1)
2207 #endif
2208   for (y=0; y < (ssize_t) image->rows; y++)
2209   {
2210     register Quantum
2211       *magick_restrict q;
2212
2213     register ssize_t
2214       x;
2215
2216     if (status == MagickFalse)
2217       continue;
2218     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2219     if (q == (Quantum *) NULL)
2220       {
2221         status=MagickFalse;
2222         continue;
2223       }
2224     for (x=0; x < (ssize_t) image->columns; x++)
2225     {
2226       double
2227         offset;
2228
2229       HaldInfo
2230         point;
2231
2232       PixelInfo
2233         pixel,
2234         pixel1,
2235         pixel2,
2236         pixel3,
2237         pixel4;
2238
2239       point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2240       point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2241       point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2242       offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2243       point.x-=floor(point.x);
2244       point.y-=floor(point.y);
2245       point.z-=floor(point.z);
2246       pixel1=zero;
2247       (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2248         fmod(offset,width),floor(offset/width),&pixel1,exception);
2249       pixel2=zero;
2250       (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2251         fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2252       pixel3=zero;
2253       CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2254         point.y,&pixel3);
2255       offset+=cube_size;
2256       (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2257         fmod(offset,width),floor(offset/width),&pixel1,exception);
2258       (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2259         fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2260       pixel4=zero;
2261       CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2262         point.y,&pixel4);
2263       pixel=zero;
2264       CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2265         point.z,&pixel);
2266       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2267         SetPixelRed(image,ClampToQuantum(pixel.red),q);
2268       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2269         SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2270       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2271         SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2272       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2273           (image->colorspace == CMYKColorspace))
2274         SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2275       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2276           (image->alpha_trait != UndefinedPixelTrait))
2277         SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2278       q+=GetPixelChannels(image);
2279     }
2280     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2281       status=MagickFalse;
2282     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2283       {
2284         MagickBooleanType
2285           proceed;
2286
2287 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2288         #pragma omp critical (MagickCore_HaldClutImage)
2289 #endif
2290         proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2291         if( IfMagickFalse(proceed) )
2292           status=MagickFalse;
2293       }
2294   }
2295   hald_view=DestroyCacheView(hald_view);
2296   image_view=DestroyCacheView(image_view);
2297   return(status);
2298 }
2299 \f
2300 /*
2301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2302 %                                                                             %
2303 %                                                                             %
2304 %                                                                             %
2305 %     L e v e l I m a g e                                                     %
2306 %                                                                             %
2307 %                                                                             %
2308 %                                                                             %
2309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2310 %
2311 %  LevelImage() adjusts the levels of a particular image channel by
2312 %  scaling the colors falling between specified white and black points to
2313 %  the full available quantum range.
2314 %
2315 %  The parameters provided represent the black, and white points.  The black
2316 %  point specifies the darkest color in the image. Colors darker than the
2317 %  black point are set to zero.  White point specifies the lightest color in
2318 %  the image.  Colors brighter than the white point are set to the maximum
2319 %  quantum value.
2320 %
2321 %  If a '!' flag is given, map black and white colors to the given levels
2322 %  rather than mapping those levels to black and white.  See
2323 %  LevelizeImage() below.
2324 %
2325 %  Gamma specifies a gamma correction to apply to the image.
2326 %
2327 %  The format of the LevelImage method is:
2328 %
2329 %      MagickBooleanType LevelImage(Image *image,const double black_point,
2330 %        const double white_point,const double gamma,ExceptionInfo *exception)
2331 %
2332 %  A description of each parameter follows:
2333 %
2334 %    o image: the image.
2335 %
2336 %    o black_point: The level to map zero (black) to.
2337 %
2338 %    o white_point: The level to map QuantumRange (white) to.
2339 %
2340 %    o exception: return any errors or warnings in this structure.
2341 %
2342 */
2343
2344 static inline double LevelPixel(const double black_point,
2345   const double white_point,const double gamma,const double pixel)
2346 {
2347   double
2348     level_pixel,
2349     scale;
2350
2351   scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0;
2352   level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
2353     1.0/gamma);
2354   return(level_pixel);
2355 }
2356
2357 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2358   const double white_point,const double gamma,ExceptionInfo *exception)
2359 {
2360 #define LevelImageTag  "Level/Image"
2361
2362   CacheView
2363     *image_view;
2364
2365   MagickBooleanType
2366     status;
2367
2368   MagickOffsetType
2369     progress;
2370
2371   register ssize_t
2372     i;
2373
2374   ssize_t
2375     y;
2376
2377   /*
2378     Allocate and initialize levels map.
2379   */
2380   assert(image != (Image *) NULL);
2381   assert(image->signature == MagickCoreSignature);
2382   if (image->debug != MagickFalse)
2383     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2384   if (image->storage_class == PseudoClass)
2385     for (i=0; i < (ssize_t) image->colors; i++)
2386     {
2387       /*
2388         Level colormap.
2389       */
2390       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2391         image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2392           white_point,gamma,image->colormap[i].red));
2393       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2394         image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2395           white_point,gamma,image->colormap[i].green));
2396       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2397         image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2398           white_point,gamma,image->colormap[i].blue));
2399       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2400         image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2401           white_point,gamma,image->colormap[i].alpha));
2402       }
2403   /*
2404     Level image.
2405   */
2406   status=MagickTrue;
2407   progress=0;
2408   image_view=AcquireAuthenticCacheView(image,exception);
2409 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2410   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2411     magick_threads(image,image,image->rows,1)
2412 #endif
2413   for (y=0; y < (ssize_t) image->rows; y++)
2414   {
2415     register Quantum
2416       *magick_restrict q;
2417
2418     register ssize_t
2419       x;
2420
2421     if (status == MagickFalse)
2422       continue;
2423     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2424     if (q == (Quantum *) NULL)
2425       {
2426         status=MagickFalse;
2427         continue;
2428       }
2429     for (x=0; x < (ssize_t) image->columns; x++)
2430     {
2431       register ssize_t
2432         j;
2433
2434       if (GetPixelReadMask(image,q) == 0)
2435         {
2436           q+=GetPixelChannels(image);
2437           continue;
2438         }
2439       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2440       {
2441         PixelChannel channel=GetPixelChannelChannel(image,j);
2442         PixelTrait traits=GetPixelChannelTraits(image,channel);
2443         if ((traits & UpdatePixelTrait) == 0)
2444           continue;
2445         q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2446           (double) q[j]));
2447       }
2448       q+=GetPixelChannels(image);
2449     }
2450     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2451       status=MagickFalse;
2452     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2453       {
2454         MagickBooleanType
2455           proceed;
2456
2457 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2458         #pragma omp critical (MagickCore_LevelImage)
2459 #endif
2460         proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2461         if( IfMagickFalse(proceed) )
2462           status=MagickFalse;
2463       }
2464   }
2465   image_view=DestroyCacheView(image_view);
2466   (void) ClampImage(image,exception);
2467   return(status);
2468 }
2469 \f
2470 /*
2471 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2472 %                                                                             %
2473 %                                                                             %
2474 %                                                                             %
2475 %     L e v e l i z e I m a g e                                               %
2476 %                                                                             %
2477 %                                                                             %
2478 %                                                                             %
2479 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2480 %
2481 %  LevelizeImage() applies the reversed LevelImage() operation to just
2482 %  the specific channels specified.  It compresses the full range of color
2483 %  values, so that they lie between the given black and white points. Gamma is
2484 %  applied before the values are mapped.
2485 %
2486 %  LevelizeImage() can be called with by using a +level command line
2487 %  API option, or using a '!' on a -level or LevelImage() geometry string.
2488 %
2489 %  It can be used to de-contrast a greyscale image to the exact levels
2490 %  specified.  Or by using specific levels for each channel of an image you
2491 %  can convert a gray-scale image to any linear color gradient, according to
2492 %  those levels.
2493 %
2494 %  The format of the LevelizeImage method is:
2495 %
2496 %      MagickBooleanType LevelizeImage(Image *image,const double black_point,
2497 %        const double white_point,const double gamma,ExceptionInfo *exception)
2498 %
2499 %  A description of each parameter follows:
2500 %
2501 %    o image: the image.
2502 %
2503 %    o black_point: The level to map zero (black) to.
2504 %
2505 %    o white_point: The level to map QuantumRange (white) to.
2506 %
2507 %    o gamma: adjust gamma by this factor before mapping values.
2508 %
2509 %    o exception: return any errors or warnings in this structure.
2510 %
2511 */
2512 MagickExport MagickBooleanType LevelizeImage(Image *image,
2513   const double black_point,const double white_point,const double gamma,
2514   ExceptionInfo *exception)
2515 {
2516 #define LevelizeImageTag  "Levelize/Image"
2517 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
2518   (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
2519
2520   CacheView
2521     *image_view;
2522
2523   MagickBooleanType
2524     status;
2525
2526   MagickOffsetType
2527     progress;
2528
2529   register ssize_t
2530     i;
2531
2532   ssize_t
2533     y;
2534
2535   /*
2536     Allocate and initialize levels map.
2537   */
2538   assert(image != (Image *) NULL);
2539   assert(image->signature == MagickCoreSignature);
2540   if (image->debug != MagickFalse)
2541     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2542   if (image->storage_class == PseudoClass)
2543     for (i=0; i < (ssize_t) image->colors; i++)
2544     {
2545       /*
2546         Level colormap.
2547       */
2548       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2549         image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
2550       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2551         image->colormap[i].green=(double) LevelizeValue(
2552           image->colormap[i].green);
2553       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2554         image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
2555       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2556         image->colormap[i].alpha=(double) LevelizeValue(
2557           image->colormap[i].alpha);
2558     }
2559   /*
2560     Level image.
2561   */
2562   status=MagickTrue;
2563   progress=0;
2564   image_view=AcquireAuthenticCacheView(image,exception);
2565 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2566   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2567     magick_threads(image,image,image->rows,1)
2568 #endif
2569   for (y=0; y < (ssize_t) image->rows; y++)
2570   {
2571     register Quantum
2572       *magick_restrict q;
2573
2574     register ssize_t
2575       x;
2576
2577     if (status == MagickFalse)
2578       continue;
2579     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2580     if (q == (Quantum *) NULL)
2581       {
2582         status=MagickFalse;
2583         continue;
2584       }
2585     for (x=0; x < (ssize_t) image->columns; x++)
2586     {
2587       register ssize_t
2588         j;
2589
2590       if (GetPixelReadMask(image,q) == 0)
2591         {
2592           q+=GetPixelChannels(image);
2593           continue;
2594         }
2595       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2596       {
2597         PixelChannel channel=GetPixelChannelChannel(image,j);
2598         PixelTrait traits=GetPixelChannelTraits(image,channel);
2599         if ((traits & UpdatePixelTrait) == 0)
2600           continue;
2601         q[j]=LevelizeValue(q[j]);
2602       }
2603       q+=GetPixelChannels(image);
2604     }
2605     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2606       status=MagickFalse;
2607     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2608       {
2609         MagickBooleanType
2610           proceed;
2611
2612 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2613         #pragma omp critical (MagickCore_LevelizeImage)
2614 #endif
2615         proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2616         if( IfMagickFalse(proceed) )
2617           status=MagickFalse;
2618       }
2619   }
2620   image_view=DestroyCacheView(image_view);
2621   return(status);
2622 }
2623 \f
2624 /*
2625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2626 %                                                                             %
2627 %                                                                             %
2628 %                                                                             %
2629 %     L e v e l I m a g e C o l o r s                                         %
2630 %                                                                             %
2631 %                                                                             %
2632 %                                                                             %
2633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2634 %
2635 %  LevelImageColors() maps the given color to "black" and "white" values,
2636 %  linearly spreading out the colors, and level values on a channel by channel
2637 %  bases, as per LevelImage().  The given colors allows you to specify
2638 %  different level ranges for each of the color channels separately.
2639 %
2640 %  If the boolean 'invert' is set true the image values will modifyed in the
2641 %  reverse direction. That is any existing "black" and "white" colors in the
2642 %  image will become the color values given, with all other values compressed
2643 %  appropriatally.  This effectivally maps a greyscale gradient into the given
2644 %  color gradient.
2645 %
2646 %  The format of the LevelImageColors method is:
2647 %
2648 %    MagickBooleanType LevelImageColors(Image *image,
2649 %      const PixelInfo *black_color,const PixelInfo *white_color,
2650 %      const MagickBooleanType invert,ExceptionInfo *exception)
2651 %
2652 %  A description of each parameter follows:
2653 %
2654 %    o image: the image.
2655 %
2656 %    o black_color: The color to map black to/from
2657 %
2658 %    o white_point: The color to map white to/from
2659 %
2660 %    o invert: if true map the colors (levelize), rather than from (level)
2661 %
2662 %    o exception: return any errors or warnings in this structure.
2663 %
2664 */
2665 MagickExport MagickBooleanType LevelImageColors(Image *image,
2666   const PixelInfo *black_color,const PixelInfo *white_color,
2667   const MagickBooleanType invert,ExceptionInfo *exception)
2668 {
2669   ChannelType
2670     channel_mask;
2671
2672   MagickStatusType
2673     status;
2674
2675   /*
2676     Allocate and initialize levels map.
2677   */
2678   assert(image != (Image *) NULL);
2679   assert(image->signature == MagickCoreSignature);
2680   if (image->debug != MagickFalse)
2681     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2682   if( IfMagickTrue(IsGrayColorspace(image->colorspace)) &&
2683       (IfMagickFalse(IsGrayColorspace(black_color->colorspace)) ||
2684        IfMagickFalse(IsGrayColorspace(white_color->colorspace))))
2685     (void) SetImageColorspace(image,sRGBColorspace,exception);
2686   status=MagickTrue;
2687   if( IfMagickFalse(invert) )
2688     {
2689       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2690         {
2691           channel_mask=SetImageChannelMask(image,RedChannel);
2692           status&=LevelImage(image,black_color->red,white_color->red,1.0,
2693             exception);
2694           (void) SetImageChannelMask(image,channel_mask);
2695         }
2696       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2697         {
2698           channel_mask=SetImageChannelMask(image,GreenChannel);
2699           status&=LevelImage(image,black_color->green,white_color->green,1.0,
2700             exception);
2701           (void) SetImageChannelMask(image,channel_mask);
2702         }
2703       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2704         {
2705           channel_mask=SetImageChannelMask(image,BlueChannel);
2706           status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
2707             exception);
2708           (void) SetImageChannelMask(image,channel_mask);
2709         }
2710       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2711           (image->colorspace == CMYKColorspace))
2712         {
2713           channel_mask=SetImageChannelMask(image,BlackChannel);
2714           status&=LevelImage(image,black_color->black,white_color->black,1.0,
2715             exception);
2716           (void) SetImageChannelMask(image,channel_mask);
2717         }
2718       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2719           (image->alpha_trait != UndefinedPixelTrait))
2720         {
2721           channel_mask=SetImageChannelMask(image,AlphaChannel);
2722           status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2723             exception);
2724           (void) SetImageChannelMask(image,channel_mask);
2725         }
2726     }
2727   else
2728     {
2729       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2730         {
2731           channel_mask=SetImageChannelMask(image,RedChannel);
2732           status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
2733             exception);
2734           (void) SetImageChannelMask(image,channel_mask);
2735         }
2736       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2737         {
2738           channel_mask=SetImageChannelMask(image,GreenChannel);
2739           status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
2740             exception);
2741           (void) SetImageChannelMask(image,channel_mask);
2742         }
2743       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2744         {
2745           channel_mask=SetImageChannelMask(image,BlueChannel);
2746           status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2747             exception);
2748           (void) SetImageChannelMask(image,channel_mask);
2749         }
2750       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2751           (image->colorspace == CMYKColorspace))
2752         {
2753           channel_mask=SetImageChannelMask(image,BlackChannel);
2754           status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
2755             exception);
2756           (void) SetImageChannelMask(image,channel_mask);
2757         }
2758       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2759           (image->alpha_trait != UndefinedPixelTrait))
2760         {
2761           channel_mask=SetImageChannelMask(image,AlphaChannel);
2762           status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2763             exception);
2764           (void) SetImageChannelMask(image,channel_mask);
2765         }
2766     }
2767   return(status != 0 ? MagickTrue : MagickFalse);
2768 }
2769 \f
2770 /*
2771 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2772 %                                                                             %
2773 %                                                                             %
2774 %                                                                             %
2775 %     L i n e a r S t r e t c h I m a g e                                     %
2776 %                                                                             %
2777 %                                                                             %
2778 %                                                                             %
2779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2780 %
2781 %  LinearStretchImage() discards any pixels below the black point and above
2782 %  the white point and levels the remaining pixels.
2783 %
2784 %  The format of the LinearStretchImage method is:
2785 %
2786 %      MagickBooleanType LinearStretchImage(Image *image,
2787 %        const double black_point,const double white_point,
2788 %        ExceptionInfo *exception)
2789 %
2790 %  A description of each parameter follows:
2791 %
2792 %    o image: the image.
2793 %
2794 %    o black_point: the black point.
2795 %
2796 %    o white_point: the white point.
2797 %
2798 %    o exception: return any errors or warnings in this structure.
2799 %
2800 */
2801 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2802   const double black_point,const double white_point,ExceptionInfo *exception)
2803 {
2804 #define LinearStretchImageTag  "LinearStretch/Image"
2805
2806   CacheView
2807     *image_view;
2808
2809   double
2810     *histogram,
2811     intensity;
2812
2813   MagickBooleanType
2814     status;
2815
2816   ssize_t
2817     black,
2818     white,
2819     y;
2820
2821   /*
2822     Allocate histogram and linear map.
2823   */
2824   assert(image != (Image *) NULL);
2825   assert(image->signature == MagickCoreSignature);
2826   histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
2827   if (histogram == (double *) NULL)
2828     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2829       image->filename);
2830   /*
2831     Form histogram.
2832   */
2833   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2834   image_view=AcquireVirtualCacheView(image,exception);
2835   for (y=0; y < (ssize_t) image->rows; y++)
2836   {
2837     register const Quantum
2838       *magick_restrict p;
2839
2840     register ssize_t
2841       x;
2842
2843     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2844     if (p == (const Quantum *) NULL)
2845       break;
2846     for (x=0; x < (ssize_t) image->columns; x++)
2847     {
2848       intensity=GetPixelIntensity(image,p);
2849       histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2850       p+=GetPixelChannels(image);
2851     }
2852   }
2853   image_view=DestroyCacheView(image_view);
2854   /*
2855     Find the histogram boundaries by locating the black and white point levels.
2856   */
2857   intensity=0.0;
2858   for (black=0; black < (ssize_t) MaxMap; black++)
2859   {
2860     intensity+=histogram[black];
2861     if (intensity >= black_point)
2862       break;
2863   }
2864   intensity=0.0;
2865   for (white=(ssize_t) MaxMap; white != 0; white--)
2866   {
2867     intensity+=histogram[white];
2868     if (intensity >= white_point)
2869       break;
2870   }
2871   histogram=(double *) RelinquishMagickMemory(histogram);
2872   status=LevelImage(image,(double) ScaleMapToQuantum((MagickRealType) black),
2873     (double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
2874   return(status);
2875 }
2876
2877
2878 /*
2879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2880 %                                                                             %
2881 %                                                                             %
2882 %                                                                             %
2883 %     M o d u l a t e I m a g e                                               %
2884 %                                                                             %
2885 %                                                                             %
2886 %                                                                             %
2887 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2888 %
2889 %  ModulateImage() lets you control the brightness, saturation, and hue
2890 %  of an image.  Modulate represents the brightness, saturation, and hue
2891 %  as one parameter (e.g. 90,150,100).  If the image colorspace is HSL, the
2892 %  modulation is lightness, saturation, and hue.  For HWB, use blackness,
2893 %  whiteness, and hue. And for HCL, use chrome, luma, and hue.
2894 %
2895 %  The format of the ModulateImage method is:
2896 %
2897 %      MagickBooleanType ModulateImage(Image *image,const char *modulate,
2898 %        ExceptionInfo *exception)
2899 %
2900 %  A description of each parameter follows:
2901 %
2902 %    o image: the image.
2903 %
2904 %    o modulate: Define the percent change in brightness, saturation, and hue.
2905 %
2906 %    o exception: return any errors or warnings in this structure.
2907 %
2908 */
2909
2910 static inline void ModulateHCL(const double percent_hue,
2911   const double percent_chroma,const double percent_luma,double *red,
2912   double *green,double *blue)
2913 {
2914   double
2915     hue,
2916     luma,
2917     chroma;
2918
2919   /*
2920     Increase or decrease color luma, chroma, or hue.
2921   */
2922   ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2923   hue+=0.5*(0.01*percent_hue-1.0);
2924   while (hue < 0.0)
2925     hue+=1.0;
2926   while (hue > 1.0)
2927     hue-=1.0;
2928   chroma*=0.01*percent_chroma;
2929   luma*=0.01*percent_luma;
2930   ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2931 }
2932
2933 static inline void ModulateHCLp(const double percent_hue,
2934   const double percent_chroma,const double percent_luma,double *red,
2935   double *green,double *blue)
2936 {
2937   double
2938     hue,
2939     luma,
2940     chroma;
2941
2942   /*
2943     Increase or decrease color luma, chroma, or hue.
2944   */
2945   ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
2946   hue+=0.5*(0.01*percent_hue-1.0);
2947   while (hue < 0.0)
2948     hue+=1.0;
2949   while (hue > 1.0)
2950     hue-=1.0;
2951   chroma*=0.01*percent_chroma;
2952   luma*=0.01*percent_luma;
2953   ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
2954 }
2955
2956 static inline void ModulateHSB(const double percent_hue,
2957   const double percent_saturation,const double percent_brightness,double *red,
2958   double *green,double *blue)
2959 {
2960   double
2961     brightness,
2962     hue,
2963     saturation;
2964
2965   /*
2966     Increase or decrease color brightness, saturation, or hue.
2967   */
2968   ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2969   hue+=0.5*(0.01*percent_hue-1.0);
2970   while (hue < 0.0)
2971     hue+=1.0;
2972   while (hue > 1.0)
2973     hue-=1.0;
2974   saturation*=0.01*percent_saturation;
2975   brightness*=0.01*percent_brightness;
2976   ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2977 }
2978
2979 static inline void ModulateHSI(const double percent_hue,
2980   const double percent_saturation,const double percent_intensity,double *red,
2981   double *green,double *blue)
2982 {
2983   double
2984     intensity,
2985     hue,
2986     saturation;
2987
2988   /*
2989     Increase or decrease color intensity, saturation, or hue.
2990   */
2991   ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
2992   hue+=0.5*(0.01*percent_hue-1.0);
2993   while (hue < 0.0)
2994     hue+=1.0;
2995   while (hue > 1.0)
2996     hue-=1.0;
2997   saturation*=0.01*percent_saturation;
2998   intensity*=0.01*percent_intensity;
2999   ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3000 }
3001
3002 static inline void ModulateHSL(const double percent_hue,
3003   const double percent_saturation,const double percent_lightness,double *red,
3004   double *green,double *blue)
3005 {
3006   double
3007     hue,
3008     lightness,
3009     saturation;
3010
3011   /*
3012     Increase or decrease color lightness, saturation, or hue.
3013   */
3014   ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3015   hue+=0.5*(0.01*percent_hue-1.0);
3016   while (hue < 0.0)
3017     hue+=1.0;
3018   while (hue >= 1.0)
3019     hue-=1.0;
3020   saturation*=0.01*percent_saturation;
3021   lightness*=0.01*percent_lightness;
3022   ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3023 }
3024
3025 static inline void ModulateHSV(const double percent_hue,
3026   const double percent_saturation,const double percent_value,double *red,
3027   double *green,double *blue)
3028 {
3029   double
3030     hue,
3031     saturation,
3032     value;
3033
3034   /*
3035     Increase or decrease color value, saturation, or hue.
3036   */
3037   ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3038   hue+=0.5*(0.01*percent_hue-1.0);
3039   while (hue < 0.0)
3040     hue+=1.0;
3041   while (hue >= 1.0)
3042     hue-=1.0;
3043   saturation*=0.01*percent_saturation;
3044   value*=0.01*percent_value;
3045   ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3046 }
3047
3048 static inline void ModulateHWB(const double percent_hue,
3049   const double percent_whiteness,const double percent_blackness,double *red,
3050   double *green,double *blue)
3051 {
3052   double
3053     blackness,
3054     hue,
3055     whiteness;
3056
3057   /*
3058     Increase or decrease color blackness, whiteness, or hue.
3059   */
3060   ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3061   hue+=0.5*(0.01*percent_hue-1.0);
3062   while (hue < 0.0)
3063     hue+=1.0;
3064   while (hue >= 1.0)
3065     hue-=1.0;
3066   blackness*=0.01*percent_blackness;
3067   whiteness*=0.01*percent_whiteness;
3068   ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3069 }
3070
3071 static inline void ModulateLCHab(const double percent_luma,
3072   const double percent_chroma,const double percent_hue,double *red,
3073   double *green,double *blue)
3074 {
3075   double
3076     hue,
3077     luma,
3078     chroma;
3079
3080   /*
3081     Increase or decrease color luma, chroma, or hue.
3082   */
3083   ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
3084   luma*=0.01*percent_luma;
3085   chroma*=0.01*percent_chroma;
3086   hue+=0.5*(0.01*percent_hue-1.0);
3087   while (hue < 0.0)
3088     hue+=1.0;
3089   while (hue >= 1.0)
3090     hue-=1.0;
3091   ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
3092 }
3093
3094 static inline void ModulateLCHuv(const double percent_luma,
3095   const double percent_chroma,const double percent_hue,double *red,
3096   double *green,double *blue)
3097 {
3098   double
3099     hue,
3100     luma,
3101     chroma;
3102
3103   /*
3104     Increase or decrease color luma, chroma, or hue.
3105   */
3106   ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
3107   luma*=0.01*percent_luma;
3108   chroma*=0.01*percent_chroma;
3109   hue+=0.5*(0.01*percent_hue-1.0);
3110   while (hue < 0.0)
3111     hue+=1.0;
3112   while (hue >= 1.0)
3113     hue-=1.0;
3114   ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
3115 }
3116
3117 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
3118   ExceptionInfo *exception)
3119 {
3120 #define ModulateImageTag  "Modulate/Image"
3121
3122   CacheView
3123     *image_view;
3124
3125   ColorspaceType
3126     colorspace;
3127
3128   const char
3129     *artifact;
3130
3131   double
3132     percent_brightness,
3133     percent_hue,
3134     percent_saturation;
3135
3136   GeometryInfo
3137     geometry_info;
3138
3139   MagickBooleanType
3140     status;
3141
3142   MagickOffsetType
3143     progress;
3144
3145   MagickStatusType
3146     flags;
3147
3148   register ssize_t
3149     i;
3150
3151   ssize_t
3152     y;
3153
3154   /*
3155     Initialize modulate table.
3156   */
3157   assert(image != (Image *) NULL);
3158   assert(image->signature == MagickCoreSignature);
3159   if (image->debug != MagickFalse)
3160     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3161   if (modulate == (char *) NULL)
3162     return(MagickFalse);
3163   if( IfMagickFalse(IssRGBCompatibleColorspace(image->colorspace)) )
3164     (void) SetImageColorspace(image,sRGBColorspace,exception);
3165   flags=ParseGeometry(modulate,&geometry_info);
3166   percent_brightness=geometry_info.rho;
3167   percent_saturation=geometry_info.sigma;
3168   if ((flags & SigmaValue) == 0)
3169     percent_saturation=100.0;
3170   percent_hue=geometry_info.xi;
3171   if ((flags & XiValue) == 0)
3172     percent_hue=100.0;
3173   colorspace=UndefinedColorspace;
3174   artifact=GetImageArtifact(image,"modulate:colorspace");
3175   if (artifact != (const char *) NULL)
3176     colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3177       MagickFalse,artifact);
3178   if (image->storage_class == PseudoClass)
3179     for (i=0; i < (ssize_t) image->colors; i++)
3180     {
3181       double
3182         blue,
3183         green,
3184         red;
3185
3186       /*
3187         Modulate image colormap.
3188       */
3189       red=(double) image->colormap[i].red;
3190       green=(double) image->colormap[i].green;
3191       blue=(double) image->colormap[i].blue;
3192       switch (colorspace)
3193       {
3194         case HCLColorspace:
3195         {
3196           ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3197             &red,&green,&blue);
3198           break;
3199         }
3200         case HCLpColorspace:
3201         {
3202           ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3203             &red,&green,&blue);
3204           break;
3205         }
3206         case HSBColorspace:
3207         {
3208           ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3209             &red,&green,&blue);
3210           break;
3211         }
3212         case HSIColorspace:
3213         {
3214           ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3215             &red,&green,&blue);
3216           break;
3217         }
3218         case HSLColorspace:
3219         default:
3220         {
3221           ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3222             &red,&green,&blue);
3223           break;
3224         }
3225         case HSVColorspace:
3226         {
3227           ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3228             &red,&green,&blue);
3229           break;
3230         }
3231         case HWBColorspace:
3232         {
3233           ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3234             &red,&green,&blue);
3235           break;
3236         }
3237         case LCHColorspace:
3238         case LCHabColorspace:
3239         {
3240           ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3241             &red,&green,&blue);
3242           break;
3243         }
3244         case LCHuvColorspace:
3245         {
3246           ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3247             &red,&green,&blue);
3248           break;
3249         }
3250       }
3251       image->colormap[i].red=red;
3252       image->colormap[i].green=green;
3253       image->colormap[i].blue=blue;
3254     }
3255   /*
3256     Modulate image.
3257   */
3258   status=MagickTrue;
3259   progress=0;
3260   image_view=AcquireAuthenticCacheView(image,exception);
3261 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3262   #pragma omp parallel for schedule(static,4) shared(progress,status) \
3263     magick_threads(image,image,image->rows,1)
3264 #endif
3265   for (y=0; y < (ssize_t) image->rows; y++)
3266   {
3267     register Quantum
3268       *magick_restrict q;
3269
3270     register ssize_t
3271       x;
3272
3273     if (status == MagickFalse)
3274       continue;
3275     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3276     if (q == (Quantum *) NULL)
3277       {
3278         status=MagickFalse;
3279         continue;
3280       }
3281     for (x=0; x < (ssize_t) image->columns; x++)
3282     {
3283       double
3284         blue,
3285         green,
3286         red;
3287
3288       red=(double) GetPixelRed(image,q);
3289       green=(double) GetPixelGreen(image,q);
3290       blue=(double) GetPixelBlue(image,q);
3291       switch (colorspace)
3292       {
3293         case HCLColorspace:
3294         {
3295           ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3296             &red,&green,&blue);
3297           break;
3298         }
3299         case HCLpColorspace:
3300         {
3301           ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3302             &red,&green,&blue);
3303           break;
3304         }
3305         case HSBColorspace:
3306         {
3307           ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3308             &red,&green,&blue);
3309           break;
3310         }
3311         case HSLColorspace:
3312         default:
3313         {
3314           ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3315             &red,&green,&blue);
3316           break;
3317         }
3318         case HSVColorspace:
3319         {
3320           ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3321             &red,&green,&blue);
3322           break;
3323         }
3324         case HWBColorspace:
3325         {
3326           ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3327             &red,&green,&blue);
3328           break;
3329         }
3330         case LCHabColorspace:
3331         {
3332           ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3333             &red,&green,&blue);
3334           break;
3335         }
3336         case LCHColorspace:
3337         case LCHuvColorspace:
3338         {
3339           ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3340             &red,&green,&blue);
3341           break;
3342         }
3343       }
3344       SetPixelRed(image,ClampToQuantum(red),q);
3345       SetPixelGreen(image,ClampToQuantum(green),q);
3346       SetPixelBlue(image,ClampToQuantum(blue),q);
3347       q+=GetPixelChannels(image);
3348     }
3349     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3350       status=MagickFalse;
3351     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3352       {
3353         MagickBooleanType
3354           proceed;
3355
3356 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3357         #pragma omp critical (MagickCore_ModulateImage)
3358 #endif
3359         proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3360         if( IfMagickFalse(proceed) )
3361           status=MagickFalse;
3362       }
3363   }
3364   image_view=DestroyCacheView(image_view);
3365   return(status);
3366 }
3367 \f
3368 /*
3369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3370 %                                                                             %
3371 %                                                                             %
3372 %                                                                             %
3373 %     N e g a t e I m a g e                                                   %
3374 %                                                                             %
3375 %                                                                             %
3376 %                                                                             %
3377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3378 %
3379 %  NegateImage() negates the colors in the reference image.  The grayscale
3380 %  option means that only grayscale values within the image are negated.
3381 %
3382 %  The format of the NegateImage method is:
3383 %
3384 %      MagickBooleanType NegateImage(Image *image,
3385 %        const MagickBooleanType grayscale,ExceptionInfo *exception)
3386 %
3387 %  A description of each parameter follows:
3388 %
3389 %    o image: the image.
3390 %
3391 %    o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3392 %
3393 %    o exception: return any errors or warnings in this structure.
3394 %
3395 */
3396 MagickExport MagickBooleanType NegateImage(Image *image,
3397   const MagickBooleanType grayscale,ExceptionInfo *exception)
3398 {
3399 #define NegateImageTag  "Negate/Image"
3400
3401   CacheView
3402     *image_view;
3403
3404   MagickBooleanType
3405     status;
3406
3407   MagickOffsetType
3408     progress;
3409
3410   register ssize_t
3411     i;
3412
3413   ssize_t
3414     y;
3415
3416   assert(image != (Image *) NULL);
3417   assert(image->signature == MagickCoreSignature);
3418   if (image->debug != MagickFalse)
3419     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3420   if (image->storage_class == PseudoClass)
3421     for (i=0; i < (ssize_t) image->colors; i++)
3422     {
3423       /*
3424         Negate colormap.
3425       */
3426       if( IfMagickTrue(grayscale) )
3427         if ((image->colormap[i].red != image->colormap[i].green) ||
3428             (image->colormap[i].green != image->colormap[i].blue))
3429           continue;
3430       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3431         image->colormap[i].red=QuantumRange-image->colormap[i].red;
3432       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3433         image->colormap[i].green=QuantumRange-image->colormap[i].green;
3434       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3435         image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3436     }
3437   /*
3438     Negate image.
3439   */
3440   status=MagickTrue;
3441   progress=0;
3442   image_view=AcquireAuthenticCacheView(image,exception);
3443   if( IfMagickTrue(grayscale) )
3444     {
3445       for (y=0; y < (ssize_t) image->rows; y++)
3446       {
3447         MagickBooleanType
3448           sync;
3449
3450         register Quantum
3451           *magick_restrict q;
3452
3453         register ssize_t
3454           x;
3455
3456         if (status == MagickFalse)
3457           continue;
3458         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3459           exception);
3460         if (q == (Quantum *) NULL)
3461           {
3462             status=MagickFalse;
3463             continue;
3464           }
3465         for (x=0; x < (ssize_t) image->columns; x++)
3466         {
3467           register ssize_t
3468             j;
3469
3470           if ((GetPixelReadMask(image,q) == 0) ||
3471               IfMagickTrue(IsPixelGray(image,q)))
3472             {
3473               q+=GetPixelChannels(image);
3474               continue;
3475             }
3476           for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3477           {
3478             PixelChannel channel=GetPixelChannelChannel(image,j);
3479             PixelTrait traits=GetPixelChannelTraits(image,channel);
3480             if ((traits & UpdatePixelTrait) == 0)
3481               continue;
3482             q[j]=QuantumRange-q[j];
3483           }
3484           q+=GetPixelChannels(image);
3485         }
3486         sync=SyncCacheViewAuthenticPixels(image_view,exception);
3487         if( IfMagickFalse(sync) )
3488           status=MagickFalse;
3489         if (image->progress_monitor != (MagickProgressMonitor) NULL)
3490           {
3491             MagickBooleanType
3492               proceed;
3493
3494 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3495             #pragma omp critical (MagickCore_NegateImage)
3496 #endif
3497             proceed=SetImageProgress(image,NegateImageTag,progress++,
3498               image->rows);
3499             if( IfMagickFalse(proceed) )
3500               status=MagickFalse;
3501           }
3502       }
3503       image_view=DestroyCacheView(image_view);
3504       return(MagickTrue);
3505     }
3506   /*
3507     Negate image.
3508   */
3509 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3510   #pragma omp parallel for schedule(static,4) shared(progress,status) \
3511     magick_threads(image,image,image->rows,1)
3512 #endif
3513   for (y=0; y < (ssize_t) image->rows; y++)
3514   {
3515     register Quantum
3516       *magick_restrict q;
3517
3518     register ssize_t
3519       x;
3520
3521     if (status == MagickFalse)
3522       continue;
3523     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3524     if (q == (Quantum *) NULL)
3525       {
3526         status=MagickFalse;
3527         continue;
3528       }
3529     for (x=0; x < (ssize_t) image->columns; x++)
3530     {
3531       register ssize_t
3532         j;
3533
3534       if (GetPixelReadMask(image,q) == 0)
3535         {
3536           q+=GetPixelChannels(image);
3537           continue;
3538         }
3539       for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3540       {
3541         PixelChannel channel=GetPixelChannelChannel(image,j);
3542         PixelTrait traits=GetPixelChannelTraits(image,channel);
3543         if ((traits & UpdatePixelTrait) == 0)
3544           continue;
3545         q[j]=QuantumRange-q[j];
3546       }
3547       q+=GetPixelChannels(image);
3548     }
3549     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3550       status=MagickFalse;
3551     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3552       {
3553         MagickBooleanType
3554           proceed;
3555
3556 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3557         #pragma omp critical (MagickCore_NegateImage)
3558 #endif
3559         proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3560         if( IfMagickFalse(proceed) )
3561           status=MagickFalse;
3562       }
3563   }
3564   image_view=DestroyCacheView(image_view);
3565   return(status);
3566 }
3567 \f
3568 /*
3569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3570 %                                                                             %
3571 %                                                                             %
3572 %                                                                             %
3573 %     N o r m a l i z e I m a g e                                             %
3574 %                                                                             %
3575 %                                                                             %
3576 %                                                                             %
3577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3578 %
3579 %  The NormalizeImage() method enhances the contrast of a color image by
3580 %  mapping the darkest 2 percent of all pixel to black and the brightest
3581 %  1 percent to white.
3582 %
3583 %  The format of the NormalizeImage method is:
3584 %
3585 %      MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3586 %
3587 %  A description of each parameter follows:
3588 %
3589 %    o image: the image.
3590 %
3591 %    o exception: return any errors or warnings in this structure.
3592 %
3593 */
3594 MagickExport MagickBooleanType NormalizeImage(Image *image,
3595   ExceptionInfo *exception)
3596 {
3597   double
3598     black_point,
3599     white_point;
3600
3601   black_point=(double) image->columns*image->rows*0.0015;
3602   white_point=(double) image->columns*image->rows*0.9995;
3603   return(ContrastStretchImage(image,black_point,white_point,exception));
3604 }
3605 \f
3606 /*
3607 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3608 %                                                                             %
3609 %                                                                             %
3610 %                                                                             %
3611 %     S i g m o i d a l C o n t r a s t I m a g e                             %
3612 %                                                                             %
3613 %                                                                             %
3614 %                                                                             %
3615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3616 %
3617 %  SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3618 %  sigmoidal contrast algorithm.  Increase the contrast of the image using a
3619 %  sigmoidal transfer function without saturating highlights or shadows.
3620 %  Contrast indicates how much to increase the contrast (0 is none; 3 is
3621 %  typical; 20 is pushing it); mid-point indicates where midtones fall in the
3622 %  resultant image (0 is white; 50% is middle-gray; 100% is black).  Set
3623 %  sharpen to MagickTrue to increase the image contrast otherwise the contrast
3624 %  is reduced.
3625 %
3626 %  The format of the SigmoidalContrastImage method is:
3627 %
3628 %      MagickBooleanType SigmoidalContrastImage(Image *image,
3629 %        const MagickBooleanType sharpen,const char *levels,
3630 %        ExceptionInfo *exception)
3631 %
3632 %  A description of each parameter follows:
3633 %
3634 %    o image: the image.
3635 %
3636 %    o sharpen: Increase or decrease image contrast.
3637 %
3638 %    o contrast: strength of the contrast, the larger the number the more
3639 %      'threshold-like' it becomes.
3640 %
3641 %    o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3642 %
3643 %    o exception: return any errors or warnings in this structure.
3644 %
3645 */
3646
3647 /*
3648   ImageMagick 6 has a version of this function which uses LUTs.
3649 */
3650
3651 /*
3652   Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
3653   constant" set to a.
3654
3655   The first version, based on the hyperbolic tangent tanh, when combined with
3656   the scaling step, is an exact arithmetic clone of the the sigmoid function
3657   based on the logistic curve. The equivalence is based on the identity
3658
3659     1/(1+exp(-t)) = (1+tanh(t/2))/2
3660
3661   (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3662   scaled sigmoidal derivation is invariant under affine transformations of
3663   the ordinate.
3664
3665   The tanh version is almost certainly more accurate and cheaper.  The 0.5
3666   factor in the argument is to clone the legacy ImageMagick behavior. The
3667   reason for making the define depend on atanh even though it only uses tanh
3668   has to do with the construction of the inverse of the scaled sigmoidal.
3669 */
3670 #if defined(MAGICKCORE_HAVE_ATANH)
3671 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3672 #else
3673 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3674 #endif
3675 /*
3676   Scaled sigmoidal function:
3677
3678     ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3679     ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3680
3681   See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
3682   http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf.  The limit
3683   of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
3684   zero. This is fixed below by exiting immediately when contrast is small,
3685   leaving the image (or colormap) unmodified. This appears to be safe because
3686   the series expansion of the logistic sigmoidal function around x=b is
3687
3688   1/2-a*(b-x)/4+...
3689
3690   so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3691 */
3692 #define ScaledSigmoidal(a,b,x) (                    \
3693   (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3694   (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3695 /*
3696   Inverse of ScaledSigmoidal, used for +sigmoidal-contrast.  Because b
3697   may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
3698   sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
3699   when creating a LUT from in gamut values, hence the branching.  In
3700   addition, HDRI may have out of gamut values.
3701   InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
3702   It is only a right inverse. This is unavoidable.
3703 */
3704 static inline double InverseScaledSigmoidal(const double a,const double b,
3705   const double x)
3706 {
3707   const double sig0=Sigmoidal(a,b,0.0);
3708   const double sig1=Sigmoidal(a,b,1.0);
3709   const double argument=(sig1-sig0)*x+sig0;
3710   const double clamped=
3711     (
3712 #if defined(MAGICKCORE_HAVE_ATANH)
3713       argument < -1+MagickEpsilon
3714       ?
3715       -1+MagickEpsilon
3716       :
3717       ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3718     );
3719   return(b+(2.0/a)*atanh(clamped));
3720 #else
3721       argument < MagickEpsilon
3722       ?
3723       MagickEpsilon
3724       :
3725       ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3726     );
3727   return(b-log(1.0/clamped-1.0)/a);
3728 #endif
3729 }
3730
3731 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3732   const MagickBooleanType sharpen,const double contrast,const double midpoint,
3733   ExceptionInfo *exception)
3734 {
3735 #define SigmoidalContrastImageTag  "SigmoidalContrast/Image"
3736 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3737   ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3738 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3739   InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3740
3741   CacheView
3742     *image_view;
3743
3744   MagickBooleanType
3745     status;
3746
3747   MagickOffsetType
3748     progress;
3749
3750   ssize_t
3751     y;
3752
3753   /*
3754     Convenience macros.
3755   */
3756   assert(image != (Image *) NULL);
3757   assert(image->signature == MagickCoreSignature);
3758   if (image->debug != MagickFalse)
3759     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3760   /*
3761     Side effect: may clamp values unless contrast<MagickEpsilon, in which
3762     case nothing is done.
3763   */
3764   if (contrast < MagickEpsilon)
3765     return(MagickTrue);
3766   /*
3767     Sigmoidal-contrast enhance colormap.
3768   */
3769   if (image->storage_class == PseudoClass)
3770     {
3771       register ssize_t
3772         i;
3773
3774       if( IfMagickTrue(sharpen) )
3775         for (i=0; i < (ssize_t) image->colors; i++)
3776         {
3777           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3778             image->colormap[i].red=(MagickRealType) ScaledSig(
3779               image->colormap[i].red);
3780           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3781             image->colormap[i].green=(MagickRealType) ScaledSig(
3782               image->colormap[i].green);
3783           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3784             image->colormap[i].blue=(MagickRealType) ScaledSig(
3785               image->colormap[i].blue);
3786           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3787             image->colormap[i].alpha=(MagickRealType) ScaledSig(
3788               image->colormap[i].alpha);
3789         }
3790       else
3791         for (i=0; i < (ssize_t) image->colors; i++)
3792         {
3793           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3794             image->colormap[i].red=(MagickRealType) InverseScaledSig(
3795               image->colormap[i].red);
3796           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3797             image->colormap[i].green=(MagickRealType) InverseScaledSig(
3798               image->colormap[i].green);
3799           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3800             image->colormap[i].blue=(MagickRealType) InverseScaledSig(
3801               image->colormap[i].blue);
3802           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3803             image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
3804               image->colormap[i].alpha);
3805         }
3806     }
3807   /*
3808     Sigmoidal-contrast enhance image.
3809   */
3810   status=MagickTrue;
3811   progress=0;
3812   image_view=AcquireAuthenticCacheView(image,exception);
3813 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3814   #pragma omp parallel for schedule(static,4) shared(progress,status) \
3815     magick_threads(image,image,image->rows,1)
3816 #endif
3817   for (y=0; y < (ssize_t) image->rows; y++)
3818   {
3819     register Quantum
3820       *magick_restrict q;
3821
3822     register ssize_t
3823       x;
3824
3825     if (status == MagickFalse)
3826       continue;
3827     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3828     if (q == (Quantum *) NULL)
3829       {
3830         status=MagickFalse;
3831         continue;
3832       }
3833     for (x=0; x < (ssize_t) image->columns; x++)
3834     {
3835       register ssize_t
3836         i;
3837
3838       if (GetPixelReadMask(image,q) == 0)
3839         {
3840           q+=GetPixelChannels(image);
3841           continue;
3842         }
3843       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3844       {
3845         PixelChannel channel=GetPixelChannelChannel(image,i);
3846         PixelTrait traits=GetPixelChannelTraits(image,channel);
3847         if ((traits & UpdatePixelTrait) == 0)
3848           continue;
3849         if( IfMagickTrue(sharpen) )
3850           q[i]=ScaledSig(q[i]);
3851         else
3852           q[i]=InverseScaledSig(q[i]);
3853       }
3854       q+=GetPixelChannels(image);
3855     }
3856     if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3857       status=MagickFalse;
3858     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3859       {
3860         MagickBooleanType
3861           proceed;
3862
3863 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3864         #pragma omp critical (MagickCore_SigmoidalContrastImage)
3865 #endif
3866         proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3867           image->rows);
3868         if( IfMagickFalse(proceed) )
3869           status=MagickFalse;
3870       }
3871   }
3872   image_view=DestroyCacheView(image_view);
3873   return(status);
3874 }