]> 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         if (clut_traits == UndefinedPixelTrait)
387           continue;
388         channel=GetPixelChannelMapChannel(clut_image,(PixelChannel) i);
389         traits=GetPixelChannelMapTraits(clut_image,channel);
390         if ((traits & UpdatePixelTrait) != 0)
391           q[channel]=ClampToQuantum(clut_map[ScaleQuantumToMap(q[channel])*
392             GetPixelChannels(clut_image)+channel]);
393       }
394       q+=GetPixelChannels(image);
395     }
396     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
397       status=MagickFalse;
398     if (image->progress_monitor != (MagickProgressMonitor) NULL)
399       {
400         MagickBooleanType
401           proceed;
402
403 #if defined(MAGICKCORE_OPENMP_SUPPORT)
404   #pragma omp critical (MagickCore_ClutImage)
405 #endif
406         proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
407         if (proceed == MagickFalse)
408           status=MagickFalse;
409       }
410   }
411   image_view=DestroyCacheView(image_view);
412   clut_map=(double *) RelinquishMagickMemory(clut_map);
413   if ((clut_image->matte != MagickFalse) &&
414       ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
415     (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
416   return(status);
417 }
418 \f
419 /*
420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421 %                                                                             %
422 %                                                                             %
423 %                                                                             %
424 %     C o l o r D e c i s i o n L i s t I m a g e                             %
425 %                                                                             %
426 %                                                                             %
427 %                                                                             %
428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429 %
430 %  ColorDecisionListImage() accepts a lightweight Color Correction Collection
431 %  (CCC) file which solely contains one or more color corrections and applies
432 %  the correction to the image.  Here is a sample CCC file:
433 %
434 %    <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
435 %          <ColorCorrection id="cc03345">
436 %                <SOPNode>
437 %                     <Slope> 0.9 1.2 0.5 </Slope>
438 %                     <Offset> 0.4 -0.5 0.6 </Offset>
439 %                     <Power> 1.0 0.8 1.5 </Power>
440 %                </SOPNode>
441 %                <SATNode>
442 %                     <Saturation> 0.85 </Saturation>
443 %                </SATNode>
444 %          </ColorCorrection>
445 %    </ColorCorrectionCollection>
446 %
447 %  which includes the slop, offset, and power for each of the RGB channels
448 %  as well as the saturation.
449 %
450 %  The format of the ColorDecisionListImage method is:
451 %
452 %      MagickBooleanType ColorDecisionListImage(Image *image,
453 %        const char *color_correction_collection,ExceptionInfo *exception)
454 %
455 %  A description of each parameter follows:
456 %
457 %    o image: the image.
458 %
459 %    o color_correction_collection: the color correction collection in XML.
460 %
461 %    o exception: return any errors or warnings in this structure.
462 %
463 */
464 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
465   const char *color_correction_collection,ExceptionInfo *exception)
466 {
467 #define ColorDecisionListCorrectImageTag  "ColorDecisionList/Image"
468
469   typedef struct _Correction
470   {
471     double
472       slope,
473       offset,
474       power;
475   } Correction;
476
477   typedef struct _ColorCorrection
478   {
479     Correction
480       red,
481       green,
482       blue;
483
484     double
485       saturation;
486   } ColorCorrection;
487
488   CacheView
489     *image_view;
490
491   char
492     token[MaxTextExtent];
493
494   ColorCorrection
495     color_correction;
496
497   const char
498     *content,
499     *p;
500
501   MagickBooleanType
502     status;
503
504   MagickOffsetType
505     progress;
506
507   PixelPacket
508     *cdl_map;
509
510   register ssize_t
511     i;
512
513   ssize_t
514     y;
515
516   XMLTreeInfo
517     *cc,
518     *ccc,
519     *sat,
520     *sop;
521
522   /*
523     Allocate and initialize cdl maps.
524   */
525   assert(image != (Image *) NULL);
526   assert(image->signature == MagickSignature);
527   if (image->debug != MagickFalse)
528     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
529   if (color_correction_collection == (const char *) NULL)
530     return(MagickFalse);
531   ccc=NewXMLTree((const char *) color_correction_collection,exception);
532   if (ccc == (XMLTreeInfo *) NULL)
533     return(MagickFalse);
534   cc=GetXMLTreeChild(ccc,"ColorCorrection");
535   if (cc == (XMLTreeInfo *) NULL)
536     {
537       ccc=DestroyXMLTree(ccc);
538       return(MagickFalse);
539     }
540   color_correction.red.slope=1.0;
541   color_correction.red.offset=0.0;
542   color_correction.red.power=1.0;
543   color_correction.green.slope=1.0;
544   color_correction.green.offset=0.0;
545   color_correction.green.power=1.0;
546   color_correction.blue.slope=1.0;
547   color_correction.blue.offset=0.0;
548   color_correction.blue.power=1.0;
549   color_correction.saturation=0.0;
550   sop=GetXMLTreeChild(cc,"SOPNode");
551   if (sop != (XMLTreeInfo *) NULL)
552     {
553       XMLTreeInfo
554         *offset,
555         *power,
556         *slope;
557
558       slope=GetXMLTreeChild(sop,"Slope");
559       if (slope != (XMLTreeInfo *) NULL)
560         {
561           content=GetXMLTreeContent(slope);
562           p=(const char *) content;
563           for (i=0; (*p != '\0') && (i < 3); i++)
564           {
565             GetMagickToken(p,&p,token);
566             if (*token == ',')
567               GetMagickToken(p,&p,token);
568             switch (i)
569             {
570               case 0:
571               {
572                 color_correction.red.slope=InterpretLocaleValue(token,
573                   (char **) NULL);
574                 break;
575               }
576               case 1:
577               {
578                 color_correction.green.slope=InterpretLocaleValue(token,
579                   (char **) NULL);
580                 break;
581               }
582               case 2:
583               {
584                 color_correction.blue.slope=InterpretLocaleValue(token,
585                   (char **) NULL);
586                 break;
587               }
588             }
589           }
590         }
591       offset=GetXMLTreeChild(sop,"Offset");
592       if (offset != (XMLTreeInfo *) NULL)
593         {
594           content=GetXMLTreeContent(offset);
595           p=(const char *) content;
596           for (i=0; (*p != '\0') && (i < 3); i++)
597           {
598             GetMagickToken(p,&p,token);
599             if (*token == ',')
600               GetMagickToken(p,&p,token);
601             switch (i)
602             {
603               case 0:
604               {
605                 color_correction.red.offset=InterpretLocaleValue(token,
606                   (char **) NULL);
607                 break;
608               }
609               case 1:
610               {
611                 color_correction.green.offset=InterpretLocaleValue(token,
612                   (char **) NULL);
613                 break;
614               }
615               case 2:
616               {
617                 color_correction.blue.offset=InterpretLocaleValue(token,
618                   (char **) NULL);
619                 break;
620               }
621             }
622           }
623         }
624       power=GetXMLTreeChild(sop,"Power");
625       if (power != (XMLTreeInfo *) NULL)
626         {
627           content=GetXMLTreeContent(power);
628           p=(const char *) content;
629           for (i=0; (*p != '\0') && (i < 3); i++)
630           {
631             GetMagickToken(p,&p,token);
632             if (*token == ',')
633               GetMagickToken(p,&p,token);
634             switch (i)
635             {
636               case 0:
637               {
638                 color_correction.red.power=InterpretLocaleValue(token,
639                   (char **) NULL);
640                 break;
641               }
642               case 1:
643               {
644                 color_correction.green.power=InterpretLocaleValue(token,
645                   (char **) NULL);
646                 break;
647               }
648               case 2:
649               {
650                 color_correction.blue.power=InterpretLocaleValue(token,
651                   (char **) NULL);
652                 break;
653               }
654             }
655           }
656         }
657     }
658   sat=GetXMLTreeChild(cc,"SATNode");
659   if (sat != (XMLTreeInfo *) NULL)
660     {
661       XMLTreeInfo
662         *saturation;
663
664       saturation=GetXMLTreeChild(sat,"Saturation");
665       if (saturation != (XMLTreeInfo *) NULL)
666         {
667           content=GetXMLTreeContent(saturation);
668           p=(const char *) content;
669           GetMagickToken(p,&p,token);
670           color_correction.saturation=InterpretLocaleValue(token,
671             (char **) NULL);
672         }
673     }
674   ccc=DestroyXMLTree(ccc);
675   if (image->debug != MagickFalse)
676     {
677       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
678         "  Color Correction Collection:");
679       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
680         "  color_correction.red.slope: %g",color_correction.red.slope);
681       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
682         "  color_correction.red.offset: %g",color_correction.red.offset);
683       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
684         "  color_correction.red.power: %g",color_correction.red.power);
685       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
686         "  color_correction.green.slope: %g",color_correction.green.slope);
687       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
688         "  color_correction.green.offset: %g",color_correction.green.offset);
689       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
690         "  color_correction.green.power: %g",color_correction.green.power);
691       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
692         "  color_correction.blue.slope: %g",color_correction.blue.slope);
693       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
694         "  color_correction.blue.offset: %g",color_correction.blue.offset);
695       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
696         "  color_correction.blue.power: %g",color_correction.blue.power);
697       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
698         "  color_correction.saturation: %g",color_correction.saturation);
699     }
700   cdl_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
701   if (cdl_map == (PixelPacket *) NULL)
702     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
703       image->filename);
704 #if defined(MAGICKCORE_OPENMP_SUPPORT)
705   #pragma omp parallel for schedule(dynamic,4)
706 #endif
707   for (i=0; i <= (ssize_t) MaxMap; i++)
708   {
709     cdl_map[i].red=ClampToQuantum((MagickRealType) ScaleMapToQuantum(
710       (MagickRealType) (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
711       color_correction.red.offset,color_correction.red.power)))));
712     cdl_map[i].green=ClampToQuantum((MagickRealType) ScaleMapToQuantum(
713       (MagickRealType) (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
714       color_correction.green.offset,color_correction.green.power)))));
715     cdl_map[i].blue=ClampToQuantum((MagickRealType) ScaleMapToQuantum(
716       (MagickRealType) (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
717       color_correction.blue.offset,color_correction.blue.power)))));
718   }
719   if (image->storage_class == PseudoClass)
720     {
721       /*
722         Apply transfer function to colormap.
723       */
724 #if defined(MAGICKCORE_OPENMP_SUPPORT)
725   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
726 #endif
727       for (i=0; i < (ssize_t) image->colors; i++)
728       {
729         double
730           luma;
731
732         luma=0.2126*image->colormap[i].red+0.7152*image->colormap[i].green+
733           0.0722*image->colormap[i].blue;
734         image->colormap[i].red=ClampToQuantum(luma+
735           color_correction.saturation*cdl_map[ScaleQuantumToMap(
736           image->colormap[i].red)].red-luma);
737         image->colormap[i].green=ClampToQuantum(luma+
738           color_correction.saturation*cdl_map[ScaleQuantumToMap(
739           image->colormap[i].green)].green-luma);
740         image->colormap[i].blue=ClampToQuantum(luma+
741           color_correction.saturation*cdl_map[ScaleQuantumToMap(
742           image->colormap[i].blue)].blue-luma);
743       }
744     }
745   /*
746     Apply transfer function to image.
747   */
748   status=MagickTrue;
749   progress=0;
750   image_view=AcquireCacheView(image);
751 #if defined(MAGICKCORE_OPENMP_SUPPORT)
752   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
753 #endif
754   for (y=0; y < (ssize_t) image->rows; y++)
755   {
756     double
757       luma;
758
759     register Quantum
760       *restrict q;
761
762     register ssize_t
763       x;
764
765     if (status == MagickFalse)
766       continue;
767     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
768     if (q == (const Quantum *) NULL)
769       {
770         status=MagickFalse;
771         continue;
772       }
773     for (x=0; x < (ssize_t) image->columns; x++)
774     {
775       luma=0.2126*GetPixelRed(image,q)+0.7152*GetPixelGreen(image,q)+0.0722*
776         GetPixelBlue(image,q);
777       SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
778         (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
779       SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
780         (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
781       SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
782         (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
783       q+=GetPixelChannels(image);
784     }
785     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
786       status=MagickFalse;
787     if (image->progress_monitor != (MagickProgressMonitor) NULL)
788       {
789         MagickBooleanType
790           proceed;
791
792 #if defined(MAGICKCORE_OPENMP_SUPPORT)
793   #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
794 #endif
795         proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
796           progress++,image->rows);
797         if (proceed == MagickFalse)
798           status=MagickFalse;
799       }
800   }
801   image_view=DestroyCacheView(image_view);
802   cdl_map=(PixelPacket *) RelinquishMagickMemory(cdl_map);
803   return(status);
804 }
805 \f
806 /*
807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808 %                                                                             %
809 %                                                                             %
810 %                                                                             %
811 %     C o n t r a s t I m a g e                                               %
812 %                                                                             %
813 %                                                                             %
814 %                                                                             %
815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816 %
817 %  ContrastImage() enhances the intensity differences between the lighter and
818 %  darker elements of the image.  Set sharpen to a MagickTrue to increase the
819 %  image contrast otherwise the contrast is reduced.
820 %
821 %  The format of the ContrastImage method is:
822 %
823 %      MagickBooleanType ContrastImage(Image *image,
824 %        const MagickBooleanType sharpen,ExceptionInfo *exception)
825 %
826 %  A description of each parameter follows:
827 %
828 %    o image: the image.
829 %
830 %    o sharpen: Increase or decrease image contrast.
831 %
832 %    o exception: return any errors or warnings in this structure.
833 %
834 */
835
836 static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
837 {
838   double
839     brightness,
840     hue,
841     saturation;
842
843   /*
844     Enhance contrast: dark color become darker, light color become lighter.
845   */
846   assert(red != (Quantum *) NULL);
847   assert(green != (Quantum *) NULL);
848   assert(blue != (Quantum *) NULL);
849   hue=0.0;
850   saturation=0.0;
851   brightness=0.0;
852   ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
853   brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
854     brightness);
855   if (brightness > 1.0)
856     brightness=1.0;
857   else
858     if (brightness < 0.0)
859       brightness=0.0;
860   ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
861 }
862
863 MagickExport MagickBooleanType ContrastImage(Image *image,
864   const MagickBooleanType sharpen,ExceptionInfo *exception)
865 {
866 #define ContrastImageTag  "Contrast/Image"
867
868   CacheView
869     *image_view;
870
871   int
872     sign;
873
874   MagickBooleanType
875     status;
876
877   MagickOffsetType
878     progress;
879
880   register ssize_t
881     i;
882
883   ssize_t
884     y;
885
886   assert(image != (Image *) NULL);
887   assert(image->signature == MagickSignature);
888   if (image->debug != MagickFalse)
889     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
890   sign=sharpen != MagickFalse ? 1 : -1;
891   if (image->storage_class == PseudoClass)
892     {
893       /*
894         Contrast enhance colormap.
895       */
896       for (i=0; i < (ssize_t) image->colors; i++)
897         Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
898           &image->colormap[i].blue);
899     }
900   /*
901     Contrast enhance image.
902   */
903   status=MagickTrue;
904   progress=0;
905   image_view=AcquireCacheView(image);
906 #if defined(MAGICKCORE_OPENMP_SUPPORT)
907   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
908 #endif
909   for (y=0; y < (ssize_t) image->rows; y++)
910   {
911     Quantum
912       blue,
913       green,
914       red;
915
916     register Quantum
917       *restrict q;
918
919     register ssize_t
920       x;
921
922     if (status == MagickFalse)
923       continue;
924     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
925     if (q == (const Quantum *) NULL)
926       {
927         status=MagickFalse;
928         continue;
929       }
930     for (x=0; x < (ssize_t) image->columns; x++)
931     {
932       red=GetPixelRed(image,q);
933       green=GetPixelGreen(image,q);
934       blue=GetPixelBlue(image,q);
935       Contrast(sign,&red,&green,&blue);
936       SetPixelRed(image,red,q);
937       SetPixelGreen(image,green,q);
938       SetPixelBlue(image,blue,q);
939       q+=GetPixelChannels(image);
940     }
941     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
942       status=MagickFalse;
943     if (image->progress_monitor != (MagickProgressMonitor) NULL)
944       {
945         MagickBooleanType
946           proceed;
947
948 #if defined(MAGICKCORE_OPENMP_SUPPORT)
949   #pragma omp critical (MagickCore_ContrastImage)
950 #endif
951         proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
952         if (proceed == MagickFalse)
953           status=MagickFalse;
954       }
955   }
956   image_view=DestroyCacheView(image_view);
957   return(status);
958 }
959 \f
960 /*
961 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
962 %                                                                             %
963 %                                                                             %
964 %                                                                             %
965 %     C o n t r a s t S t r e t c h I m a g e                                 %
966 %                                                                             %
967 %                                                                             %
968 %                                                                             %
969 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
970 %
971 %  ContrastStretchImage() is a simple image enhancement technique that attempts
972 %  to improve the contrast in an image by `stretching' the range of intensity
973 %  values it contains to span a desired range of values. It differs from the
974 %  more sophisticated histogram equalization in that it can only apply a
975 %  linear scaling function to the image pixel values.  As a result the
976 %  `enhancement' is less harsh.
977 %
978 %  The format of the ContrastStretchImage method is:
979 %
980 %      MagickBooleanType ContrastStretchImage(Image *image,
981 %        const char *levels,ExceptionInfo *exception)
982 %
983 %  A description of each parameter follows:
984 %
985 %    o image: the image.
986 %
987 %    o black_point: the black point.
988 %
989 %    o white_point: the white point.
990 %
991 %    o levels: Specify the levels where the black and white points have the
992 %      range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
993 %
994 %    o exception: return any errors or warnings in this structure.
995 %
996 */
997 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
998   const double black_point,const double white_point,ExceptionInfo *exception)
999 {
1000 #define MaxRange(color)  ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
1001 #define ContrastStretchImageTag  "ContrastStretch/Image"
1002
1003   CacheView
1004     *image_view;
1005
1006   MagickBooleanType
1007     status;
1008
1009   MagickOffsetType
1010     progress;
1011
1012   double
1013     *black,
1014     *histogram,
1015     *stretch_map,
1016     *white;
1017
1018   register ssize_t
1019     i;
1020
1021   ssize_t
1022     y;
1023
1024   /*
1025     Allocate histogram and stretch map.
1026   */
1027   assert(image != (Image *) NULL);
1028   assert(image->signature == MagickSignature);
1029   if (image->debug != MagickFalse)
1030     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1031   black=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*black));
1032   white=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*white));
1033   histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,
1034     GetPixelChannels(image)*sizeof(*histogram));
1035   stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1036     GetPixelChannels(image)*sizeof(*stretch_map));
1037   if ((black == (double *) NULL) || (white == (double *) NULL) ||
1038       (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1039     {
1040       if (stretch_map != (double *) NULL)
1041         stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1042       if (histogram != (double *) NULL)
1043         histogram=(double *) RelinquishMagickMemory(histogram);
1044       if (white != (double *) NULL)
1045         white=(double *) RelinquishMagickMemory(white);
1046       if (black != (double *) NULL)
1047         black=(double *) RelinquishMagickMemory(black);
1048       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1049         image->filename);
1050     }
1051   /*
1052     Form histogram.
1053   */
1054   status=MagickTrue;
1055   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1056     sizeof(*histogram));
1057   image_view=AcquireCacheView(image);
1058   for (y=0; y < (ssize_t) image->rows; y++)
1059   {
1060     register const Quantum
1061       *restrict p;
1062
1063     register ssize_t
1064       x;
1065
1066     if (status == MagickFalse)
1067       continue;
1068     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1069     if (p == (const Quantum *) NULL)
1070       {
1071         status=MagickFalse;
1072         continue;
1073       }
1074     for (x=0; x < (ssize_t) image->columns; x++)
1075     {
1076       register ssize_t
1077         i;
1078
1079       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1080         histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++;
1081       p+=GetPixelChannels(image);
1082     }
1083   }
1084   /*
1085     Find the histogram boundaries by locating the black/white levels.
1086   */
1087 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1088   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1089 #endif
1090   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1091   {
1092     double
1093       intensity;
1094
1095     register ssize_t
1096       j;
1097
1098     black[i]=0.0;
1099     white[i]=MaxRange(QuantumRange);
1100     intensity=0.0;
1101     for (j=0; j <= (ssize_t) MaxMap; j++)
1102     {
1103       intensity+=histogram[GetPixelChannels(image)*j+i];
1104       if (intensity > black_point)
1105         break;
1106     }
1107     black[i]=(MagickRealType) j;
1108     intensity=0.0;
1109     for (j=(ssize_t) MaxMap; j != 0; j--)
1110     {
1111       intensity+=histogram[GetPixelChannels(image)*j+i];
1112       if (intensity > ((double) image->columns*image->rows-white_point))
1113         break;
1114     }
1115     white[i]=(MagickRealType) j;
1116   }
1117   histogram=(double *) RelinquishMagickMemory(histogram);
1118   /*
1119     Stretch the histogram to create the stretched image mapping.
1120   */
1121   (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1122     sizeof(*stretch_map));
1123 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1124   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1125 #endif
1126   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1127   {
1128     register ssize_t
1129       j;
1130
1131     for (j=0; j <= (ssize_t) MaxMap; j++)
1132     {
1133       if (j < (ssize_t) black[i])
1134         stretch_map[GetPixelChannels(image)*j+i]=0.0;
1135       else
1136         if (j > (ssize_t) white[i])
1137           stretch_map[GetPixelChannels(image)*j+i]=(MagickRealType)
1138             QuantumRange;
1139         else
1140           if (black[i] != white[i])
1141             stretch_map[GetPixelChannels(image)*j+i]=(MagickRealType)
1142               ScaleMapToQuantum((MagickRealType) (MaxMap*(j-black[i])/
1143               (white[i]-black[i])));
1144     }
1145   }
1146   if (image->storage_class == PseudoClass)
1147     {
1148       register ssize_t
1149         j;
1150
1151       /*
1152         Stretch-contrast colormap.
1153       */
1154 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1155   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1156 #endif
1157       for (j=0; j < (ssize_t) image->colors; j++)
1158       {
1159         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1160           {
1161             i=GetPixelChannelMapChannel(image,RedPixelChannel);
1162             if (black[i] != white[i])
1163               image->colormap[i].red=ClampToQuantum(stretch_map[
1164                 GetPixelChannels(image)*ScaleQuantumToMap(
1165                 image->colormap[i].red)]+i);
1166           }
1167         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1168           {
1169             i=GetPixelChannelMapChannel(image,GreenPixelChannel);
1170             if (black[i] != white[i])
1171               image->colormap[i].red=ClampToQuantum(stretch_map[
1172                 GetPixelChannels(image)*ScaleQuantumToMap(
1173                 image->colormap[i].red)]+i);
1174           }
1175         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1176           {
1177             i=GetPixelChannelMapChannel(image,BluePixelChannel);
1178             if (black[i] != white[i])
1179               image->colormap[i].red=ClampToQuantum(stretch_map[
1180                 GetPixelChannels(image)*ScaleQuantumToMap(
1181                 image->colormap[i].red)]+i);
1182           }
1183         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1184           {
1185             i=GetPixelChannelMapChannel(image,AlphaPixelChannel);
1186             if (black[i] != white[i])
1187               image->colormap[i].red=ClampToQuantum(stretch_map[
1188                 GetPixelChannels(image)*ScaleQuantumToMap(
1189                 image->colormap[i].red)]+i);
1190           }
1191       }
1192     }
1193   /*
1194     Stretch-contrast image.
1195   */
1196   status=MagickTrue;
1197   progress=0;
1198 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1199   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1200 #endif
1201   for (y=0; y < (ssize_t) image->rows; y++)
1202   {
1203     register Quantum
1204       *restrict q;
1205
1206     register ssize_t
1207       x;
1208
1209     if (status == MagickFalse)
1210       continue;
1211     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1212     if (q == (const Quantum *) NULL)
1213       {
1214         status=MagickFalse;
1215         continue;
1216       }
1217     for (x=0; x < (ssize_t) image->columns; x++)
1218     {
1219       register ssize_t
1220         i;
1221
1222       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1223       {
1224         PixelTrait
1225           traits;
1226
1227         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1228         if (((traits & UpdatePixelTrait) != 0) && (black[i] != white[i]))
1229           q[i]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1230             ScaleQuantumToMap(q[i])+i]);
1231       }
1232       q+=GetPixelChannels(image);
1233     }
1234     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1235       status=MagickFalse;
1236     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1237       {
1238         MagickBooleanType
1239           proceed;
1240
1241 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1242   #pragma omp critical (MagickCore_ContrastStretchImage)
1243 #endif
1244         proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1245           image->rows);
1246         if (proceed == MagickFalse)
1247           status=MagickFalse;
1248       }
1249   }
1250   image_view=DestroyCacheView(image_view);
1251   stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1252   white=(double *) RelinquishMagickMemory(white);
1253   black=(double *) RelinquishMagickMemory(black);
1254   return(status);
1255 }
1256 \f
1257 /*
1258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1259 %                                                                             %
1260 %                                                                             %
1261 %                                                                             %
1262 %     E n h a n c e I m a g e                                                 %
1263 %                                                                             %
1264 %                                                                             %
1265 %                                                                             %
1266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1267 %
1268 %  EnhanceImage() applies a digital filter that improves the quality of a
1269 %  noisy image.
1270 %
1271 %  The format of the EnhanceImage method is:
1272 %
1273 %      Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1274 %
1275 %  A description of each parameter follows:
1276 %
1277 %    o image: the image.
1278 %
1279 %    o exception: return any errors or warnings in this structure.
1280 %
1281 */
1282 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1283 {
1284 #define EnhancePixel(weight) \
1285   mean=((MagickRealType) r[i]+q[channel])/2.0; \
1286   distance=(MagickRealType) r[i]-(MagickRealType) q[channel]; \
1287   distance_squared=QuantumScale*(2.0*((MagickRealType) QuantumRange+1.0)+ \
1288     mean)*distance*distance; \
1289   if (distance_squared < ((MagickRealType) QuantumRange*(MagickRealType) \
1290       QuantumRange/25.0f)) \
1291     { \
1292       aggregate+=(weight)*r[i]; \
1293       total_weight+=(weight); \
1294     } \
1295   r+=GetPixelChannels(image);
1296 #define EnhanceImageTag  "Enhance/Image"
1297
1298   CacheView
1299     *enhance_view,
1300     *image_view;
1301
1302   Image
1303     *enhance_image;
1304
1305   MagickBooleanType
1306     status;
1307
1308   MagickOffsetType
1309     progress;
1310
1311   ssize_t
1312     y;
1313
1314   /*
1315     Initialize enhanced image attributes.
1316   */
1317   assert(image != (const Image *) NULL);
1318   assert(image->signature == MagickSignature);
1319   if (image->debug != MagickFalse)
1320     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1321   assert(exception != (ExceptionInfo *) NULL);
1322   assert(exception->signature == MagickSignature);
1323   enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1324     exception);
1325   if (enhance_image == (Image *) NULL)
1326     return((Image *) NULL);
1327   if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1328     {
1329       enhance_image=DestroyImage(enhance_image);
1330       return((Image *) NULL);
1331     }
1332   /*
1333     Enhance image.
1334   */
1335   status=MagickTrue;
1336   progress=0;
1337   image_view=AcquireCacheView(image);
1338   enhance_view=AcquireCacheView(enhance_image);
1339 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1340   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1341 #endif
1342   for (y=0; y < (ssize_t) image->rows; y++)
1343   {
1344     register const Quantum
1345       *restrict p;
1346
1347     register Quantum
1348       *restrict q;
1349
1350     register ssize_t
1351       x;
1352
1353     ssize_t
1354       center;
1355
1356     if (status == MagickFalse)
1357       continue;
1358     p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1359     q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1360       exception);
1361     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1362       {
1363         status=MagickFalse;
1364         continue;
1365       }
1366     center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1367     for (x=0; x < (ssize_t) image->columns; x++)
1368     {
1369       register ssize_t
1370         i;
1371
1372       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1373       {
1374         MagickRealType
1375           aggregate,
1376           distance,
1377           distance_squared,
1378           mean,
1379           total_weight;
1380
1381         PixelChannel
1382           channel;
1383
1384         PixelTrait
1385           enhance_traits,
1386           traits;
1387
1388         register const Quantum
1389           *restrict r;
1390
1391         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1392         if (traits == UndefinedPixelTrait)
1393           continue;
1394         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1395         enhance_traits=GetPixelChannelMapTraits(enhance_image,channel);
1396         if (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 == (const 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 == (const 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 == (const 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 == (const 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           continue;
2200         if ((traits & UpdatePixelTrait) == 0)
2201           continue;
2202         q[i]=LevelQuantum(q[i]);
2203       }
2204       q+=GetPixelChannels(image);
2205     }
2206     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2207       status=MagickFalse;
2208     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2209       {
2210         MagickBooleanType
2211           proceed;
2212
2213 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2214   #pragma omp critical (MagickCore_LevelImage)
2215 #endif
2216         proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2217         if (proceed == MagickFalse)
2218           status=MagickFalse;
2219       }
2220   }
2221   image_view=DestroyCacheView(image_view);
2222   return(status);
2223 }
2224 \f
2225 /*
2226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2227 %                                                                             %
2228 %                                                                             %
2229 %                                                                             %
2230 %     L e v e l i z e I m a g e                                               %
2231 %                                                                             %
2232 %                                                                             %
2233 %                                                                             %
2234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2235 %
2236 %  LevelizeImage() applies the reversed LevelImage() operation to just
2237 %  the specific channels specified.  It compresses the full range of color
2238 %  values, so that they lie between the given black and white points. Gamma is
2239 %  applied before the values are mapped.
2240 %
2241 %  LevelizeImage() can be called with by using a +level command line
2242 %  API option, or using a '!' on a -level or LevelImage() geometry string.
2243 %
2244 %  It can be used for example de-contrast a greyscale image to the exact
2245 %  levels specified.  Or by using specific levels for each channel of an image
2246 %  you can convert a gray-scale image to any linear color gradient, according
2247 %  to those levels.
2248 %
2249 %  The format of the LevelizeImage method is:
2250 %
2251 %      MagickBooleanType LevelizeImage(Image *image,const double black_point,
2252 %        const double white_point,const double gamma,ExceptionInfo *exception)
2253 %
2254 %  A description of each parameter follows:
2255 %
2256 %    o image: the image.
2257 %
2258 %    o black_point: The level to map zero (black) to.
2259 %
2260 %    o white_point: The level to map QuantumRange (white) to.
2261 %
2262 %    o gamma: adjust gamma by this factor before mapping values.
2263 %
2264 %    o exception: return any errors or warnings in this structure.
2265 %
2266 */
2267 MagickExport MagickBooleanType LevelizeImage(Image *image,
2268   const double black_point,const double white_point,const double gamma,
2269   ExceptionInfo *exception)
2270 {
2271 #define LevelizeImageTag  "Levelize/Image"
2272 #define LevelizeValue(x) (ClampToQuantum(((MagickRealType) \
2273   pow((double) (QuantumScale*(x)),1.0/gamma))*(white_point-black_point)+ \
2274   black_point))
2275
2276   CacheView
2277     *image_view;
2278
2279   MagickBooleanType
2280     status;
2281
2282   MagickOffsetType
2283     progress;
2284
2285   register ssize_t
2286     i;
2287
2288   ssize_t
2289     y;
2290
2291   /*
2292     Allocate and initialize levels map.
2293   */
2294   assert(image != (Image *) NULL);
2295   assert(image->signature == MagickSignature);
2296   if (image->debug != MagickFalse)
2297     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2298   if (image->storage_class == PseudoClass)
2299 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2300   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2301 #endif
2302     for (i=0; i < (ssize_t) image->colors; i++)
2303     {
2304       /*
2305         Level colormap.
2306       */
2307       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2308         image->colormap[i].red=LevelizeValue(image->colormap[i].red);
2309       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2310         image->colormap[i].green=LevelizeValue(image->colormap[i].green);
2311       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2312         image->colormap[i].blue=LevelizeValue(image->colormap[i].blue);
2313       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2314         image->colormap[i].alpha=LevelizeValue(image->colormap[i].alpha);
2315     }
2316   /*
2317     Level image.
2318   */
2319   status=MagickTrue;
2320   progress=0;
2321   exception=(&image->exception);
2322   image_view=AcquireCacheView(image);
2323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2324   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2325 #endif
2326   for (y=0; y < (ssize_t) image->rows; y++)
2327   {
2328     register Quantum
2329       *restrict q;
2330
2331     register ssize_t
2332       x;
2333
2334     if (status == MagickFalse)
2335       continue;
2336     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2337     if (q == (const Quantum *) NULL)
2338       {
2339         status=MagickFalse;
2340         continue;
2341       }
2342     for (x=0; x < (ssize_t) image->columns; x++)
2343     {
2344       register ssize_t
2345         i;
2346
2347       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2348       {
2349         PixelTrait
2350           traits;
2351
2352         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2353         if ((traits & UpdatePixelTrait) != 0)
2354           q[i]=LevelizeValue(q[i]);
2355       }
2356       q+=GetPixelChannels(image);
2357     }
2358     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2359       status=MagickFalse;
2360     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2361       {
2362         MagickBooleanType
2363           proceed;
2364
2365 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2366   #pragma omp critical (MagickCore_LevelizeImage)
2367 #endif
2368         proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2369         if (proceed == MagickFalse)
2370           status=MagickFalse;
2371       }
2372   }
2373   image_view=DestroyCacheView(image_view);
2374   return(status);
2375 }
2376 \f
2377 /*
2378 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2379 %                                                                             %
2380 %                                                                             %
2381 %                                                                             %
2382 %     L e v e l I m a g e C o l o r s                                         %
2383 %                                                                             %
2384 %                                                                             %
2385 %                                                                             %
2386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2387 %
2388 %  LevelImageColors() maps the given color to "black" and "white" values,
2389 %  linearly spreading out the colors, and level values on a channel by channel
2390 %  bases, as per LevelImage().  The given colors allows you to specify
2391 %  different level ranges for each of the color channels separately.
2392 %
2393 %  If the boolean 'invert' is set true the image values will modifyed in the
2394 %  reverse direction. That is any existing "black" and "white" colors in the
2395 %  image will become the color values given, with all other values compressed
2396 %  appropriatally.  This effectivally maps a greyscale gradient into the given
2397 %  color gradient.
2398 %
2399 %  The format of the LevelImageColors method is:
2400 %
2401 %    MagickBooleanType LevelImageColors(Image *image,
2402 %      const PixelInfo *black_color,const PixelInfo *white_color,
2403 %      const MagickBooleanType invert,ExceptionInfo *exception)
2404 %
2405 %  A description of each parameter follows:
2406 %
2407 %    o image: the image.
2408 %
2409 %    o black_color: The color to map black to/from
2410 %
2411 %    o white_point: The color to map white to/from
2412 %
2413 %    o invert: if true map the colors (levelize), rather than from (level)
2414 %
2415 %    o exception: return any errors or warnings in this structure.
2416 %
2417 */
2418 MagickExport MagickBooleanType LevelImageColors(Image *image,
2419   const PixelInfo *black_color,const PixelInfo *white_color,
2420   const MagickBooleanType invert,ExceptionInfo *exception)
2421 {
2422   ChannelType
2423     channel_mask;
2424
2425   MagickStatusType
2426     status;
2427
2428   /*
2429     Allocate and initialize levels map.
2430   */
2431   assert(image != (Image *) NULL);
2432   assert(image->signature == MagickSignature);
2433   if (image->debug != MagickFalse)
2434     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2435   status=MagickFalse;
2436   if (invert == MagickFalse)
2437     {
2438       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2439         {
2440           channel_mask=SetPixelChannelMask(image,RedChannel);
2441           status|=LevelImage(image,black_color->red,white_color->red,1.0,
2442             exception);
2443           (void) SetPixelChannelMask(image,channel_mask);
2444         }
2445       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2446         {
2447           channel_mask=SetPixelChannelMask(image,GreenChannel);
2448           status|=LevelImage(image,black_color->green,white_color->green,1.0,
2449             exception);
2450           (void) SetPixelChannelMask(image,channel_mask);
2451         }
2452       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2453         {
2454           channel_mask=SetPixelChannelMask(image,BlueChannel);
2455           status|=LevelImage(image,black_color->blue,white_color->blue,1.0,
2456             exception);
2457           (void) SetPixelChannelMask(image,channel_mask);
2458         }
2459       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2460           (image->colorspace == CMYKColorspace))
2461         {
2462           channel_mask=SetPixelChannelMask(image,BlackChannel);
2463           status|=LevelImage(image,black_color->black,white_color->black,1.0,
2464             exception);
2465           (void) SetPixelChannelMask(image,channel_mask);
2466         }
2467       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2468           (image->matte == MagickTrue))
2469         {
2470           channel_mask=SetPixelChannelMask(image,AlphaChannel);
2471           status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2472             exception);
2473           (void) SetPixelChannelMask(image,channel_mask);
2474         }
2475     }
2476   else
2477     {
2478       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2479         {
2480           channel_mask=SetPixelChannelMask(image,RedChannel);
2481           status|=LevelizeImage(image,black_color->red,white_color->red,1.0,
2482             exception);
2483           (void) SetPixelChannelMask(image,channel_mask);
2484         }
2485       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2486         {
2487           channel_mask=SetPixelChannelMask(image,GreenChannel);
2488           status|=LevelizeImage(image,black_color->green,white_color->green,1.0,
2489             exception);
2490           (void) SetPixelChannelMask(image,channel_mask);
2491         }
2492       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2493         {
2494           channel_mask=SetPixelChannelMask(image,BlueChannel);
2495           status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2496             exception);
2497           (void) SetPixelChannelMask(image,channel_mask);
2498         }
2499       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2500           (image->colorspace == CMYKColorspace))
2501         {
2502           channel_mask=SetPixelChannelMask(image,BlackChannel);
2503           status|=LevelizeImage(image,black_color->black,white_color->black,1.0,
2504             exception);
2505           (void) SetPixelChannelMask(image,channel_mask);
2506         }
2507       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2508           (image->matte == MagickTrue))
2509         {
2510           channel_mask=SetPixelChannelMask(image,AlphaChannel);
2511           status|=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2512             exception);
2513           (void) SetPixelChannelMask(image,channel_mask);
2514         }
2515     }
2516   return(status == 0 ? MagickFalse : MagickTrue);
2517 }
2518 \f
2519 /*
2520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2521 %                                                                             %
2522 %                                                                             %
2523 %                                                                             %
2524 %     L i n e a r S t r e t c h I m a g e                                     %
2525 %                                                                             %
2526 %                                                                             %
2527 %                                                                             %
2528 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2529 %
2530 %  LinearStretchImage() discards any pixels below the black point and above
2531 %  the white point and levels the remaining pixels.
2532 %
2533 %  The format of the LinearStretchImage method is:
2534 %
2535 %      MagickBooleanType LinearStretchImage(Image *image,
2536 %        const double black_point,const double white_point,
2537 %        ExceptionInfo *exception)
2538 %
2539 %  A description of each parameter follows:
2540 %
2541 %    o image: the image.
2542 %
2543 %    o black_point: the black point.
2544 %
2545 %    o white_point: the white point.
2546 %
2547 %    o exception: return any errors or warnings in this structure.
2548 %
2549 */
2550 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2551   const double black_point,const double white_point,ExceptionInfo *exception)
2552 {
2553 #define LinearStretchImageTag  "LinearStretch/Image"
2554
2555   CacheView
2556     *image_view;
2557
2558   MagickBooleanType
2559     status;
2560
2561   MagickRealType
2562     *histogram,
2563     intensity;
2564
2565   ssize_t
2566     black,
2567     white,
2568     y;
2569
2570   /*
2571     Allocate histogram and linear map.
2572   */
2573   assert(image != (Image *) NULL);
2574   assert(image->signature == MagickSignature);
2575   histogram=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
2576     sizeof(*histogram));
2577   if (histogram == (MagickRealType *) NULL)
2578     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2579       image->filename);
2580   /*
2581     Form histogram.
2582   */
2583   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2584   image_view=AcquireCacheView(image);
2585   for (y=0; y < (ssize_t) image->rows; y++)
2586   {
2587     register const Quantum
2588       *restrict p;
2589
2590     register ssize_t
2591       x;
2592
2593     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2594     if (p == (const Quantum *) NULL)
2595       break;
2596     for (x=0; x < (ssize_t) image->columns; x++)
2597     {
2598       histogram[ScaleQuantumToMap(GetPixelIntensity(image,p))]++;
2599       p+=GetPixelChannels(image);
2600     }
2601   }
2602   image_view=DestroyCacheView(image_view);
2603   /*
2604     Find the histogram boundaries by locating the black and white point levels.
2605   */
2606   intensity=0.0;
2607   for (black=0; black < (ssize_t) MaxMap; black++)
2608   {
2609     intensity+=histogram[black];
2610     if (intensity >= black_point)
2611       break;
2612   }
2613   intensity=0.0;
2614   for (white=(ssize_t) MaxMap; white != 0; white--)
2615   {
2616     intensity+=histogram[white];
2617     if (intensity >= white_point)
2618       break;
2619   }
2620   histogram=(MagickRealType *) RelinquishMagickMemory(histogram);
2621   status=LevelImage(image,(double) black,(double) white,1.0,&image->exception);
2622   return(status);
2623 }
2624 \f
2625 /*
2626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2627 %                                                                             %
2628 %                                                                             %
2629 %                                                                             %
2630 %     M o d u l a t e I m a g e                                               %
2631 %                                                                             %
2632 %                                                                             %
2633 %                                                                             %
2634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2635 %
2636 %  ModulateImage() lets you control the brightness, saturation, and hue
2637 %  of an image.  Modulate represents the brightness, saturation, and hue
2638 %  as one parameter (e.g. 90,150,100).  If the image colorspace is HSL, the
2639 %  modulation is lightness, saturation, and hue.  And if the colorspace is
2640 %  HWB, use blackness, whiteness, and hue.
2641 %
2642 %  The format of the ModulateImage method is:
2643 %
2644 %      MagickBooleanType ModulateImage(Image *image,const char *modulate,
2645 %        ExceptionInfo *exception)
2646 %
2647 %  A description of each parameter follows:
2648 %
2649 %    o image: the image.
2650 %
2651 %    o modulate: Define the percent change in brightness, saturation, and hue.
2652 %
2653 %    o exception: return any errors or warnings in this structure.
2654 %
2655 */
2656
2657 static void ModulateHSB(const double percent_hue,
2658   const double percent_saturation,const double percent_brightness,
2659   Quantum *red,Quantum *green,Quantum *blue)
2660 {
2661   double
2662     brightness,
2663     hue,
2664     saturation;
2665
2666   /*
2667     Increase or decrease color brightness, saturation, or hue.
2668   */
2669   assert(red != (Quantum *) NULL);
2670   assert(green != (Quantum *) NULL);
2671   assert(blue != (Quantum *) NULL);
2672   ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2673   hue+=0.5*(0.01*percent_hue-1.0);
2674   while (hue < 0.0)
2675     hue+=1.0;
2676   while (hue > 1.0)
2677     hue-=1.0;
2678   saturation*=0.01*percent_saturation;
2679   brightness*=0.01*percent_brightness;
2680   ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2681 }
2682
2683 static void ModulateHSL(const double percent_hue,
2684   const double percent_saturation,const double percent_lightness,
2685   Quantum *red,Quantum *green,Quantum *blue)
2686 {
2687   double
2688     hue,
2689     lightness,
2690     saturation;
2691
2692   /*
2693     Increase or decrease color lightness, saturation, or hue.
2694   */
2695   assert(red != (Quantum *) NULL);
2696   assert(green != (Quantum *) NULL);
2697   assert(blue != (Quantum *) NULL);
2698   ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
2699   hue+=0.5*(0.01*percent_hue-1.0);
2700   while (hue < 0.0)
2701     hue+=1.0;
2702   while (hue > 1.0)
2703     hue-=1.0;
2704   saturation*=0.01*percent_saturation;
2705   lightness*=0.01*percent_lightness;
2706   ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
2707 }
2708
2709 static void ModulateHWB(const double percent_hue,const double percent_whiteness,  const double percent_blackness,Quantum *red,Quantum *green,Quantum *blue)
2710 {
2711   double
2712     blackness,
2713     hue,
2714     whiteness;
2715
2716   /*
2717     Increase or decrease color blackness, whiteness, or hue.
2718   */
2719   assert(red != (Quantum *) NULL);
2720   assert(green != (Quantum *) NULL);
2721   assert(blue != (Quantum *) NULL);
2722   ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
2723   hue+=0.5*(0.01*percent_hue-1.0);
2724   while (hue < 0.0)
2725     hue+=1.0;
2726   while (hue > 1.0)
2727     hue-=1.0;
2728   blackness*=0.01*percent_blackness;
2729   whiteness*=0.01*percent_whiteness;
2730   ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
2731 }
2732
2733 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
2734   ExceptionInfo *exception)
2735 {
2736 #define ModulateImageTag  "Modulate/Image"
2737
2738   CacheView
2739     *image_view;
2740
2741   ColorspaceType
2742     colorspace;
2743
2744   const char
2745     *artifact;
2746
2747   double
2748     percent_brightness,
2749     percent_hue,
2750     percent_saturation;
2751
2752   GeometryInfo
2753     geometry_info;
2754
2755   MagickBooleanType
2756     status;
2757
2758   MagickOffsetType
2759     progress;
2760
2761   MagickStatusType
2762     flags;
2763
2764   register ssize_t
2765     i;
2766
2767   ssize_t
2768     y;
2769
2770   /*
2771     Initialize modulate table.
2772   */
2773   assert(image != (Image *) NULL);
2774   assert(image->signature == MagickSignature);
2775   if (image->debug != MagickFalse)
2776     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2777   if (modulate == (char *) NULL)
2778     return(MagickFalse);
2779   flags=ParseGeometry(modulate,&geometry_info);
2780   percent_brightness=geometry_info.rho;
2781   percent_saturation=geometry_info.sigma;
2782   if ((flags & SigmaValue) == 0)
2783     percent_saturation=100.0;
2784   percent_hue=geometry_info.xi;
2785   if ((flags & XiValue) == 0)
2786     percent_hue=100.0;
2787   colorspace=UndefinedColorspace;
2788   artifact=GetImageArtifact(image,"modulate:colorspace");
2789   if (artifact != (const char *) NULL)
2790     colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
2791       MagickFalse,artifact);
2792   if (image->storage_class == PseudoClass)
2793     {
2794       /*
2795         Modulate colormap.
2796       */
2797 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2798   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2799 #endif
2800       for (i=0; i < (ssize_t) image->colors; i++)
2801         switch (colorspace)
2802         {
2803           case HSBColorspace:
2804           {
2805             ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2806               &image->colormap[i].red,&image->colormap[i].green,
2807               &image->colormap[i].blue);
2808             break;
2809           }
2810           case HSLColorspace:
2811           default:
2812           {
2813             ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2814               &image->colormap[i].red,&image->colormap[i].green,
2815               &image->colormap[i].blue);
2816             break;
2817           }
2818           case HWBColorspace:
2819           {
2820             ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2821               &image->colormap[i].red,&image->colormap[i].green,
2822               &image->colormap[i].blue);
2823             break;
2824           }
2825         }
2826     }
2827   /*
2828     Modulate image.
2829   */
2830   status=MagickTrue;
2831   progress=0;
2832   image_view=AcquireCacheView(image);
2833 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2834   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2835 #endif
2836   for (y=0; y < (ssize_t) image->rows; y++)
2837   {
2838     Quantum
2839       blue,
2840       green,
2841       red;
2842
2843     register Quantum
2844       *restrict q;
2845
2846     register ssize_t
2847       x;
2848
2849     if (status == MagickFalse)
2850       continue;
2851     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2852     if (q == (const Quantum *) NULL)
2853       {
2854         status=MagickFalse;
2855         continue;
2856       }
2857     for (x=0; x < (ssize_t) image->columns; x++)
2858     {
2859       red=GetPixelRed(image,q);
2860       green=GetPixelGreen(image,q);
2861       blue=GetPixelBlue(image,q);
2862       switch (colorspace)
2863       {
2864         case HSBColorspace:
2865         {
2866           ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2867             &red,&green,&blue);
2868           break;
2869         }
2870         case HSLColorspace:
2871         default:
2872         {
2873           ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2874             &red,&green,&blue);
2875           break;
2876         }
2877         case HWBColorspace:
2878         {
2879           ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2880             &red,&green,&blue);
2881           break;
2882         }
2883       }
2884       SetPixelRed(image,red,q);
2885       SetPixelGreen(image,green,q);
2886       SetPixelBlue(image,blue,q);
2887       q+=GetPixelChannels(image);
2888     }
2889     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2890       status=MagickFalse;
2891     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2892       {
2893         MagickBooleanType
2894           proceed;
2895
2896 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2897   #pragma omp critical (MagickCore_ModulateImage)
2898 #endif
2899         proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
2900         if (proceed == MagickFalse)
2901           status=MagickFalse;
2902       }
2903   }
2904   image_view=DestroyCacheView(image_view);
2905   return(status);
2906 }
2907 \f
2908 /*
2909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2910 %                                                                             %
2911 %                                                                             %
2912 %                                                                             %
2913 %     N e g a t e I m a g e                                                   %
2914 %                                                                             %
2915 %                                                                             %
2916 %                                                                             %
2917 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2918 %
2919 %  NegateImage() negates the colors in the reference image.  The grayscale
2920 %  option means that only grayscale values within the image are negated.
2921 %
2922 %  The format of the NegateImage method is:
2923 %
2924 %      MagickBooleanType NegateImage(Image *image,
2925 %        const MagickBooleanType grayscale,ExceptionInfo *exception)
2926 %
2927 %  A description of each parameter follows:
2928 %
2929 %    o image: the image.
2930 %
2931 %    o grayscale: If MagickTrue, only negate grayscale pixels within the image.
2932 %
2933 %    o exception: return any errors or warnings in this structure.
2934 %
2935 */
2936 MagickExport MagickBooleanType NegateImage(Image *image,
2937   const MagickBooleanType grayscale,ExceptionInfo *exception)
2938 {
2939 #define NegateImageTag  "Negate/Image"
2940
2941   CacheView
2942     *image_view;
2943
2944   MagickBooleanType
2945     status;
2946
2947   MagickOffsetType
2948     progress;
2949
2950   register ssize_t
2951     i;
2952
2953   ssize_t
2954     y;
2955
2956   assert(image != (Image *) NULL);
2957   assert(image->signature == MagickSignature);
2958   if (image->debug != MagickFalse)
2959     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2960   if (image->storage_class == PseudoClass)
2961     {
2962       /*
2963         Negate colormap.
2964       */
2965 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2966   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2967 #endif
2968       for (i=0; i < (ssize_t) image->colors; i++)
2969       {
2970         if (grayscale != MagickFalse)
2971           if ((image->colormap[i].red != image->colormap[i].green) ||
2972               (image->colormap[i].green != image->colormap[i].blue))
2973             continue;
2974         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2975           image->colormap[i].red=(Quantum) QuantumRange-
2976             image->colormap[i].red;
2977         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2978           image->colormap[i].green=(Quantum) QuantumRange-
2979             image->colormap[i].green;
2980         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2981           image->colormap[i].blue=(Quantum) QuantumRange-
2982             image->colormap[i].blue;
2983       }
2984     }
2985   /*
2986     Negate image.
2987   */
2988   status=MagickTrue;
2989   progress=0;
2990   image_view=AcquireCacheView(image);
2991   if (grayscale != MagickFalse)
2992     {
2993 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2994   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2995 #endif
2996       for (y=0; y < (ssize_t) image->rows; y++)
2997       {
2998         MagickBooleanType
2999           sync;
3000
3001         register Quantum
3002           *restrict q;
3003
3004         register ssize_t
3005           x;
3006
3007         if (status == MagickFalse)
3008           continue;
3009         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3010           exception);
3011         if (q == (const Quantum *) NULL)
3012           {
3013             status=MagickFalse;
3014             continue;
3015           }
3016         for (x=0; x < (ssize_t) image->columns; x++)
3017         {
3018           register ssize_t
3019             i;
3020
3021           if (IsPixelGray(image,q) != MagickFalse)
3022             {
3023               q+=GetPixelChannels(image);
3024               continue;
3025             }
3026           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3027           {
3028             PixelTrait
3029               traits;
3030
3031             traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3032             if ((traits & UpdatePixelTrait) != 0)
3033               q[i]=QuantumRange-q[i];
3034           }
3035           q+=GetPixelChannels(image);
3036         }
3037         sync=SyncCacheViewAuthenticPixels(image_view,exception);
3038         if (sync == MagickFalse)
3039           status=MagickFalse;
3040         if (image->progress_monitor != (MagickProgressMonitor) NULL)
3041           {
3042             MagickBooleanType
3043               proceed;
3044
3045 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3046   #pragma omp critical (MagickCore_NegateImage)
3047 #endif
3048             proceed=SetImageProgress(image,NegateImageTag,progress++,
3049               image->rows);
3050             if (proceed == MagickFalse)
3051               status=MagickFalse;
3052           }
3053       }
3054       image_view=DestroyCacheView(image_view);
3055       return(MagickTrue);
3056     }
3057   /*
3058     Negate image.
3059   */
3060 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3061   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3062 #endif
3063   for (y=0; y < (ssize_t) image->rows; y++)
3064   {
3065     register Quantum
3066       *restrict q;
3067
3068     register ssize_t
3069       x;
3070
3071     if (status == MagickFalse)
3072       continue;
3073     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3074     if (q == (const Quantum *) NULL)
3075       {
3076         status=MagickFalse;
3077         continue;
3078       }
3079     for (x=0; x < (ssize_t) image->columns; x++)
3080     {
3081       register ssize_t
3082         i;
3083
3084       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3085       {
3086         PixelTrait
3087           traits;
3088
3089         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3090         if ((traits & UpdatePixelTrait) != 0)
3091           q[i]=QuantumRange-q[i];
3092       }
3093       q+=GetPixelChannels(image);
3094     }
3095     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3096       status=MagickFalse;
3097     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3098       {
3099         MagickBooleanType
3100           proceed;
3101
3102 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3103   #pragma omp critical (MagickCore_NegateImage)
3104 #endif
3105         proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3106         if (proceed == MagickFalse)
3107           status=MagickFalse;
3108       }
3109   }
3110   image_view=DestroyCacheView(image_view);
3111   return(status);
3112 }
3113 \f
3114 /*
3115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3116 %                                                                             %
3117 %                                                                             %
3118 %                                                                             %
3119 %     N o r m a l i z e I m a g e                                             %
3120 %                                                                             %
3121 %                                                                             %
3122 %                                                                             %
3123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3124 %
3125 %  NormalizeImage() enhances the contrast of a color image by mapping the
3126 %  darkest 2 percent of all pixel to black and the brightest 1 percent to white.
3127 %
3128 %  The format of the NormalizeImage method is:
3129 %
3130 %      MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3131 %
3132 %  A description of each parameter follows:
3133 %
3134 %    o image: the image.
3135 %
3136 %    o exception: return any errors or warnings in this structure.
3137 %
3138 */
3139 MagickExport MagickBooleanType NormalizeImage(Image *image,
3140   ExceptionInfo *exception)
3141 {
3142   double
3143     black_point,
3144     white_point;
3145
3146   black_point=(double) image->columns*image->rows*0.0015;
3147   white_point=(double) image->columns*image->rows*0.9995;
3148   return(ContrastStretchImage(image,black_point,white_point,exception));
3149 }
3150 \f
3151 /*
3152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3153 %                                                                             %
3154 %                                                                             %
3155 %                                                                             %
3156 %     S i g m o i d a l C o n t r a s t I m a g e                             %
3157 %                                                                             %
3158 %                                                                             %
3159 %                                                                             %
3160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3161 %
3162 %  SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3163 %  sigmoidal contrast algorithm.  Increase the contrast of the image using a
3164 %  sigmoidal transfer function without saturating highlights or shadows.
3165 %  Contrast indicates how much to increase the contrast (0 is none; 3 is
3166 %  typical; 20 is pushing it); mid-point indicates where midtones fall in the
3167 %  resultant image (0 is white; 50% is middle-gray; 100% is black).  Set
3168 %  sharpen to MagickTrue to increase the image contrast otherwise the contrast
3169 %  is reduced.
3170 %
3171 %  The format of the SigmoidalContrastImage method is:
3172 %
3173 %      MagickBooleanType SigmoidalContrastImage(Image *image,
3174 %        const MagickBooleanType sharpen,const char *levels,
3175 %        ExceptionInfo *exception)
3176 %
3177 %  A description of each parameter follows:
3178 %
3179 %    o image: the image.
3180 %
3181 %    o sharpen: Increase or decrease image contrast.
3182 %
3183 %    o alpha: strength of the contrast, the larger the number the more
3184 %      'threshold-like' it becomes.
3185 %
3186 %    o beta: midpoint of the function as a color value 0 to QuantumRange.
3187 %
3188 %    o exception: return any errors or warnings in this structure.
3189 %
3190 */
3191 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3192   const MagickBooleanType sharpen,const double contrast,const double midpoint,
3193   ExceptionInfo *exception)
3194 {
3195 #define SigmoidalContrastImageTag  "SigmoidalContrast/Image"
3196
3197   CacheView
3198     *image_view;
3199
3200   MagickBooleanType
3201     status;
3202
3203   MagickOffsetType
3204     progress;
3205
3206   MagickRealType
3207     *sigmoidal_map;
3208
3209   register ssize_t
3210     i;
3211
3212   ssize_t
3213     y;
3214
3215   /*
3216     Allocate and initialize sigmoidal maps.
3217   */
3218   assert(image != (Image *) NULL);
3219   assert(image->signature == MagickSignature);
3220   if (image->debug != MagickFalse)
3221     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3222   sigmoidal_map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
3223     sizeof(*sigmoidal_map));
3224   if (sigmoidal_map == (MagickRealType *) NULL)
3225     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3226       image->filename);
3227   (void) ResetMagickMemory(sigmoidal_map,0,(MaxMap+1)*sizeof(*sigmoidal_map));
3228 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3229   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3230 #endif
3231   for (i=0; i <= (ssize_t) MaxMap; i++)
3232   {
3233     if (sharpen != MagickFalse)
3234       {
3235         sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
3236           (MaxMap*((1.0/(1.0+exp(contrast*(midpoint/(double) QuantumRange-
3237           (double) i/MaxMap))))-(1.0/(1.0+exp(contrast*(midpoint/(double)
3238           QuantumRange)))))/((1.0/(1.0+exp(contrast*(midpoint/(double)
3239           QuantumRange-1.0))))-(1.0/(1.0+exp(contrast*(midpoint/(double)
3240           QuantumRange)))))+0.5));
3241         continue;
3242       }
3243     sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
3244       (MaxMap*(QuantumScale*midpoint-log((1.0-(1.0/(1.0+exp(midpoint/(double)
3245       QuantumRange*contrast))+((double) i/MaxMap)*((1.0/(1.0+exp(contrast*(
3246       midpoint/(double) QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/(double)
3247       QuantumRange*contrast))))))/(1.0/(1.0+exp(midpoint/(double) QuantumRange*
3248       contrast))+((double) i/MaxMap)*((1.0/(1.0+exp(contrast*(midpoint/(double)
3249       QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/(double) QuantumRange*
3250       contrast))))))/contrast)));
3251   }
3252   if (image->storage_class == PseudoClass)
3253     {
3254       /*
3255         Sigmoidal-contrast enhance colormap.
3256       */
3257 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3258   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3259 #endif
3260       for (i=0; i < (ssize_t) image->colors; i++)
3261       {
3262         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3263           image->colormap[i].red=ClampToQuantum(sigmoidal_map[
3264             ScaleQuantumToMap(image->colormap[i].red)]);
3265         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3266           image->colormap[i].green=ClampToQuantum(sigmoidal_map[
3267             ScaleQuantumToMap(image->colormap[i].green)]);
3268         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3269           image->colormap[i].blue=ClampToQuantum(sigmoidal_map[
3270             ScaleQuantumToMap(image->colormap[i].blue)]);
3271         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3272           image->colormap[i].alpha=ClampToQuantum(sigmoidal_map[
3273             ScaleQuantumToMap(image->colormap[i].alpha)]);
3274       }
3275     }
3276   /*
3277     Sigmoidal-contrast enhance image.
3278   */
3279   status=MagickTrue;
3280   progress=0;
3281   image_view=AcquireCacheView(image);
3282 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3283   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3284 #endif
3285   for (y=0; y < (ssize_t) image->rows; y++)
3286   {
3287     register Quantum
3288       *restrict q;
3289
3290     register ssize_t
3291       x;
3292
3293     if (status == MagickFalse)
3294       continue;
3295     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3296     if (q == (const Quantum *) NULL)
3297       {
3298         status=MagickFalse;
3299         continue;
3300       }
3301     for (x=0; x < (ssize_t) image->columns; x++)
3302     {
3303       register ssize_t
3304         i;
3305
3306       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3307       {
3308         PixelTrait
3309           traits;
3310
3311         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3312         if ((traits & UpdatePixelTrait) != 0)
3313           q[i]=ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(q[i])]);
3314       }
3315       q+=GetPixelChannels(image);
3316     }
3317     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3318       status=MagickFalse;
3319     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3320       {
3321         MagickBooleanType
3322           proceed;
3323
3324 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3325   #pragma omp critical (MagickCore_SigmoidalContrastImage)
3326 #endif
3327         proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3328           image->rows);
3329         if (proceed == MagickFalse)
3330           status=MagickFalse;
3331       }
3332   }
3333   image_view=DestroyCacheView(image_view);
3334   sigmoidal_map=(MagickRealType *) RelinquishMagickMemory(sigmoidal_map);
3335   return(status);
3336 }