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