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