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