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