]> 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.2126*image->colormap[i].red+0.7152*image->colormap[i].green+
739           0.0722*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.2126*GetPixelRed(image,q)+0.7152*GetPixelGreen(image,q)+0.0722*
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   ConvertsRGBToHSB(*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   ConvertHSBTosRGB(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 (IsGrayColorspace(image->colorspace) != MagickFalse)
2222     (void) SetImageColorspace(image,sRGBColorspace,exception);
2223   if (image->storage_class == PseudoClass)
2224 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2225     #pragma omp parallel for schedule(static,4) shared(progress,status) \
2226       dynamic_number_threads(image,image->columns,1,1)
2227 #endif
2228     for (i=0; i < (ssize_t) image->colors; i++)
2229     {
2230       /*
2231         Level colormap.
2232       */
2233       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2234         image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2235           white_point,gamma,image->colormap[i].red));
2236       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2237         image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2238           white_point,gamma,image->colormap[i].green));
2239       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2240         image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2241           white_point,gamma,image->colormap[i].blue));
2242       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2243         image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2244           white_point,gamma,image->colormap[i].alpha));
2245       }
2246   /*
2247     Level image.
2248   */
2249   status=MagickTrue;
2250   progress=0;
2251   image_view=AcquireAuthenticCacheView(image,exception);
2252 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2253   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2254     dynamic_number_threads(image,image->columns,image->rows,1)
2255 #endif
2256   for (y=0; y < (ssize_t) image->rows; y++)
2257   {
2258     register Quantum
2259       *restrict q;
2260
2261     register ssize_t
2262       x;
2263
2264     if (status == MagickFalse)
2265       continue;
2266     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2267     if (q == (Quantum *) NULL)
2268       {
2269         status=MagickFalse;
2270         continue;
2271       }
2272     for (x=0; x < (ssize_t) image->columns; x++)
2273     {
2274       register ssize_t
2275         i;
2276
2277       if (GetPixelMask(image,q) != 0)
2278         {
2279           q+=GetPixelChannels(image);
2280           continue;
2281         }
2282       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2283       {
2284         PixelChannel
2285           channel;
2286
2287         PixelTrait
2288           traits;
2289
2290         channel=GetPixelChannelMapChannel(image,i);
2291         traits=GetPixelChannelMapTraits(image,channel);
2292         if ((traits & UpdatePixelTrait) == 0)
2293           continue;
2294         q[i]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2295           (MagickRealType) q[i]));
2296       }
2297       q+=GetPixelChannels(image);
2298     }
2299     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2300       status=MagickFalse;
2301     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2302       {
2303         MagickBooleanType
2304           proceed;
2305
2306 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2307         #pragma omp critical (MagickCore_LevelImage)
2308 #endif
2309         proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2310         if (proceed == MagickFalse)
2311           status=MagickFalse;
2312       }
2313   }
2314   image_view=DestroyCacheView(image_view);
2315   return(status);
2316 }
2317 \f
2318 /*
2319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2320 %                                                                             %
2321 %                                                                             %
2322 %                                                                             %
2323 %     L e v e l i z e I m a g e                                               %
2324 %                                                                             %
2325 %                                                                             %
2326 %                                                                             %
2327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2328 %
2329 %  LevelizeImage() applies the reversed LevelImage() operation to just
2330 %  the specific channels specified.  It compresses the full range of color
2331 %  values, so that they lie between the given black and white points. Gamma is
2332 %  applied before the values are mapped.
2333 %
2334 %  LevelizeImage() can be called with by using a +level command line
2335 %  API option, or using a '!' on a -level or LevelImage() geometry string.
2336 %
2337 %  It can be used to de-contrast a greyscale image to the exact levels
2338 %  specified.  Or by using specific levels for each channel of an image you
2339 %  can convert a gray-scale image to any linear color gradient, according to
2340 %  those levels.
2341 %
2342 %  The format of the LevelizeImage method is:
2343 %
2344 %      MagickBooleanType LevelizeImage(Image *image,const double black_point,
2345 %        const double white_point,const double gamma,ExceptionInfo *exception)
2346 %
2347 %  A description of each parameter follows:
2348 %
2349 %    o image: the image.
2350 %
2351 %    o black_point: The level to map zero (black) to.
2352 %
2353 %    o white_point: The level to map QuantumRange (white) to.
2354 %
2355 %    o gamma: adjust gamma by this factor before mapping values.
2356 %
2357 %    o exception: return any errors or warnings in this structure.
2358 %
2359 */
2360 MagickExport MagickBooleanType LevelizeImage(Image *image,
2361   const double black_point,const double white_point,const double gamma,
2362   ExceptionInfo *exception)
2363 {
2364 #define LevelizeImageTag  "Levelize/Image"
2365 #define LevelizeValue(x) (ClampToQuantum(((MagickRealType) \
2366   pow((double) (QuantumScale*(x)),1.0/gamma))*(white_point-black_point)+ \
2367   black_point))
2368
2369   CacheView
2370     *image_view;
2371
2372   MagickBooleanType
2373     status;
2374
2375   MagickOffsetType
2376     progress;
2377
2378   register ssize_t
2379     i;
2380
2381   ssize_t
2382     y;
2383
2384   /*
2385     Allocate and initialize levels map.
2386   */
2387   assert(image != (Image *) NULL);
2388   assert(image->signature == MagickSignature);
2389   if (image->debug != MagickFalse)
2390     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2391   if (IsGrayColorspace(image->colorspace) != MagickFalse)
2392     (void) SetImageColorspace(image,sRGBColorspace,exception);
2393   if (image->storage_class == PseudoClass)
2394 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2395     #pragma omp parallel for schedule(static,4) shared(progress,status) \
2396       dynamic_number_threads(image,image->columns,1,1)
2397 #endif
2398     for (i=0; i < (ssize_t) image->colors; i++)
2399     {
2400       /*
2401         Level colormap.
2402       */
2403       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2404         image->colormap[i].red=(double) LevelizeValue(
2405           image->colormap[i].red);
2406       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2407         image->colormap[i].green=(double) LevelizeValue(
2408           image->colormap[i].green);
2409       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2410         image->colormap[i].blue=(double) LevelizeValue(
2411           image->colormap[i].blue);
2412       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2413         image->colormap[i].alpha=(double) LevelizeValue(
2414           image->colormap[i].alpha);
2415     }
2416   /*
2417     Level image.
2418   */
2419   status=MagickTrue;
2420   progress=0;
2421   image_view=AcquireAuthenticCacheView(image,exception);
2422 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2423   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2424     dynamic_number_threads(image,image->columns,image->rows,1)
2425 #endif
2426   for (y=0; y < (ssize_t) image->rows; y++)
2427   {
2428     register Quantum
2429       *restrict q;
2430
2431     register ssize_t
2432       x;
2433
2434     if (status == MagickFalse)
2435       continue;
2436     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2437     if (q == (Quantum *) NULL)
2438       {
2439         status=MagickFalse;
2440         continue;
2441       }
2442     for (x=0; x < (ssize_t) image->columns; x++)
2443     {
2444       register ssize_t
2445         i;
2446
2447       if (GetPixelMask(image,q) != 0)
2448         {
2449           q+=GetPixelChannels(image);
2450           continue;
2451         }
2452       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2453       {
2454         PixelChannel
2455           channel;
2456
2457         PixelTrait
2458           traits;
2459
2460         channel=GetPixelChannelMapChannel(image,i);
2461         traits=GetPixelChannelMapTraits(image,channel);
2462         if ((traits & UpdatePixelTrait) == 0)
2463           continue;
2464         q[i]=LevelizeValue(q[i]);
2465       }
2466       q+=GetPixelChannels(image);
2467     }
2468     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2469       status=MagickFalse;
2470     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2471       {
2472         MagickBooleanType
2473           proceed;
2474
2475 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2476         #pragma omp critical (MagickCore_LevelizeImage)
2477 #endif
2478         proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2479         if (proceed == MagickFalse)
2480           status=MagickFalse;
2481       }
2482   }
2483   image_view=DestroyCacheView(image_view);
2484   return(status);
2485 }
2486 \f
2487 /*
2488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2489 %                                                                             %
2490 %                                                                             %
2491 %                                                                             %
2492 %     L e v e l I m a g e C o l o r s                                         %
2493 %                                                                             %
2494 %                                                                             %
2495 %                                                                             %
2496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2497 %
2498 %  LevelImageColors() maps the given color to "black" and "white" values,
2499 %  linearly spreading out the colors, and level values on a channel by channel
2500 %  bases, as per LevelImage().  The given colors allows you to specify
2501 %  different level ranges for each of the color channels separately.
2502 %
2503 %  If the boolean 'invert' is set true the image values will modifyed in the
2504 %  reverse direction. That is any existing "black" and "white" colors in the
2505 %  image will become the color values given, with all other values compressed
2506 %  appropriatally.  This effectivally maps a greyscale gradient into the given
2507 %  color gradient.
2508 %
2509 %  The format of the LevelImageColors method is:
2510 %
2511 %    MagickBooleanType LevelImageColors(Image *image,
2512 %      const PixelInfo *black_color,const PixelInfo *white_color,
2513 %      const MagickBooleanType invert,ExceptionInfo *exception)
2514 %
2515 %  A description of each parameter follows:
2516 %
2517 %    o image: the image.
2518 %
2519 %    o black_color: The color to map black to/from
2520 %
2521 %    o white_point: The color to map white to/from
2522 %
2523 %    o invert: if true map the colors (levelize), rather than from (level)
2524 %
2525 %    o exception: return any errors or warnings in this structure.
2526 %
2527 */
2528 MagickExport MagickBooleanType LevelImageColors(Image *image,
2529   const PixelInfo *black_color,const PixelInfo *white_color,
2530   const MagickBooleanType invert,ExceptionInfo *exception)
2531 {
2532   ChannelType
2533     channel_mask;
2534
2535   MagickStatusType
2536     status;
2537
2538   /*
2539     Allocate and initialize levels map.
2540   */
2541   assert(image != (Image *) NULL);
2542   assert(image->signature == MagickSignature);
2543   if (image->debug != MagickFalse)
2544     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2545   status=MagickFalse;
2546   if (invert == MagickFalse)
2547     {
2548       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2549         {
2550           channel_mask=SetPixelChannelMask(image,RedChannel);
2551           status|=LevelImage(image,black_color->red,white_color->red,1.0,
2552             exception);
2553           (void) SetPixelChannelMask(image,channel_mask);
2554         }
2555       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2556         {
2557           channel_mask=SetPixelChannelMask(image,GreenChannel);
2558           status|=LevelImage(image,black_color->green,white_color->green,1.0,
2559             exception);
2560           (void) SetPixelChannelMask(image,channel_mask);
2561         }
2562       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2563         {
2564           channel_mask=SetPixelChannelMask(image,BlueChannel);
2565           status|=LevelImage(image,black_color->blue,white_color->blue,1.0,
2566             exception);
2567           (void) SetPixelChannelMask(image,channel_mask);
2568         }
2569       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2570           (image->colorspace == CMYKColorspace))
2571         {
2572           channel_mask=SetPixelChannelMask(image,BlackChannel);
2573           status|=LevelImage(image,black_color->black,white_color->black,1.0,
2574             exception);
2575           (void) SetPixelChannelMask(image,channel_mask);
2576         }
2577       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2578           (image->matte == MagickTrue))
2579         {
2580           channel_mask=SetPixelChannelMask(image,AlphaChannel);
2581           status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2582             exception);
2583           (void) SetPixelChannelMask(image,channel_mask);
2584         }
2585     }
2586   else
2587     {
2588       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2589         {
2590           channel_mask=SetPixelChannelMask(image,RedChannel);
2591           status|=LevelizeImage(image,black_color->red,white_color->red,1.0,
2592             exception);
2593           (void) SetPixelChannelMask(image,channel_mask);
2594         }
2595       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2596         {
2597           channel_mask=SetPixelChannelMask(image,GreenChannel);
2598           status|=LevelizeImage(image,black_color->green,white_color->green,1.0,
2599             exception);
2600           (void) SetPixelChannelMask(image,channel_mask);
2601         }
2602       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2603         {
2604           channel_mask=SetPixelChannelMask(image,BlueChannel);
2605           status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2606             exception);
2607           (void) SetPixelChannelMask(image,channel_mask);
2608         }
2609       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2610           (image->colorspace == CMYKColorspace))
2611         {
2612           channel_mask=SetPixelChannelMask(image,BlackChannel);
2613           status|=LevelizeImage(image,black_color->black,white_color->black,1.0,
2614             exception);
2615           (void) SetPixelChannelMask(image,channel_mask);
2616         }
2617       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2618           (image->matte == MagickTrue))
2619         {
2620           channel_mask=SetPixelChannelMask(image,AlphaChannel);
2621           status|=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2622             exception);
2623           (void) SetPixelChannelMask(image,channel_mask);
2624         }
2625     }
2626   return(status == 0 ? MagickFalse : MagickTrue);
2627 }
2628 \f
2629 /*
2630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2631 %                                                                             %
2632 %                                                                             %
2633 %                                                                             %
2634 %     L i n e a r S t r e t c h I m a g e                                     %
2635 %                                                                             %
2636 %                                                                             %
2637 %                                                                             %
2638 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2639 %
2640 %  LinearStretchImage() discards any pixels below the black point and above
2641 %  the white point and levels the remaining pixels.
2642 %
2643 %  The format of the LinearStretchImage method is:
2644 %
2645 %      MagickBooleanType LinearStretchImage(Image *image,
2646 %        const double black_point,const double white_point,
2647 %        ExceptionInfo *exception)
2648 %
2649 %  A description of each parameter follows:
2650 %
2651 %    o image: the image.
2652 %
2653 %    o black_point: the black point.
2654 %
2655 %    o white_point: the white point.
2656 %
2657 %    o exception: return any errors or warnings in this structure.
2658 %
2659 */
2660 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2661   const double black_point,const double white_point,ExceptionInfo *exception)
2662 {
2663 #define LinearStretchImageTag  "LinearStretch/Image"
2664
2665   CacheView
2666     *image_view;
2667
2668   MagickBooleanType
2669     status;
2670
2671   MagickRealType
2672     *histogram,
2673     intensity;
2674
2675   ssize_t
2676     black,
2677     white,
2678     y;
2679
2680   /*
2681     Allocate histogram and linear map.
2682   */
2683   assert(image != (Image *) NULL);
2684   assert(image->signature == MagickSignature);
2685   histogram=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
2686     sizeof(*histogram));
2687   if (histogram == (MagickRealType *) NULL)
2688     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2689       image->filename);
2690   /*
2691     Form histogram.
2692   */
2693   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2694   image_view=AcquireVirtualCacheView(image,exception);
2695   for (y=0; y < (ssize_t) image->rows; y++)
2696   {
2697     register const Quantum
2698       *restrict p;
2699
2700     register ssize_t
2701       x;
2702
2703     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2704     if (p == (const Quantum *) NULL)
2705       break;
2706     for (x=0; x < (ssize_t) image->columns; x++)
2707     {
2708       histogram[ScaleQuantumToMap(GetPixelIntensity(image,p))]++;
2709       p+=GetPixelChannels(image);
2710     }
2711   }
2712   image_view=DestroyCacheView(image_view);
2713   /*
2714     Find the histogram boundaries by locating the black and white point levels.
2715   */
2716   intensity=0.0;
2717   for (black=0; black < (ssize_t) MaxMap; black++)
2718   {
2719     intensity+=histogram[black];
2720     if (intensity >= black_point)
2721       break;
2722   }
2723   intensity=0.0;
2724   for (white=(ssize_t) MaxMap; white != 0; white--)
2725   {
2726     intensity+=histogram[white];
2727     if (intensity >= white_point)
2728       break;
2729   }
2730   histogram=(MagickRealType *) RelinquishMagickMemory(histogram);
2731   status=LevelImage(image,(double) black,(double) white,1.0,exception);
2732   return(status);
2733 }
2734 \f
2735 /*
2736 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2737 %                                                                             %
2738 %                                                                             %
2739 %                                                                             %
2740 %     M o d u l a t e I m a g e                                               %
2741 %                                                                             %
2742 %                                                                             %
2743 %                                                                             %
2744 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2745 %
2746 %  ModulateImage() lets you control the brightness, saturation, and hue
2747 %  of an image.  Modulate represents the brightness, saturation, and hue
2748 %  as one parameter (e.g. 90,150,100).  If the image colorspace is HSL, the
2749 %  modulation is lightness, saturation, and hue.  And if the colorspace is
2750 %  HWB, use blackness, whiteness, and hue.
2751 %
2752 %  The format of the ModulateImage method is:
2753 %
2754 %      MagickBooleanType ModulateImage(Image *image,const char *modulate,
2755 %        ExceptionInfo *exception)
2756 %
2757 %  A description of each parameter follows:
2758 %
2759 %    o image: the image.
2760 %
2761 %    o modulate: Define the percent change in brightness, saturation, and hue.
2762 %
2763 %    o exception: return any errors or warnings in this structure.
2764 %
2765 */
2766
2767 static void ModulateHSB(const double percent_hue,
2768   const double percent_saturation,const double percent_brightness,double *red,
2769   double *green,double *blue)
2770 {
2771   double
2772     brightness,
2773     hue,
2774     saturation;
2775
2776   /*
2777     Increase or decrease color brightness, saturation, or hue.
2778   */
2779   assert(red != (double *) NULL);
2780   assert(green != (double *) NULL);
2781   assert(blue != (double *) NULL);
2782   ConvertsRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2783   hue+=0.5*(0.01*percent_hue-1.0);
2784   while (hue < 0.0)
2785     hue+=1.0;
2786   while (hue > 1.0)
2787     hue-=1.0;
2788   saturation*=0.01*percent_saturation;
2789   brightness*=0.01*percent_brightness;
2790   ConvertHSBTosRGB(hue,saturation,brightness,red,green,blue);
2791 }
2792
2793 static void ModulateHSL(const double percent_hue,
2794   const double percent_saturation,const double percent_lightness,double *red,
2795   double *green,double *blue)
2796 {
2797   double
2798     hue,
2799     lightness,
2800     saturation;
2801
2802   /*
2803     Increase or decrease color lightness, saturation, or hue.
2804   */
2805   assert(red != (double *) NULL);
2806   assert(green != (double *) NULL);
2807   assert(blue != (double *) NULL);
2808   ConvertsRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
2809   hue+=0.5*(0.01*percent_hue-1.0);
2810   while (hue < 0.0)
2811     hue+=1.0;
2812   while (hue > 1.0)
2813     hue-=1.0;
2814   saturation*=0.01*percent_saturation;
2815   lightness*=0.01*percent_lightness;
2816   ConvertHSLTosRGB(hue,saturation,lightness,red,green,blue);
2817 }
2818
2819 static void ModulateHWB(const double percent_hue,const double percent_whiteness,  const double percent_blackness,double *red,double *green,double *blue)
2820 {
2821   double
2822     blackness,
2823     hue,
2824     whiteness;
2825
2826   /*
2827     Increase or decrease color blackness, whiteness, or hue.
2828   */
2829   assert(red != (double *) NULL);
2830   assert(green != (double *) NULL);
2831   assert(blue != (double *) NULL);
2832   ConvertsRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
2833   hue+=0.5*(0.01*percent_hue-1.0);
2834   while (hue < 0.0)
2835     hue+=1.0;
2836   while (hue > 1.0)
2837     hue-=1.0;
2838   blackness*=0.01*percent_blackness;
2839   whiteness*=0.01*percent_whiteness;
2840   ConvertHWBTosRGB(hue,whiteness,blackness,red,green,blue);
2841 }
2842
2843 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
2844   ExceptionInfo *exception)
2845 {
2846 #define ModulateImageTag  "Modulate/Image"
2847
2848   CacheView
2849     *image_view;
2850
2851   ColorspaceType
2852     colorspace;
2853
2854   const char
2855     *artifact;
2856
2857   double
2858     percent_brightness,
2859     percent_hue,
2860     percent_saturation;
2861
2862   GeometryInfo
2863     geometry_info;
2864
2865   MagickBooleanType
2866     status;
2867
2868   MagickOffsetType
2869     progress;
2870
2871   MagickStatusType
2872     flags;
2873
2874   register ssize_t
2875     i;
2876
2877   ssize_t
2878     y;
2879
2880   /*
2881     Initialize modulate table.
2882   */
2883   assert(image != (Image *) NULL);
2884   assert(image->signature == MagickSignature);
2885   if (image->debug != MagickFalse)
2886     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2887   if (modulate == (char *) NULL)
2888     return(MagickFalse);
2889   flags=ParseGeometry(modulate,&geometry_info);
2890   percent_brightness=geometry_info.rho;
2891   percent_saturation=geometry_info.sigma;
2892   if ((flags & SigmaValue) == 0)
2893     percent_saturation=100.0;
2894   percent_hue=geometry_info.xi;
2895   if ((flags & XiValue) == 0)
2896     percent_hue=100.0;
2897   colorspace=UndefinedColorspace;
2898   artifact=GetImageArtifact(image,"modulate:colorspace");
2899   if (artifact != (const char *) NULL)
2900     colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
2901       MagickFalse,artifact);
2902   if (image->storage_class == PseudoClass)
2903     {
2904       /*
2905         Modulate colormap.
2906       */
2907 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2908       #pragma omp parallel for schedule(static,4) shared(progress,status) \
2909         dynamic_number_threads(image,image->columns,1,1)
2910 #endif
2911       for (i=0; i < (ssize_t) image->colors; i++)
2912         switch (colorspace)
2913         {
2914           case HSBColorspace:
2915           {
2916             ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2917               &image->colormap[i].red,&image->colormap[i].green,
2918               &image->colormap[i].blue);
2919             break;
2920           }
2921           case HSLColorspace:
2922           default:
2923           {
2924             ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2925               &image->colormap[i].red,&image->colormap[i].green,
2926               &image->colormap[i].blue);
2927             break;
2928           }
2929           case HWBColorspace:
2930           {
2931             ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2932               &image->colormap[i].red,&image->colormap[i].green,
2933               &image->colormap[i].blue);
2934             break;
2935           }
2936         }
2937     }
2938   /*
2939     Modulate image.
2940   */
2941   status=MagickTrue;
2942   progress=0;
2943   image_view=AcquireAuthenticCacheView(image,exception);
2944 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2945   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2946     dynamic_number_threads(image,image->columns,image->rows,1)
2947 #endif
2948   for (y=0; y < (ssize_t) image->rows; y++)
2949   {
2950     double
2951       blue,
2952       green,
2953       red;
2954
2955     register Quantum
2956       *restrict q;
2957
2958     register ssize_t
2959       x;
2960
2961     if (status == MagickFalse)
2962       continue;
2963     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2964     if (q == (Quantum *) NULL)
2965       {
2966         status=MagickFalse;
2967         continue;
2968       }
2969     for (x=0; x < (ssize_t) image->columns; x++)
2970     {
2971       red=(double) GetPixelRed(image,q);
2972       green=(double) GetPixelGreen(image,q);
2973       blue=(double) GetPixelBlue(image,q);
2974       switch (colorspace)
2975       {
2976         case HSBColorspace:
2977         {
2978           ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2979             &red,&green,&blue);
2980           break;
2981         }
2982         case HSLColorspace:
2983         default:
2984         {
2985           ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2986             &red,&green,&blue);
2987           break;
2988         }
2989         case HWBColorspace:
2990         {
2991           ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2992             &red,&green,&blue);
2993           break;
2994         }
2995       }
2996       SetPixelRed(image,ClampToQuantum(red),q);
2997       SetPixelGreen(image,ClampToQuantum(green),q);
2998       SetPixelBlue(image,ClampToQuantum(blue),q);
2999       q+=GetPixelChannels(image);
3000     }
3001     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3002       status=MagickFalse;
3003     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3004       {
3005         MagickBooleanType
3006           proceed;
3007
3008 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3009         #pragma omp critical (MagickCore_ModulateImage)
3010 #endif
3011         proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3012         if (proceed == MagickFalse)
3013           status=MagickFalse;
3014       }
3015   }
3016   image_view=DestroyCacheView(image_view);
3017   return(status);
3018 }
3019 \f
3020 /*
3021 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3022 %                                                                             %
3023 %                                                                             %
3024 %                                                                             %
3025 %     N e g a t e I m a g e                                                   %
3026 %                                                                             %
3027 %                                                                             %
3028 %                                                                             %
3029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3030 %
3031 %  NegateImage() negates the colors in the reference image.  The grayscale
3032 %  option means that only grayscale values within the image are negated.
3033 %
3034 %  The format of the NegateImage method is:
3035 %
3036 %      MagickBooleanType NegateImage(Image *image,
3037 %        const MagickBooleanType grayscale,ExceptionInfo *exception)
3038 %
3039 %  A description of each parameter follows:
3040 %
3041 %    o image: the image.
3042 %
3043 %    o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3044 %
3045 %    o exception: return any errors or warnings in this structure.
3046 %
3047 */
3048 MagickExport MagickBooleanType NegateImage(Image *image,
3049   const MagickBooleanType grayscale,ExceptionInfo *exception)
3050 {
3051 #define NegateImageTag  "Negate/Image"
3052
3053   CacheView
3054     *image_view;
3055
3056   MagickBooleanType
3057     status;
3058
3059   MagickOffsetType
3060     progress;
3061
3062   register ssize_t
3063     i;
3064
3065   ssize_t
3066     y;
3067
3068   assert(image != (Image *) NULL);
3069   assert(image->signature == MagickSignature);
3070   if (image->debug != MagickFalse)
3071     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3072   if (image->storage_class == PseudoClass)
3073     {
3074       /*
3075         Negate colormap.
3076       */
3077 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3078       #pragma omp parallel for schedule(static) shared(progress,status) \
3079         dynamic_number_threads(image,image->columns,1,1)
3080 #endif
3081       for (i=0; i < (ssize_t) image->colors; i++)
3082       {
3083         if (grayscale != MagickFalse)
3084           if ((image->colormap[i].red != image->colormap[i].green) ||
3085               (image->colormap[i].green != image->colormap[i].blue))
3086             continue;
3087         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3088           image->colormap[i].red=(Quantum) QuantumRange-
3089             image->colormap[i].red;
3090         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3091           image->colormap[i].green=(Quantum) QuantumRange-
3092             image->colormap[i].green;
3093         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3094           image->colormap[i].blue=(Quantum) QuantumRange-
3095             image->colormap[i].blue;
3096       }
3097     }
3098   /*
3099     Negate image.
3100   */
3101   status=MagickTrue;
3102   progress=0;
3103   image_view=AcquireAuthenticCacheView(image,exception);
3104   if (grayscale != MagickFalse)
3105     {
3106 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3107       #pragma omp parallel for schedule(static) shared(progress,status) \
3108         dynamic_number_threads(image,image->columns,image->rows,1)
3109 #endif
3110       for (y=0; y < (ssize_t) image->rows; y++)
3111       {
3112         MagickBooleanType
3113           sync;
3114
3115         register Quantum
3116           *restrict q;
3117
3118         register ssize_t
3119           x;
3120
3121         if (status == MagickFalse)
3122           continue;
3123         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3124           exception);
3125         if (q == (Quantum *) NULL)
3126           {
3127             status=MagickFalse;
3128             continue;
3129           }
3130         for (x=0; x < (ssize_t) image->columns; x++)
3131         {
3132           register ssize_t
3133             i;
3134
3135           if ((GetPixelMask(image,q) != 0) ||
3136               (IsPixelGray(image,q) != MagickFalse))
3137             {
3138               q+=GetPixelChannels(image);
3139               continue;
3140             }
3141           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3142           {
3143             PixelChannel
3144               channel;
3145
3146             PixelTrait
3147               traits;
3148
3149             channel=GetPixelChannelMapChannel(image,i);
3150             traits=GetPixelChannelMapTraits(image,channel);
3151             if ((traits & UpdatePixelTrait) == 0)
3152               continue;
3153             q[i]=QuantumRange-q[i];
3154           }
3155           q+=GetPixelChannels(image);
3156         }
3157         sync=SyncCacheViewAuthenticPixels(image_view,exception);
3158         if (sync == MagickFalse)
3159           status=MagickFalse;
3160         if (image->progress_monitor != (MagickProgressMonitor) NULL)
3161           {
3162             MagickBooleanType
3163               proceed;
3164
3165 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3166             #pragma omp critical (MagickCore_NegateImage)
3167 #endif
3168             proceed=SetImageProgress(image,NegateImageTag,progress++,
3169               image->rows);
3170             if (proceed == MagickFalse)
3171               status=MagickFalse;
3172           }
3173       }
3174       image_view=DestroyCacheView(image_view);
3175       return(MagickTrue);
3176     }
3177   /*
3178     Negate image.
3179   */
3180 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3181   #pragma omp parallel for schedule(static) shared(progress,status) \
3182     dynamic_number_threads(image,image->columns,image->rows,1)
3183 #endif
3184   for (y=0; y < (ssize_t) image->rows; y++)
3185   {
3186     register Quantum
3187       *restrict q;
3188
3189     register ssize_t
3190       x;
3191
3192     if (status == MagickFalse)
3193       continue;
3194     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3195     if (q == (Quantum *) NULL)
3196       {
3197         status=MagickFalse;
3198         continue;
3199       }
3200     for (x=0; x < (ssize_t) image->columns; x++)
3201     {
3202       register ssize_t
3203         i;
3204
3205       if (GetPixelMask(image,q) != 0)
3206         {
3207           q+=GetPixelChannels(image);
3208           continue;
3209         }
3210       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3211       {
3212         PixelChannel
3213           channel;
3214
3215         PixelTrait
3216           traits;
3217
3218         channel=GetPixelChannelMapChannel(image,i);
3219         traits=GetPixelChannelMapTraits(image,channel);
3220         if ((traits & UpdatePixelTrait) == 0)
3221           continue;
3222         q[i]=QuantumRange-q[i];
3223       }
3224       q+=GetPixelChannels(image);
3225     }
3226     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3227       status=MagickFalse;
3228     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3229       {
3230         MagickBooleanType
3231           proceed;
3232
3233 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3234         #pragma omp critical (MagickCore_NegateImage)
3235 #endif
3236         proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3237         if (proceed == MagickFalse)
3238           status=MagickFalse;
3239       }
3240   }
3241   image_view=DestroyCacheView(image_view);
3242   return(status);
3243 }
3244 \f
3245 /*
3246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3247 %                                                                             %
3248 %                                                                             %
3249 %                                                                             %
3250 %     N o r m a l i z e I m a g e                                             %
3251 %                                                                             %
3252 %                                                                             %
3253 %                                                                             %
3254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3255 %
3256 %  The NormalizeImage() method enhances the contrast of a color image by
3257 %  mapping the darkest 2 percent of all pixel to black and the brightest
3258 %  1 percent to white.
3259 %
3260 %  The format of the NormalizeImage method is:
3261 %
3262 %      MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3263 %
3264 %  A description of each parameter follows:
3265 %
3266 %    o image: the image.
3267 %
3268 %    o exception: return any errors or warnings in this structure.
3269 %
3270 */
3271 MagickExport MagickBooleanType NormalizeImage(Image *image,
3272   ExceptionInfo *exception)
3273 {
3274   double
3275     black_point,
3276     white_point;
3277
3278   black_point=(double) image->columns*image->rows*0.0015;
3279   white_point=(double) image->columns*image->rows*0.9995;
3280   return(ContrastStretchImage(image,black_point,white_point,exception));
3281 }
3282 \f
3283 /*
3284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3285 %                                                                             %
3286 %                                                                             %
3287 %                                                                             %
3288 %     S i g m o i d a l C o n t r a s t I m a g e                             %
3289 %                                                                             %
3290 %                                                                             %
3291 %                                                                             %
3292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3293 %
3294 %  SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3295 %  sigmoidal contrast algorithm.  Increase the contrast of the image using a
3296 %  sigmoidal transfer function without saturating highlights or shadows.
3297 %  Contrast indicates how much to increase the contrast (0 is none; 3 is
3298 %  typical; 20 is pushing it); mid-point indicates where midtones fall in the
3299 %  resultant image (0 is white; 50% is middle-gray; 100% is black).  Set
3300 %  sharpen to MagickTrue to increase the image contrast otherwise the contrast
3301 %  is reduced.
3302 %
3303 %  The format of the SigmoidalContrastImage method is:
3304 %
3305 %      MagickBooleanType SigmoidalContrastImage(Image *image,
3306 %        const MagickBooleanType sharpen,const char *levels,
3307 %        ExceptionInfo *exception)
3308 %
3309 %  A description of each parameter follows:
3310 %
3311 %    o image: the image.
3312 %
3313 %    o sharpen: Increase or decrease image contrast.
3314 %
3315 %    o alpha: strength of the contrast, the larger the number the more
3316 %      'threshold-like' it becomes.
3317 %
3318 %    o beta: midpoint of the function as a color value 0 to QuantumRange.
3319 %
3320 %    o exception: return any errors or warnings in this structure.
3321 %
3322 */
3323 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3324   const MagickBooleanType sharpen,const double contrast,const double midpoint,
3325   ExceptionInfo *exception)
3326 {
3327 #define SigmoidalContrastImageTag  "SigmoidalContrast/Image"
3328
3329   CacheView
3330     *image_view;
3331
3332   MagickBooleanType
3333     status;
3334
3335   MagickOffsetType
3336     progress;
3337
3338   MagickRealType
3339     *sigmoidal_map;
3340
3341   register ssize_t
3342     i;
3343
3344   ssize_t
3345     y;
3346
3347   /*
3348     Allocate and initialize sigmoidal maps.
3349   */
3350   assert(image != (Image *) NULL);
3351   assert(image->signature == MagickSignature);
3352   if (image->debug != MagickFalse)
3353     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3354   sigmoidal_map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
3355     sizeof(*sigmoidal_map));
3356   if (sigmoidal_map == (MagickRealType *) NULL)
3357     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3358       image->filename);
3359   (void) ResetMagickMemory(sigmoidal_map,0,(MaxMap+1)*sizeof(*sigmoidal_map));
3360 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3361   #pragma omp parallel for schedule(static) shared(progress,status) \
3362     dynamic_number_threads(image,image->columns,1,1)
3363 #endif
3364   for (i=0; i <= (ssize_t) MaxMap; i++)
3365   {
3366     if (sharpen != MagickFalse)
3367       {
3368 #define sigmoidal(a,b,x)  (1/(1+exp((a)*((b)-(x)))))
3369 #if 1
3370         /* Simpilified function scaling,
3371          * with better 'contrast=0' or 'flatline' handling (greyscale)
3372          */
3373         double
3374           u0 = sigmoidal(contrast,QuantumScale*midpoint,0.0),
3375           u1 = sigmoidal(contrast,QuantumScale*midpoint,1.0);
3376         sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum(
3377            (MagickRealType)(MaxMap*(
3378                (sigmoidal(contrast,QuantumScale*midpoint,(double)i/MaxMap)
3379                   -(u0+u1)/2.0)/(u1-u0+MagickEpsilon)+0.5)   ));
3380 #else
3381         /* Scaled sigmoidal formula...
3382              (1/(1+exp(a*(b-u))) - 1/(1+exp(a))) /
3383                      (1/(1+exp(a*(b-1)))/(1+exp(a)))) */
3384         sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
3385           (MaxMap*((1.0/(1.0+exp(contrast*(midpoint/(double) QuantumRange-
3386           (double) i/MaxMap))))-(1.0/(1.0+exp(contrast*(midpoint/
3387           (double) QuantumRange)))))/((1.0/(1.0+exp(contrast*(midpoint/
3388           (double) QuantumRange-1.0))))-(1.0/(1.0+exp(contrast*(midpoint/
3389           (double) QuantumRange)))))+0.5));
3390 #endif
3391         continue;
3392       }
3393 #if 1
3394     {
3395       /* Inverse -- See
3396          http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html
3397       */
3398       double
3399         min = sigmoidal(contrast,1.0,0.0),
3400         max = sigmoidal(contrast,QuantumScale*midpoint,1.0),
3401         xi  = min+(double)i/MaxMap*(max-min);
3402       sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum(
3403          (MagickRealType)(MaxMap*(
3404              QuantumScale*midpoint-log((1-xi)/xi)/contrast) ));
3405     }
3406 #else
3407     /* expanded form of the above */
3408     sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
3409       (MaxMap*(QuantumScale*midpoint-log((1.0-(1.0/(1.0+exp(midpoint/
3410       (double) QuantumRange*contrast))+((double) i/MaxMap)*((1.0/
3411       (1.0+exp(contrast*(midpoint/(double) QuantumRange-1.0))))-(1.0/
3412       (1.0+exp(midpoint/(double) QuantumRange*contrast))))))/
3413       (1.0/(1.0+exp(midpoint/(double) QuantumRange*contrast))+
3414       ((double) i/MaxMap)*((1.0/(1.0+exp(contrast*(midpoint/
3415       (double) QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/
3416       (double) QuantumRange*contrast))))))/contrast)));
3417 #endif
3418   }
3419   if (image->storage_class == PseudoClass)
3420     {
3421       /*
3422         Sigmoidal-contrast enhance colormap.
3423       */
3424 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3425       #pragma omp parallel for schedule(static,4) shared(progress,status) \
3426         dynamic_number_threads(image,image->columns,1,1)
3427 #endif
3428       for (i=0; i < (ssize_t) image->colors; i++)
3429       {
3430         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3431           image->colormap[i].red=sigmoidal_map[ScaleQuantumToMap(
3432             ClampToQuantum(image->colormap[i].red))];
3433         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3434           image->colormap[i].green=sigmoidal_map[ScaleQuantumToMap(
3435             ClampToQuantum(image->colormap[i].green))];
3436         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3437           image->colormap[i].blue=sigmoidal_map[ScaleQuantumToMap(
3438             ClampToQuantum(image->colormap[i].blue))];
3439         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3440           image->colormap[i].alpha=sigmoidal_map[ScaleQuantumToMap(
3441             ClampToQuantum(image->colormap[i].alpha))];
3442       }
3443     }
3444   /*
3445     Sigmoidal-contrast enhance image.
3446   */
3447   status=MagickTrue;
3448   progress=0;
3449   image_view=AcquireAuthenticCacheView(image,exception);
3450 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3451   #pragma omp parallel for schedule(static,4) shared(progress,status) \
3452     dynamic_number_threads(image,image->columns,image->rows,1)
3453 #endif
3454   for (y=0; y < (ssize_t) image->rows; y++)
3455   {
3456     register Quantum
3457       *restrict q;
3458
3459     register ssize_t
3460       x;
3461
3462     if (status == MagickFalse)
3463       continue;
3464     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3465     if (q == (Quantum *) NULL)
3466       {
3467         status=MagickFalse;
3468         continue;
3469       }
3470     for (x=0; x < (ssize_t) image->columns; x++)
3471     {
3472       register ssize_t
3473         i;
3474
3475       if (GetPixelMask(image,q) != 0)
3476         {
3477           q+=GetPixelChannels(image);
3478           continue;
3479         }
3480       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3481       {
3482         PixelChannel
3483           channel;
3484
3485         PixelTrait
3486           traits;
3487
3488         channel=GetPixelChannelMapChannel(image,i);
3489         traits=GetPixelChannelMapTraits(image,channel);
3490         if ((traits & UpdatePixelTrait) == 0)
3491           continue;
3492         q[i]=ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(q[i])]);
3493       }
3494       q+=GetPixelChannels(image);
3495     }
3496     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3497       status=MagickFalse;
3498     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3499       {
3500         MagickBooleanType
3501           proceed;
3502
3503 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3504         #pragma omp critical (MagickCore_SigmoidalContrastImage)
3505 #endif
3506         proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3507           image->rows);
3508         if (proceed == MagickFalse)
3509           status=MagickFalse;
3510       }
3511   }
3512   image_view=DestroyCacheView(image_view);
3513   sigmoidal_map=(MagickRealType *) RelinquishMagickMemory(sigmoidal_map);
3514   return(status);
3515 }