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