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