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