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