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