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