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