2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Image Enhancement Methods %
20 % Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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/threshold.h"
78 #include "MagickCore/token.h"
79 #include "MagickCore/xml-tree.h"
80 #include "MagickCore/xml-tree-private.h"
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 % A u t o G a m m a I m a g e %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93 % AutoGammaImage() extract the 'mean' from the image and adjust the image
94 % to try make set its gamma appropriatally.
96 % The format of the AutoGammaImage method is:
98 % MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception)
100 % A description of each parameter follows:
102 % o image: The image to auto-level
104 % o exception: return any errors or warnings in this structure.
107 MagickExport MagickBooleanType AutoGammaImage(Image *image,
108 ExceptionInfo *exception)
123 if (image->channel_mask == DefaultChannels)
126 Apply gamma correction equally across all given channels.
128 (void) GetImageMean(image,&mean,&sans,exception);
129 gamma=log(mean*QuantumScale)/log_mean;
130 return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception));
133 Auto-gamma each channel separately.
136 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
141 PixelChannel channel=GetPixelChannelChannel(image,i);
142 PixelTrait traits=GetPixelChannelTraits(image,channel);
143 if ((traits & UpdatePixelTrait) == 0)
145 channel_mask=SetImageChannelMask(image,(ChannelType) (1 << i));
146 status=GetImageMean(image,&mean,&sans,exception);
147 gamma=log(mean*QuantumScale)/log_mean;
148 status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception);
149 (void) SetImageChannelMask(image,channel_mask);
150 if (status == MagickFalse)
153 return(status != 0 ? MagickTrue : MagickFalse);
157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
161 % A u t o L e v e l I m a g e %
165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167 % AutoLevelImage() adjusts the levels of a particular image channel by
168 % scaling the minimum and maximum values to the full quantum range.
170 % The format of the LevelImage method is:
172 % MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception)
174 % A description of each parameter follows:
176 % o image: The image to auto-level
178 % o exception: return any errors or warnings in this structure.
181 MagickExport MagickBooleanType AutoLevelImage(Image *image,
182 ExceptionInfo *exception)
184 return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
192 % B r i g h t n e s s C o n t r a s t I m a g e %
196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
198 % BrightnessContrastImage() changes the brightness and/or contrast of an
199 % image. It converts the brightness and contrast parameters into slope and
200 % intercept and calls a polynomical function to apply to the image.
202 % The format of the BrightnessContrastImage method is:
204 % MagickBooleanType BrightnessContrastImage(Image *image,
205 % const double brightness,const double contrast,ExceptionInfo *exception)
207 % A description of each parameter follows:
209 % o image: the image.
211 % o brightness: the brightness percent (-100 .. 100).
213 % o contrast: the contrast percent (-100 .. 100).
215 % o exception: return any errors or warnings in this structure.
218 MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
219 const double brightness,const double contrast,ExceptionInfo *exception)
221 #define BrightnessContastImageTag "BrightnessContast/Image"
233 Compute slope and intercept.
235 assert(image != (Image *) NULL);
236 assert(image->signature == MagickSignature);
237 if (image->debug != MagickFalse)
238 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
240 slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
243 intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
244 coefficients[0]=slope;
245 coefficients[1]=intercept;
246 status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255 % C l u t I m a g e %
259 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261 % ClutImage() replaces each color value in the given image, by using it as an
262 % index to lookup a replacement color value in a Color Look UP Table in the
263 % form of an image. The values are extracted along a diagonal of the CLUT
264 % image so either a horizontal or vertial gradient image can be used.
266 % Typically this is used to either re-color a gray-scale image according to a
267 % color gradient in the CLUT image, or to perform a freeform histogram
268 % (level) adjustment according to the (typically gray-scale) gradient in the
271 % When the 'channel' mask includes the matte/alpha transparency channel but
272 % one image has no such channel it is assumed that that image is a simple
273 % gray-scale image that will effect the alpha channel values, either for
274 % gray-scale coloring (with transparent or semi-transparent colors), or
275 % a histogram adjustment of existing alpha channel values. If both images
276 % have matte channels, direct and normal indexing is applied, which is rarely
279 % The format of the ClutImage method is:
281 % MagickBooleanType ClutImage(Image *image,Image *clut_image,
282 % const PixelInterpolateMethod method,ExceptionInfo *exception)
284 % A description of each parameter follows:
286 % o image: the image, which is replaced by indexed CLUT values
288 % o clut_image: the color lookup table image for replacement color values.
290 % o method: the pixel interpolation method.
292 % o exception: return any errors or warnings in this structure.
295 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image,
296 const PixelInterpolateMethod method,ExceptionInfo *exception)
298 #define ClutImageTag "Clut/Image"
319 assert(image != (Image *) NULL);
320 assert(image->signature == MagickSignature);
321 if (image->debug != MagickFalse)
322 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
323 assert(clut_image != (Image *) NULL);
324 assert(clut_image->signature == MagickSignature);
325 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
327 if (IsGrayColorspace(image->colorspace) != MagickFalse)
328 (void) TransformImageColorspace(image,RGBColorspace,exception);
329 clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*clut_map));
330 if (clut_map == (PixelInfo *) NULL)
331 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
338 adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
339 clut_view=AcquireVirtualCacheView(clut_image,exception);
340 for (i=0; i <= (ssize_t) MaxMap; i++)
342 GetPixelInfo(clut_image,clut_map+i);
343 (void) InterpolatePixelInfo(clut_image,clut_view,method,
344 QuantumScale*i*(clut_image->columns-adjust),QuantumScale*i*
345 (clut_image->rows-adjust),clut_map+i,exception);
347 clut_view=DestroyCacheView(clut_view);
348 image_view=AcquireAuthenticCacheView(image,exception);
349 #if defined(MAGICKCORE_OPENMP_SUPPORT)
350 #pragma omp parallel for schedule(static,4) shared(progress,status) \
351 magick_threads(image,image,image->rows,1)
353 for (y=0; y < (ssize_t) image->rows; y++)
364 if (status == MagickFalse)
366 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
367 if (q == (Quantum *) NULL)
372 GetPixelInfo(image,&pixel);
373 for (x=0; x < (ssize_t) image->columns; x++)
375 if (GetPixelMask(image,q) != 0)
377 q+=GetPixelChannels(image);
380 GetPixelInfoPixel(image,q,&pixel);
381 pixel.red=clut_map[ScaleQuantumToMap(
382 ClampToQuantum(pixel.red))].red;
383 pixel.green=clut_map[ScaleQuantumToMap(
384 ClampToQuantum(pixel.green))].green;
385 pixel.blue=clut_map[ScaleQuantumToMap(
386 ClampToQuantum(pixel.blue))].blue;
387 pixel.black=clut_map[ScaleQuantumToMap(
388 ClampToQuantum(pixel.black))].black;
389 pixel.alpha=clut_map[ScaleQuantumToMap(
390 ClampToQuantum(pixel.alpha))].alpha;
391 SetPixelInfoPixel(image,&pixel,q);
392 q+=GetPixelChannels(image);
394 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
396 if (image->progress_monitor != (MagickProgressMonitor) NULL)
401 #if defined(MAGICKCORE_OPENMP_SUPPORT)
402 #pragma omp critical (MagickCore_ClutImage)
404 proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
405 if (proceed == MagickFalse)
409 image_view=DestroyCacheView(image_view);
410 clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
411 if ((clut_image->alpha_trait == BlendPixelTrait) &&
412 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
413 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
418 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422 % C o l o r D e c i s i o n L i s t I m a g e %
426 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428 % ColorDecisionListImage() accepts a lightweight Color Correction Collection
429 % (CCC) file which solely contains one or more color corrections and applies
430 % the correction to the image. Here is a sample CCC file:
432 % <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
433 % <ColorCorrection id="cc03345">
435 % <Slope> 0.9 1.2 0.5 </Slope>
436 % <Offset> 0.4 -0.5 0.6 </Offset>
437 % <Power> 1.0 0.8 1.5 </Power>
440 % <Saturation> 0.85 </Saturation>
443 % </ColorCorrectionCollection>
445 % which includes the slop, offset, and power for each of the RGB channels
446 % as well as the saturation.
448 % The format of the ColorDecisionListImage method is:
450 % MagickBooleanType ColorDecisionListImage(Image *image,
451 % const char *color_correction_collection,ExceptionInfo *exception)
453 % A description of each parameter follows:
455 % o image: the image.
457 % o color_correction_collection: the color correction collection in XML.
459 % o exception: return any errors or warnings in this structure.
462 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
463 const char *color_correction_collection,ExceptionInfo *exception)
465 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
467 typedef struct _Correction
475 typedef struct _ColorCorrection
490 token[MaxTextExtent];
521 Allocate and initialize cdl maps.
523 assert(image != (Image *) NULL);
524 assert(image->signature == MagickSignature);
525 if (image->debug != MagickFalse)
526 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
527 if (color_correction_collection == (const char *) NULL)
529 ccc=NewXMLTree((const char *) color_correction_collection,exception);
530 if (ccc == (XMLTreeInfo *) NULL)
532 cc=GetXMLTreeChild(ccc,"ColorCorrection");
533 if (cc == (XMLTreeInfo *) NULL)
535 ccc=DestroyXMLTree(ccc);
538 color_correction.red.slope=1.0;
539 color_correction.red.offset=0.0;
540 color_correction.red.power=1.0;
541 color_correction.green.slope=1.0;
542 color_correction.green.offset=0.0;
543 color_correction.green.power=1.0;
544 color_correction.blue.slope=1.0;
545 color_correction.blue.offset=0.0;
546 color_correction.blue.power=1.0;
547 color_correction.saturation=0.0;
548 sop=GetXMLTreeChild(cc,"SOPNode");
549 if (sop != (XMLTreeInfo *) NULL)
556 slope=GetXMLTreeChild(sop,"Slope");
557 if (slope != (XMLTreeInfo *) NULL)
559 content=GetXMLTreeContent(slope);
560 p=(const char *) content;
561 for (i=0; (*p != '\0') && (i < 3); i++)
563 GetMagickToken(p,&p,token);
565 GetMagickToken(p,&p,token);
570 color_correction.red.slope=StringToDouble(token,(char **) NULL);
575 color_correction.green.slope=StringToDouble(token,
581 color_correction.blue.slope=StringToDouble(token,
588 offset=GetXMLTreeChild(sop,"Offset");
589 if (offset != (XMLTreeInfo *) NULL)
591 content=GetXMLTreeContent(offset);
592 p=(const char *) content;
593 for (i=0; (*p != '\0') && (i < 3); i++)
595 GetMagickToken(p,&p,token);
597 GetMagickToken(p,&p,token);
602 color_correction.red.offset=StringToDouble(token,
608 color_correction.green.offset=StringToDouble(token,
614 color_correction.blue.offset=StringToDouble(token,
621 power=GetXMLTreeChild(sop,"Power");
622 if (power != (XMLTreeInfo *) NULL)
624 content=GetXMLTreeContent(power);
625 p=(const char *) content;
626 for (i=0; (*p != '\0') && (i < 3); i++)
628 GetMagickToken(p,&p,token);
630 GetMagickToken(p,&p,token);
635 color_correction.red.power=StringToDouble(token,(char **) NULL);
640 color_correction.green.power=StringToDouble(token,
646 color_correction.blue.power=StringToDouble(token,
654 sat=GetXMLTreeChild(cc,"SATNode");
655 if (sat != (XMLTreeInfo *) NULL)
660 saturation=GetXMLTreeChild(sat,"Saturation");
661 if (saturation != (XMLTreeInfo *) NULL)
663 content=GetXMLTreeContent(saturation);
664 p=(const char *) content;
665 GetMagickToken(p,&p,token);
666 color_correction.saturation=StringToDouble(token,(char **) NULL);
669 ccc=DestroyXMLTree(ccc);
670 if (image->debug != MagickFalse)
672 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
673 " Color Correction Collection:");
674 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
675 " color_correction.red.slope: %g",color_correction.red.slope);
676 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
677 " color_correction.red.offset: %g",color_correction.red.offset);
678 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
679 " color_correction.red.power: %g",color_correction.red.power);
680 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
681 " color_correction.green.slope: %g",color_correction.green.slope);
682 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
683 " color_correction.green.offset: %g",color_correction.green.offset);
684 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
685 " color_correction.green.power: %g",color_correction.green.power);
686 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
687 " color_correction.blue.slope: %g",color_correction.blue.slope);
688 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
689 " color_correction.blue.offset: %g",color_correction.blue.offset);
690 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
691 " color_correction.blue.power: %g",color_correction.blue.power);
692 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
693 " color_correction.saturation: %g",color_correction.saturation);
695 cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
696 if (cdl_map == (PixelInfo *) NULL)
697 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
699 for (i=0; i <= (ssize_t) MaxMap; i++)
701 cdl_map[i].red=(double) ScaleMapToQuantum((double)
702 (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
703 color_correction.red.offset,color_correction.red.power))));
704 cdl_map[i].green=(double) ScaleMapToQuantum((double)
705 (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
706 color_correction.green.offset,color_correction.green.power))));
707 cdl_map[i].blue=(double) ScaleMapToQuantum((double)
708 (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
709 color_correction.blue.offset,color_correction.blue.power))));
711 if (image->storage_class == PseudoClass)
712 for (i=0; i < (ssize_t) image->colors; i++)
715 Apply transfer function to colormap.
720 luma=0.21267f*image->colormap[i].red+0.71526*image->colormap[i].green+
721 0.07217f*image->colormap[i].blue;
722 image->colormap[i].red=luma+color_correction.saturation*cdl_map[
723 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
724 image->colormap[i].green=luma+color_correction.saturation*cdl_map[
725 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
726 image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
727 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
730 Apply transfer function to image.
734 image_view=AcquireAuthenticCacheView(image,exception);
735 #if defined(MAGICKCORE_OPENMP_SUPPORT)
736 #pragma omp parallel for schedule(static,4) shared(progress,status) \
737 magick_threads(image,image,image->rows,1)
739 for (y=0; y < (ssize_t) image->rows; y++)
750 if (status == MagickFalse)
752 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
753 if (q == (Quantum *) NULL)
758 for (x=0; x < (ssize_t) image->columns; x++)
760 luma=0.21267f*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+
761 0.07217f*GetPixelBlue(image,q);
762 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
763 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
764 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
765 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
766 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
767 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
768 q+=GetPixelChannels(image);
770 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
772 if (image->progress_monitor != (MagickProgressMonitor) NULL)
777 #if defined(MAGICKCORE_OPENMP_SUPPORT)
778 #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
780 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
781 progress++,image->rows);
782 if (proceed == MagickFalse)
786 image_view=DestroyCacheView(image_view);
787 cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
796 % C o n t r a s t I m a g e %
800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802 % ContrastImage() enhances the intensity differences between the lighter and
803 % darker elements of the image. Set sharpen to a MagickTrue to increase the
804 % image contrast otherwise the contrast is reduced.
806 % The format of the ContrastImage method is:
808 % MagickBooleanType ContrastImage(Image *image,
809 % const MagickBooleanType sharpen,ExceptionInfo *exception)
811 % A description of each parameter follows:
813 % o image: the image.
815 % o sharpen: Increase or decrease image contrast.
817 % o exception: return any errors or warnings in this structure.
821 static void Contrast(const int sign,double *red,double *green,double *blue)
829 Enhance contrast: dark color become darker, light color become lighter.
831 assert(red != (double *) NULL);
832 assert(green != (double *) NULL);
833 assert(blue != (double *) NULL);
837 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
838 brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
840 if (brightness > 1.0)
843 if (brightness < 0.0)
845 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
848 MagickExport MagickBooleanType ContrastImage(Image *image,
849 const MagickBooleanType sharpen,ExceptionInfo *exception)
851 #define ContrastImageTag "Contrast/Image"
871 assert(image != (Image *) NULL);
872 assert(image->signature == MagickSignature);
873 if (image->debug != MagickFalse)
874 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
875 sign=sharpen != MagickFalse ? 1 : -1;
876 if (image->storage_class == PseudoClass)
879 Contrast enhance colormap.
881 for (i=0; i < (ssize_t) image->colors; i++)
888 Contrast(sign,&red,&green,&blue);
889 image->colormap[i].red=(MagickRealType) red;
890 image->colormap[i].red=(MagickRealType) red;
891 image->colormap[i].red=(MagickRealType) red;
895 Contrast enhance image.
899 image_view=AcquireAuthenticCacheView(image,exception);
900 #if defined(MAGICKCORE_OPENMP_SUPPORT)
901 #pragma omp parallel for schedule(static,4) shared(progress,status) \
902 magick_threads(image,image,image->rows,1)
904 for (y=0; y < (ssize_t) image->rows; y++)
917 if (status == MagickFalse)
919 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
920 if (q == (Quantum *) NULL)
925 for (x=0; x < (ssize_t) image->columns; x++)
927 red=(double) GetPixelRed(image,q);
928 green=(double) GetPixelGreen(image,q);
929 blue=(double) GetPixelBlue(image,q);
930 Contrast(sign,&red,&green,&blue);
931 SetPixelRed(image,ClampToQuantum(red),q);
932 SetPixelGreen(image,ClampToQuantum(green),q);
933 SetPixelBlue(image,ClampToQuantum(blue),q);
934 q+=GetPixelChannels(image);
936 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
938 if (image->progress_monitor != (MagickProgressMonitor) NULL)
943 #if defined(MAGICKCORE_OPENMP_SUPPORT)
944 #pragma omp critical (MagickCore_ContrastImage)
946 proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
947 if (proceed == MagickFalse)
951 image_view=DestroyCacheView(image_view);
956 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
960 % C o n t r a s t S t r e t c h I m a g e %
964 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
966 % ContrastStretchImage() is a simple image enhancement technique that attempts
967 % to improve the contrast in an image by 'stretching' the range of intensity
968 % values it contains to span a desired range of values. It differs from the
969 % more sophisticated histogram equalization in that it can only apply a
970 % linear scaling function to the image pixel values. As a result the
971 % 'enhancement' is less harsh.
973 % The format of the ContrastStretchImage method is:
975 % MagickBooleanType ContrastStretchImage(Image *image,
976 % const char *levels,ExceptionInfo *exception)
978 % A description of each parameter follows:
980 % o image: the image.
982 % o black_point: the black point.
984 % o white_point: the white point.
986 % o levels: Specify the levels where the black and white points have the
987 % range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
989 % o exception: return any errors or warnings in this structure.
992 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
993 const double black_point,const double white_point,ExceptionInfo *exception)
995 #define MaxRange(color) ((double) ScaleQuantumToMap((Quantum) (color)))
996 #define ContrastStretchImageTag "ContrastStretch/Image"
1026 Allocate histogram and stretch map.
1028 assert(image != (Image *) NULL);
1029 assert(image->signature == MagickSignature);
1030 if (image->debug != MagickFalse)
1031 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1032 black=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*black));
1033 white=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*white));
1034 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1035 sizeof(*histogram));
1036 stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1037 GetPixelChannels(image)*sizeof(*stretch_map));
1038 if ((black == (double *) NULL) || (white == (double *) NULL) ||
1039 (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1041 if (stretch_map != (double *) NULL)
1042 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1043 if (histogram != (double *) NULL)
1044 histogram=(double *) RelinquishMagickMemory(histogram);
1045 if (white != (double *) NULL)
1046 white=(double *) RelinquishMagickMemory(white);
1047 if (black != (double *) NULL)
1048 black=(double *) RelinquishMagickMemory(black);
1049 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1055 colorspace=image->colorspace;
1056 if (colorspace == sRGBColorspace)
1057 (void) SetImageColorspace(image,RGBColorspace,exception);
1059 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1060 sizeof(*histogram));
1061 image_view=AcquireVirtualCacheView(image,exception);
1062 for (y=0; y < (ssize_t) image->rows; y++)
1064 register const Quantum
1070 if (status == MagickFalse)
1072 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1073 if (p == (const Quantum *) NULL)
1078 for (x=0; x < (ssize_t) image->columns; x++)
1086 pixel=GetPixelIntensity(image,p);
1087 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1089 if (image->channel_mask != DefaultChannels)
1090 pixel=(double) p[i];
1091 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1092 ClampToQuantum(pixel))+i]++;
1094 p+=GetPixelChannels(image);
1097 image_view=DestroyCacheView(image_view);
1099 Find the histogram boundaries by locating the black/white levels.
1101 number_channels=GetPixelChannels(image);
1102 for (i=0; i < (ssize_t) number_channels; i++)
1111 white[i]=MaxRange(QuantumRange);
1113 for (j=0; j <= (ssize_t) MaxMap; j++)
1115 intensity+=histogram[GetPixelChannels(image)*j+i];
1116 if (intensity > black_point)
1119 black[i]=(double) j;
1121 for (j=(ssize_t) MaxMap; j != 0; j--)
1123 intensity+=histogram[GetPixelChannels(image)*j+i];
1124 if (intensity > ((double) image->columns*image->rows-white_point))
1127 white[i]=(double) j;
1129 histogram=(double *) RelinquishMagickMemory(histogram);
1131 Stretch the histogram to create the stretched image mapping.
1133 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1134 sizeof(*stretch_map));
1135 number_channels=GetPixelChannels(image);
1136 for (i=0; i < (ssize_t) number_channels; i++)
1141 for (j=0; j <= (ssize_t) MaxMap; j++)
1143 if (j < (ssize_t) black[i])
1144 stretch_map[GetPixelChannels(image)*j+i]=0.0;
1146 if (j > (ssize_t) white[i])
1147 stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange;
1149 if (black[i] != white[i])
1150 stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum(
1151 (double) (MaxMap*(j-black[i])/(white[i]-black[i])));
1154 if (image->storage_class == PseudoClass)
1160 Stretch-contrast colormap.
1162 for (j=0; j < (ssize_t) image->colors; j++)
1164 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1166 i=GetPixelChannelChannel(image,RedPixelChannel);
1167 if (black[i] != white[i])
1168 image->colormap[j].red=stretch_map[GetPixelChannels(image)*
1169 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+i;
1171 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1173 i=GetPixelChannelChannel(image,GreenPixelChannel);
1174 if (black[i] != white[i])
1175 image->colormap[j].green=stretch_map[GetPixelChannels(image)*
1176 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+i;
1178 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1180 i=GetPixelChannelChannel(image,BluePixelChannel);
1181 if (black[i] != white[i])
1182 image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
1183 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+i;
1185 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1187 i=GetPixelChannelChannel(image,AlphaPixelChannel);
1188 if (black[i] != white[i])
1189 image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
1190 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+i;
1195 Stretch-contrast image.
1199 image_view=AcquireAuthenticCacheView(image,exception);
1200 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1201 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1202 magick_threads(image,image,image->rows,1)
1204 for (y=0; y < (ssize_t) image->rows; y++)
1212 if (status == MagickFalse)
1214 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1215 if (q == (Quantum *) NULL)
1220 for (x=0; x < (ssize_t) image->columns; x++)
1225 if (GetPixelMask(image,q) != 0)
1227 q+=GetPixelChannels(image);
1230 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1232 PixelChannel channel=GetPixelChannelChannel(image,i);
1233 PixelTrait traits=GetPixelChannelTraits(image,channel);
1234 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1236 q[i]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1237 ScaleQuantumToMap(q[i])+i]);
1239 q+=GetPixelChannels(image);
1241 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1243 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1248 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1249 #pragma omp critical (MagickCore_ContrastStretchImage)
1251 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1253 if (proceed == MagickFalse)
1257 if (colorspace == sRGBColorspace)
1258 (void) SetImageColorspace(image,sRGBColorspace,exception);
1259 image_view=DestroyCacheView(image_view);
1260 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1261 white=(double *) RelinquishMagickMemory(white);
1262 black=(double *) RelinquishMagickMemory(black);
1267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1271 % E n h a n c e I m a g e %
1275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277 % EnhanceImage() applies a digital filter that improves the quality of a
1280 % The format of the EnhanceImage method is:
1282 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1284 % A description of each parameter follows:
1286 % o image: the image.
1288 % o exception: return any errors or warnings in this structure.
1291 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1293 #define EnhancePixel(weight) \
1294 mean=((double) r[i]+GetPixelChannel(enhance_image,channel,q))/2.0; \
1295 distance=(double) r[i]-(double) GetPixelChannel(enhance_image,channel,q); \
1296 distance_squared=QuantumScale*(2.0*((double) QuantumRange+1.0)+mean)* \
1297 distance*distance; \
1298 if (distance_squared < ((double) QuantumRange*(double) QuantumRange/25.0f)) \
1300 aggregate+=(weight)*r[i]; \
1301 total_weight+=(weight); \
1303 r+=GetPixelChannels(image);
1304 #define EnhanceImageTag "Enhance/Image"
1323 Initialize enhanced image attributes.
1325 assert(image != (const Image *) NULL);
1326 assert(image->signature == MagickSignature);
1327 if (image->debug != MagickFalse)
1328 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1329 assert(exception != (ExceptionInfo *) NULL);
1330 assert(exception->signature == MagickSignature);
1331 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1333 if (enhance_image == (Image *) NULL)
1334 return((Image *) NULL);
1335 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1337 enhance_image=DestroyImage(enhance_image);
1338 return((Image *) NULL);
1345 image_view=AcquireVirtualCacheView(image,exception);
1346 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1347 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1348 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1349 magick_threads(image,enhance_image,image->rows,1)
1351 for (y=0; y < (ssize_t) image->rows; y++)
1353 register const Quantum
1365 if (status == MagickFalse)
1367 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1368 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1370 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1375 center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1376 for (x=0; x < (ssize_t) image->columns; x++)
1381 if (GetPixelMask(image,p) != 0)
1383 p+=GetPixelChannels(image);
1384 q+=GetPixelChannels(enhance_image);
1387 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1396 register const Quantum
1399 PixelChannel channel=GetPixelChannelChannel(image,i);
1400 PixelTrait traits=GetPixelChannelTraits(image,channel);
1401 PixelTrait enhance_traits=GetPixelChannelTraits(enhance_image,channel);
1402 if ((traits == UndefinedPixelTrait) ||
1403 (enhance_traits == UndefinedPixelTrait))
1405 SetPixelChannel(enhance_image,channel,p[center+i],q);
1406 if ((enhance_traits & CopyPixelTrait) != 0)
1409 Compute weighted average of target pixel color components.
1414 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1415 EnhancePixel(8.0); EnhancePixel(5.0);
1416 r=p+1*GetPixelChannels(image)*(image->columns+4);
1417 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1418 EnhancePixel(20.0); EnhancePixel(8.0);
1419 r=p+2*GetPixelChannels(image)*(image->columns+4);
1420 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1421 EnhancePixel(40.0); EnhancePixel(10.0);
1422 r=p+3*GetPixelChannels(image)*(image->columns+4);
1423 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1424 EnhancePixel(20.0); EnhancePixel(8.0);
1425 r=p+4*GetPixelChannels(image)*(image->columns+4);
1426 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1427 EnhancePixel(8.0); EnhancePixel(5.0);
1428 SetPixelChannel(enhance_image,channel,ClampToQuantum(aggregate/
1431 p+=GetPixelChannels(image);
1432 q+=GetPixelChannels(enhance_image);
1434 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1436 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1441 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1442 #pragma omp critical (MagickCore_EnhanceImage)
1444 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1445 if (proceed == MagickFalse)
1449 enhance_view=DestroyCacheView(enhance_view);
1450 image_view=DestroyCacheView(image_view);
1451 if (status == MagickFalse)
1452 enhance_image=DestroyImage(enhance_image);
1453 return(enhance_image);
1457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1461 % E q u a l i z e I m a g e %
1465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1467 % EqualizeImage() applies a histogram equalization to the image.
1469 % The format of the EqualizeImage method is:
1471 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
1473 % A description of each parameter follows:
1475 % o image: the image.
1477 % o exception: return any errors or warnings in this structure.
1480 MagickExport MagickBooleanType EqualizeImage(Image *image,
1481 ExceptionInfo *exception)
1483 #define EqualizeImageTag "Equalize/Image"
1495 black[CompositePixelChannel],
1499 white[CompositePixelChannel];
1511 Allocate and initialize histogram arrays.
1513 assert(image != (Image *) NULL);
1514 assert(image->signature == MagickSignature);
1515 if (image->debug != MagickFalse)
1516 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1517 equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1518 GetPixelChannels(image)*sizeof(*equalize_map));
1519 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,
1520 GetPixelChannels(image)*sizeof(*histogram));
1521 map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1522 GetPixelChannels(image)*sizeof(*map));
1523 if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
1524 (map == (double *) NULL))
1526 if (map != (double *) NULL)
1527 map=(double *) RelinquishMagickMemory(map);
1528 if (histogram != (double *) NULL)
1529 histogram=(double *) RelinquishMagickMemory(histogram);
1530 if (equalize_map != (double *) NULL)
1531 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1532 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1539 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1540 sizeof(*histogram));
1541 image_view=AcquireVirtualCacheView(image,exception);
1542 for (y=0; y < (ssize_t) image->rows; y++)
1544 register const Quantum
1550 if (status == MagickFalse)
1552 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1553 if (p == (const Quantum *) NULL)
1558 for (x=0; x < (ssize_t) image->columns; x++)
1563 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1564 histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++;
1565 p+=GetPixelChannels(image);
1568 image_view=DestroyCacheView(image_view);
1570 Integrate the histogram to get the equalization map.
1572 number_channels=GetPixelChannels(image);
1573 for (i=0; i < (ssize_t) number_channels; i++)
1582 for (j=0; j <= (ssize_t) MaxMap; j++)
1584 intensity+=histogram[GetPixelChannels(image)*j+i];
1585 map[GetPixelChannels(image)*j+i]=intensity;
1588 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
1589 sizeof(*equalize_map));
1590 number_channels=GetPixelChannels(image);
1591 for (i=0; i < (ssize_t) number_channels; i++)
1597 white[i]=map[GetPixelChannels(image)*MaxMap+i];
1598 if (black[i] != white[i])
1599 for (j=0; j <= (ssize_t) MaxMap; j++)
1600 equalize_map[GetPixelChannels(image)*j+i]=(double)
1601 ScaleMapToQuantum((double) ((MaxMap*(map[
1602 GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
1604 histogram=(double *) RelinquishMagickMemory(histogram);
1605 map=(double *) RelinquishMagickMemory(map);
1606 if (image->storage_class == PseudoClass)
1614 for (j=0; j < (ssize_t) image->colors; j++)
1616 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1618 PixelChannel channel=GetPixelChannelChannel(image,RedPixelChannel);
1619 if (black[channel] != white[channel])
1620 image->colormap[j].red=equalize_map[GetPixelChannels(image)*
1621 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+
1624 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1626 PixelChannel channel=GetPixelChannelChannel(image,
1628 if (black[channel] != white[channel])
1629 image->colormap[j].green=equalize_map[GetPixelChannels(image)*
1630 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+
1633 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1635 PixelChannel channel=GetPixelChannelChannel(image,BluePixelChannel);
1636 if (black[channel] != white[channel])
1637 image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
1638 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+
1641 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1643 PixelChannel channel=GetPixelChannelChannel(image,
1645 if (black[channel] != white[channel])
1646 image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
1647 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+
1656 image_view=AcquireAuthenticCacheView(image,exception);
1657 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1658 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1659 magick_threads(image,image,image->rows,1)
1661 for (y=0; y < (ssize_t) image->rows; y++)
1669 if (status == MagickFalse)
1671 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1672 if (q == (Quantum *) NULL)
1677 for (x=0; x < (ssize_t) image->columns; x++)
1682 if (GetPixelMask(image,q) != 0)
1684 q+=GetPixelChannels(image);
1687 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1689 PixelChannel channel=GetPixelChannelChannel(image,i);
1690 PixelTrait traits=GetPixelChannelTraits(image,channel);
1691 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1693 q[i]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
1694 ScaleQuantumToMap(q[i])+i]);
1696 q+=GetPixelChannels(image);
1698 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1700 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1705 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1706 #pragma omp critical (MagickCore_EqualizeImage)
1708 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1709 if (proceed == MagickFalse)
1713 image_view=DestroyCacheView(image_view);
1714 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1723 % G a m m a I m a g e %
1727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1729 % GammaImage() gamma-corrects a particular image channel. The same
1730 % image viewed on different devices will have perceptual differences in the
1731 % way the image's intensities are represented on the screen. Specify
1732 % individual gamma levels for the red, green, and blue channels, or adjust
1733 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
1735 % You can also reduce the influence of a particular channel with a gamma
1738 % The format of the GammaImage method is:
1740 % MagickBooleanType GammaImage(Image *image,const double gamma,
1741 % ExceptionInfo *exception)
1743 % A description of each parameter follows:
1745 % o image: the image.
1747 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1749 % o gamma: the image gamma.
1752 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
1753 ExceptionInfo *exception)
1755 #define GammaCorrectImageTag "GammaCorrect/Image"
1776 Allocate and initialize gamma maps.
1778 assert(image != (Image *) NULL);
1779 assert(image->signature == MagickSignature);
1780 if (image->debug != MagickFalse)
1781 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1784 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1785 if (gamma_map == (Quantum *) NULL)
1786 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1788 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1790 for (i=0; i <= (ssize_t) MaxMap; i++)
1791 gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
1792 MaxMap,1.0/gamma)));
1793 if (image->storage_class == PseudoClass)
1794 for (i=0; i < (ssize_t) image->colors; i++)
1797 Gamma-correct colormap.
1799 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1800 image->colormap[i].red=(double) gamma_map[
1801 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))];
1802 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1803 image->colormap[i].green=(double) gamma_map[
1804 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))];
1805 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1806 image->colormap[i].blue=(double) gamma_map[
1807 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))];
1808 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1809 image->colormap[i].alpha=(double) gamma_map[
1810 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].alpha))];
1813 Gamma-correct image.
1817 image_view=AcquireAuthenticCacheView(image,exception);
1818 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1819 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1820 magick_threads(image,image,image->rows,1)
1822 for (y=0; y < (ssize_t) image->rows; y++)
1830 if (status == MagickFalse)
1832 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1833 if (q == (Quantum *) NULL)
1838 for (x=0; x < (ssize_t) image->columns; x++)
1843 if (GetPixelMask(image,q) != 0)
1845 q+=GetPixelChannels(image);
1848 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1850 PixelChannel channel=GetPixelChannelChannel(image,i);
1851 PixelTrait traits=GetPixelChannelTraits(image,channel);
1852 if ((traits & UpdatePixelTrait) == 0)
1854 q[i]=gamma_map[ScaleQuantumToMap(q[i])];
1856 q+=GetPixelChannels(image);
1858 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1860 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1865 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1866 #pragma omp critical (MagickCore_GammaImage)
1868 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
1870 if (proceed == MagickFalse)
1874 image_view=DestroyCacheView(image_view);
1875 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
1876 if (image->gamma != 0.0)
1877 image->gamma*=gamma;
1882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1886 % H a l d C l u t I m a g e %
1890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1892 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
1893 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
1894 % Create it with the HALD coder. You can apply any color transformation to
1895 % the Hald image and then use this method to apply the transform to the
1898 % The format of the HaldClutImage method is:
1900 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
1901 % ExceptionInfo *exception)
1903 % A description of each parameter follows:
1905 % o image: the image, which is replaced by indexed CLUT values
1907 % o hald_image: the color lookup table image for replacement color values.
1909 % o exception: return any errors or warnings in this structure.
1913 static inline size_t MagickMin(const size_t x,const size_t y)
1920 MagickExport MagickBooleanType HaldClutImage(Image *image,
1921 const Image *hald_image,ExceptionInfo *exception)
1923 #define HaldClutImageTag "Clut/Image"
1925 typedef struct _HaldInfo
1957 assert(image != (Image *) NULL);
1958 assert(image->signature == MagickSignature);
1959 if (image->debug != MagickFalse)
1960 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1961 assert(hald_image != (Image *) NULL);
1962 assert(hald_image->signature == MagickSignature);
1963 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1964 return(MagickFalse);
1965 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1966 (void) TransformImageColorspace(image,RGBColorspace,exception);
1967 if (image->alpha_trait != BlendPixelTrait)
1968 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1974 length=MagickMin(hald_image->columns,hald_image->rows);
1975 for (level=2; (level*level*level) < length; level++) ;
1977 cube_size=level*level;
1978 width=(double) hald_image->columns;
1979 GetPixelInfo(hald_image,&zero);
1980 hald_view=AcquireVirtualCacheView(hald_image,exception);
1981 image_view=AcquireAuthenticCacheView(image,exception);
1982 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1983 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1984 magick_threads(image,image,image->rows,1)
1986 for (y=0; y < (ssize_t) image->rows; y++)
1994 if (status == MagickFalse)
1996 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1997 if (q == (Quantum *) NULL)
2002 for (x=0; x < (ssize_t) image->columns; x++)
2017 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2018 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2019 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2020 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2021 point.x-=floor(point.x);
2022 point.y-=floor(point.y);
2023 point.z-=floor(point.z);
2025 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2026 fmod(offset,width),floor(offset/width),&pixel1,exception);
2028 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2029 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2031 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2034 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2035 fmod(offset,width),floor(offset/width),&pixel1,exception);
2036 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2037 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2039 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2042 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2044 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2045 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2046 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2047 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2048 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2049 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2050 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2051 (image->colorspace == CMYKColorspace))
2052 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2053 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2054 (image->alpha_trait == BlendPixelTrait))
2055 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2056 q+=GetPixelChannels(image);
2058 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2060 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2065 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2066 #pragma omp critical (MagickCore_HaldClutImage)
2068 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2069 if (proceed == MagickFalse)
2073 hald_view=DestroyCacheView(hald_view);
2074 image_view=DestroyCacheView(image_view);
2079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2083 % L e v e l I m a g e %
2087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2089 % LevelImage() adjusts the levels of a particular image channel by
2090 % scaling the colors falling between specified white and black points to
2091 % the full available quantum range.
2093 % The parameters provided represent the black, and white points. The black
2094 % point specifies the darkest color in the image. Colors darker than the
2095 % black point are set to zero. White point specifies the lightest color in
2096 % the image. Colors brighter than the white point are set to the maximum
2099 % If a '!' flag is given, map black and white colors to the given levels
2100 % rather than mapping those levels to black and white. See
2101 % LevelizeImage() below.
2103 % Gamma specifies a gamma correction to apply to the image.
2105 % The format of the LevelImage method is:
2107 % MagickBooleanType LevelImage(Image *image,const double black_point,
2108 % const double white_point,const double gamma,ExceptionInfo *exception)
2110 % A description of each parameter follows:
2112 % o image: the image.
2114 % o black_point: The level to map zero (black) to.
2116 % o white_point: The level to map QuantumRange (white) to.
2118 % o exception: return any errors or warnings in this structure.
2122 static inline double LevelPixel(const double black_point,
2123 const double white_point,const double gamma,const double pixel)
2129 scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0;
2130 level_pixel=(double) QuantumRange*pow(scale*((double) pixel-
2131 black_point),1.0/gamma);
2132 return(level_pixel);
2135 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2136 const double white_point,const double gamma,ExceptionInfo *exception)
2138 #define LevelImageTag "Level/Image"
2156 Allocate and initialize levels map.
2158 assert(image != (Image *) NULL);
2159 assert(image->signature == MagickSignature);
2160 if (image->debug != MagickFalse)
2161 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2162 if (image->storage_class == PseudoClass)
2163 for (i=0; i < (ssize_t) image->colors; i++)
2168 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2169 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2170 white_point,gamma,image->colormap[i].red));
2171 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2172 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2173 white_point,gamma,image->colormap[i].green));
2174 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2175 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2176 white_point,gamma,image->colormap[i].blue));
2177 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2178 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2179 white_point,gamma,image->colormap[i].alpha));
2186 image_view=AcquireAuthenticCacheView(image,exception);
2187 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2188 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2189 magick_threads(image,image,image->rows,1)
2191 for (y=0; y < (ssize_t) image->rows; y++)
2199 if (status == MagickFalse)
2201 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2202 if (q == (Quantum *) NULL)
2207 for (x=0; x < (ssize_t) image->columns; x++)
2212 if (GetPixelMask(image,q) != 0)
2214 q+=GetPixelChannels(image);
2217 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2219 PixelChannel channel=GetPixelChannelChannel(image,i);
2220 PixelTrait traits=GetPixelChannelTraits(image,channel);
2221 if ((traits & UpdatePixelTrait) == 0)
2223 q[i]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2226 q+=GetPixelChannels(image);
2228 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2230 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2235 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2236 #pragma omp critical (MagickCore_LevelImage)
2238 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2239 if (proceed == MagickFalse)
2243 image_view=DestroyCacheView(image_view);
2248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2252 % L e v e l i z e I m a g e %
2256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2258 % LevelizeImage() applies the reversed LevelImage() operation to just
2259 % the specific channels specified. It compresses the full range of color
2260 % values, so that they lie between the given black and white points. Gamma is
2261 % applied before the values are mapped.
2263 % LevelizeImage() can be called with by using a +level command line
2264 % API option, or using a '!' on a -level or LevelImage() geometry string.
2266 % It can be used to de-contrast a greyscale image to the exact levels
2267 % specified. Or by using specific levels for each channel of an image you
2268 % can convert a gray-scale image to any linear color gradient, according to
2271 % The format of the LevelizeImage method is:
2273 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
2274 % const double white_point,const double gamma,ExceptionInfo *exception)
2276 % A description of each parameter follows:
2278 % o image: the image.
2280 % o black_point: The level to map zero (black) to.
2282 % o white_point: The level to map QuantumRange (white) to.
2284 % o gamma: adjust gamma by this factor before mapping values.
2286 % o exception: return any errors or warnings in this structure.
2289 MagickExport MagickBooleanType LevelizeImage(Image *image,
2290 const double black_point,const double white_point,const double gamma,
2291 ExceptionInfo *exception)
2293 #define LevelizeImageTag "Levelize/Image"
2294 #define LevelizeValue(x) (ClampToQuantum((pow((double) (QuantumScale*(x)), \
2295 1.0/gamma))*(white_point-black_point)+black_point))
2313 Allocate and initialize levels map.
2315 assert(image != (Image *) NULL);
2316 assert(image->signature == MagickSignature);
2317 if (image->debug != MagickFalse)
2318 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2319 if (IsGrayColorspace(image->colorspace) != MagickFalse)
2320 (void) SetImageColorspace(image,RGBColorspace,exception);
2321 if (image->storage_class == PseudoClass)
2322 for (i=0; i < (ssize_t) image->colors; i++)
2327 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2328 image->colormap[i].red=(double) LevelizeValue(
2329 image->colormap[i].red);
2330 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2331 image->colormap[i].green=(double) LevelizeValue(
2332 image->colormap[i].green);
2333 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2334 image->colormap[i].blue=(double) LevelizeValue(
2335 image->colormap[i].blue);
2336 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2337 image->colormap[i].alpha=(double) LevelizeValue(
2338 image->colormap[i].alpha);
2345 image_view=AcquireAuthenticCacheView(image,exception);
2346 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2347 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2348 magick_threads(image,image,image->rows,1)
2350 for (y=0; y < (ssize_t) image->rows; y++)
2358 if (status == MagickFalse)
2360 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2361 if (q == (Quantum *) NULL)
2366 for (x=0; x < (ssize_t) image->columns; x++)
2371 if (GetPixelMask(image,q) != 0)
2373 q+=GetPixelChannels(image);
2376 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2378 PixelChannel channel=GetPixelChannelChannel(image,i);
2379 PixelTrait traits=GetPixelChannelTraits(image,channel);
2380 if ((traits & UpdatePixelTrait) == 0)
2382 q[i]=LevelizeValue(q[i]);
2384 q+=GetPixelChannels(image);
2386 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2388 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2393 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2394 #pragma omp critical (MagickCore_LevelizeImage)
2396 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2397 if (proceed == MagickFalse)
2401 image_view=DestroyCacheView(image_view);
2406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2410 % L e v e l I m a g e C o l o r s %
2414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2416 % LevelImageColors() maps the given color to "black" and "white" values,
2417 % linearly spreading out the colors, and level values on a channel by channel
2418 % bases, as per LevelImage(). The given colors allows you to specify
2419 % different level ranges for each of the color channels separately.
2421 % If the boolean 'invert' is set true the image values will modifyed in the
2422 % reverse direction. That is any existing "black" and "white" colors in the
2423 % image will become the color values given, with all other values compressed
2424 % appropriatally. This effectivally maps a greyscale gradient into the given
2427 % The format of the LevelImageColors method is:
2429 % MagickBooleanType LevelImageColors(Image *image,
2430 % const PixelInfo *black_color,const PixelInfo *white_color,
2431 % const MagickBooleanType invert,ExceptionInfo *exception)
2433 % A description of each parameter follows:
2435 % o image: the image.
2437 % o black_color: The color to map black to/from
2439 % o white_point: The color to map white to/from
2441 % o invert: if true map the colors (levelize), rather than from (level)
2443 % o exception: return any errors or warnings in this structure.
2446 MagickExport MagickBooleanType LevelImageColors(Image *image,
2447 const PixelInfo *black_color,const PixelInfo *white_color,
2448 const MagickBooleanType invert,ExceptionInfo *exception)
2457 Allocate and initialize levels map.
2459 assert(image != (Image *) NULL);
2460 assert(image->signature == MagickSignature);
2461 if (image->debug != MagickFalse)
2462 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2464 if (invert == MagickFalse)
2466 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2468 channel_mask=SetImageChannelMask(image,RedChannel);
2469 status|=LevelImage(image,black_color->red,white_color->red,1.0,
2471 (void) SetImageChannelMask(image,channel_mask);
2473 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2475 channel_mask=SetImageChannelMask(image,GreenChannel);
2476 status|=LevelImage(image,black_color->green,white_color->green,1.0,
2478 (void) SetImageChannelMask(image,channel_mask);
2480 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2482 channel_mask=SetImageChannelMask(image,BlueChannel);
2483 status|=LevelImage(image,black_color->blue,white_color->blue,1.0,
2485 (void) SetImageChannelMask(image,channel_mask);
2487 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2488 (image->colorspace == CMYKColorspace))
2490 channel_mask=SetImageChannelMask(image,BlackChannel);
2491 status|=LevelImage(image,black_color->black,white_color->black,1.0,
2493 (void) SetImageChannelMask(image,channel_mask);
2495 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2496 (image->alpha_trait == BlendPixelTrait))
2498 channel_mask=SetImageChannelMask(image,AlphaChannel);
2499 status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2501 (void) SetImageChannelMask(image,channel_mask);
2506 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2508 channel_mask=SetImageChannelMask(image,RedChannel);
2509 status|=LevelizeImage(image,black_color->red,white_color->red,1.0,
2511 (void) SetImageChannelMask(image,channel_mask);
2513 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2515 channel_mask=SetImageChannelMask(image,GreenChannel);
2516 status|=LevelizeImage(image,black_color->green,white_color->green,1.0,
2518 (void) SetImageChannelMask(image,channel_mask);
2520 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2522 channel_mask=SetImageChannelMask(image,BlueChannel);
2523 status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2525 (void) SetImageChannelMask(image,channel_mask);
2527 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2528 (image->colorspace == CMYKColorspace))
2530 channel_mask=SetImageChannelMask(image,BlackChannel);
2531 status|=LevelizeImage(image,black_color->black,white_color->black,1.0,
2533 (void) SetImageChannelMask(image,channel_mask);
2535 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2536 (image->alpha_trait == BlendPixelTrait))
2538 channel_mask=SetImageChannelMask(image,AlphaChannel);
2539 status|=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2541 (void) SetImageChannelMask(image,channel_mask);
2544 return(status == 0 ? MagickFalse : MagickTrue);
2548 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2552 % L i n e a r S t r e t c h I m a g e %
2556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2558 % LinearStretchImage() discards any pixels below the black point and above
2559 % the white point and levels the remaining pixels.
2561 % The format of the LinearStretchImage method is:
2563 % MagickBooleanType LinearStretchImage(Image *image,
2564 % const double black_point,const double white_point,
2565 % ExceptionInfo *exception)
2567 % A description of each parameter follows:
2569 % o image: the image.
2571 % o black_point: the black point.
2573 % o white_point: the white point.
2575 % o exception: return any errors or warnings in this structure.
2578 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2579 const double black_point,const double white_point,ExceptionInfo *exception)
2581 #define LinearStretchImageTag "LinearStretch/Image"
2599 Allocate histogram and linear map.
2601 assert(image != (Image *) NULL);
2602 assert(image->signature == MagickSignature);
2603 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
2604 if (histogram == (double *) NULL)
2605 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2610 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2611 image_view=AcquireVirtualCacheView(image,exception);
2612 for (y=0; y < (ssize_t) image->rows; y++)
2614 register const Quantum
2620 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2621 if (p == (const Quantum *) NULL)
2623 for (x=0; x < (ssize_t) image->columns; x++)
2628 intensity=GetPixelIntensity(image,p);
2629 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2630 p+=GetPixelChannels(image);
2633 image_view=DestroyCacheView(image_view);
2635 Find the histogram boundaries by locating the black and white point levels.
2638 for (black=0; black < (ssize_t) MaxMap; black++)
2640 intensity+=histogram[black];
2641 if (intensity >= black_point)
2645 for (white=(ssize_t) MaxMap; white != 0; white--)
2647 intensity+=histogram[white];
2648 if (intensity >= white_point)
2651 histogram=(double *) RelinquishMagickMemory(histogram);
2652 status=LevelImage(image,(double) black,(double) white,1.0,exception);
2657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2661 % M o d u l a t e I m a g e %
2665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2667 % ModulateImage() lets you control the brightness, saturation, and hue
2668 % of an image. Modulate represents the brightness, saturation, and hue
2669 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
2670 % modulation is lightness, saturation, and hue. For HWB, use blackness,
2671 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
2673 % The format of the ModulateImage method is:
2675 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
2676 % ExceptionInfo *exception)
2678 % A description of each parameter follows:
2680 % o image: the image.
2682 % o modulate: Define the percent change in brightness, saturation, and hue.
2684 % o exception: return any errors or warnings in this structure.
2688 static inline void ModulateHCL(const double percent_hue,
2689 const double percent_chroma,const double percent_luma,double *red,
2690 double *green,double *blue)
2698 Increase or decrease color luma, chroma, or hue.
2700 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2701 hue+=0.5*(0.01*percent_hue-1.0);
2706 chroma*=0.01*percent_chroma;
2707 luma*=0.01*percent_luma;
2708 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2711 static inline void ModulateHSB(const double percent_hue,
2712 const double percent_saturation,const double percent_brightness,double *red,
2713 double *green,double *blue)
2721 Increase or decrease color brightness, saturation, or hue.
2723 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2724 hue+=0.5*(0.01*percent_hue-1.0);
2729 saturation*=0.01*percent_saturation;
2730 brightness*=0.01*percent_brightness;
2731 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2734 static inline void ModulateHSL(const double percent_hue,
2735 const double percent_saturation,const double percent_lightness,double *red,
2736 double *green,double *blue)
2744 Increase or decrease color lightness, saturation, or hue.
2746 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
2747 hue+=0.5*(0.01*percent_hue-1.0);
2752 saturation*=0.01*percent_saturation;
2753 lightness*=0.01*percent_lightness;
2754 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
2757 static inline void ModulateHWB(const double percent_hue,
2758 const double percent_whiteness,const double percent_blackness,double *red,
2759 double *green,double *blue)
2767 Increase or decrease color blackness, whiteness, or hue.
2769 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
2770 hue+=0.5*(0.01*percent_hue-1.0);
2775 blackness*=0.01*percent_blackness;
2776 whiteness*=0.01*percent_whiteness;
2777 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
2780 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
2781 ExceptionInfo *exception)
2783 #define ModulateImageTag "Modulate/Image"
2818 Initialize modulate table.
2820 assert(image != (Image *) NULL);
2821 assert(image->signature == MagickSignature);
2822 if (image->debug != MagickFalse)
2823 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2824 if (modulate == (char *) NULL)
2825 return(MagickFalse);
2826 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
2827 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2828 flags=ParseGeometry(modulate,&geometry_info);
2829 percent_brightness=geometry_info.rho;
2830 percent_saturation=geometry_info.sigma;
2831 if ((flags & SigmaValue) == 0)
2832 percent_saturation=100.0;
2833 percent_hue=geometry_info.xi;
2834 if ((flags & XiValue) == 0)
2836 colorspace=UndefinedColorspace;
2837 artifact=GetImageArtifact(image,"modulate:colorspace");
2838 if (artifact != (const char *) NULL)
2839 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
2840 MagickFalse,artifact);
2841 if (image->storage_class == PseudoClass)
2842 for (i=0; i < (ssize_t) image->colors; i++)
2852 red=image->colormap[i].red;
2853 green=image->colormap[i].green;
2854 blue=image->colormap[i].blue;
2859 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
2865 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2872 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2878 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2889 image_view=AcquireAuthenticCacheView(image,exception);
2890 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2891 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2892 magick_threads(image,image,image->rows,1)
2894 for (y=0; y < (ssize_t) image->rows; y++)
2902 if (status == MagickFalse)
2904 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2905 if (q == (Quantum *) NULL)
2910 for (x=0; x < (ssize_t) image->columns; x++)
2917 red=(double) GetPixelRed(image,q);
2918 green=(double) GetPixelGreen(image,q);
2919 blue=(double) GetPixelBlue(image,q);
2924 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
2930 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2937 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2943 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2948 SetPixelRed(image,ClampToQuantum(red),q);
2949 SetPixelGreen(image,ClampToQuantum(green),q);
2950 SetPixelBlue(image,ClampToQuantum(blue),q);
2951 q+=GetPixelChannels(image);
2953 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2955 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2960 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2961 #pragma omp critical (MagickCore_ModulateImage)
2963 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
2964 if (proceed == MagickFalse)
2968 image_view=DestroyCacheView(image_view);
2973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2977 % N e g a t e I m a g e %
2981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2983 % NegateImage() negates the colors in the reference image. The grayscale
2984 % option means that only grayscale values within the image are negated.
2986 % The format of the NegateImage method is:
2988 % MagickBooleanType NegateImage(Image *image,
2989 % const MagickBooleanType grayscale,ExceptionInfo *exception)
2991 % A description of each parameter follows:
2993 % o image: the image.
2995 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
2997 % o exception: return any errors or warnings in this structure.
3000 MagickExport MagickBooleanType NegateImage(Image *image,
3001 const MagickBooleanType grayscale,ExceptionInfo *exception)
3003 #define NegateImageTag "Negate/Image"
3020 assert(image != (Image *) NULL);
3021 assert(image->signature == MagickSignature);
3022 if (image->debug != MagickFalse)
3023 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3024 if (image->storage_class == PseudoClass)
3025 for (i=0; i < (ssize_t) image->colors; i++)
3030 if (grayscale != MagickFalse)
3031 if ((image->colormap[i].red != image->colormap[i].green) ||
3032 (image->colormap[i].green != image->colormap[i].blue))
3034 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3035 image->colormap[i].red=QuantumRange-image->colormap[i].red;
3036 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3037 image->colormap[i].green=QuantumRange-image->colormap[i].green;
3038 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3039 image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3046 image_view=AcquireAuthenticCacheView(image,exception);
3047 if (grayscale != MagickFalse)
3049 for (y=0; y < (ssize_t) image->rows; y++)
3060 if (status == MagickFalse)
3062 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3064 if (q == (Quantum *) NULL)
3069 for (x=0; x < (ssize_t) image->columns; x++)
3074 if ((GetPixelMask(image,q) != 0) ||
3075 (IsPixelGray(image,q) != MagickFalse))
3077 q+=GetPixelChannels(image);
3080 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3082 PixelChannel channel=GetPixelChannelChannel(image,i);
3083 PixelTrait traits=GetPixelChannelTraits(image,channel);
3084 if ((traits & UpdatePixelTrait) == 0)
3086 q[i]=QuantumRange-q[i];
3088 q+=GetPixelChannels(image);
3090 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3091 if (sync == MagickFalse)
3093 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3098 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3099 #pragma omp critical (MagickCore_NegateImage)
3101 proceed=SetImageProgress(image,NegateImageTag,progress++,
3103 if (proceed == MagickFalse)
3107 image_view=DestroyCacheView(image_view);
3113 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3114 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3115 magick_threads(image,image,image->rows,1)
3117 for (y=0; y < (ssize_t) image->rows; y++)
3125 if (status == MagickFalse)
3127 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3128 if (q == (Quantum *) NULL)
3133 for (x=0; x < (ssize_t) image->columns; x++)
3138 if (GetPixelMask(image,q) != 0)
3140 q+=GetPixelChannels(image);
3143 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3145 PixelChannel channel=GetPixelChannelChannel(image,i);
3146 PixelTrait traits=GetPixelChannelTraits(image,channel);
3147 if ((traits & UpdatePixelTrait) == 0)
3149 q[i]=QuantumRange-q[i];
3151 q+=GetPixelChannels(image);
3153 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3155 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3160 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3161 #pragma omp critical (MagickCore_NegateImage)
3163 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3164 if (proceed == MagickFalse)
3168 image_view=DestroyCacheView(image_view);
3173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3177 % N o r m a l i z e I m a g e %
3181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3183 % The NormalizeImage() method enhances the contrast of a color image by
3184 % mapping the darkest 2 percent of all pixel to black and the brightest
3185 % 1 percent to white.
3187 % The format of the NormalizeImage method is:
3189 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3191 % A description of each parameter follows:
3193 % o image: the image.
3195 % o exception: return any errors or warnings in this structure.
3198 MagickExport MagickBooleanType NormalizeImage(Image *image,
3199 ExceptionInfo *exception)
3205 black_point=(double) image->columns*image->rows*0.0015;
3206 white_point=(double) image->columns*image->rows*0.9995;
3207 return(ContrastStretchImage(image,black_point,white_point,exception));
3211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3215 % S i g m o i d a l C o n t r a s t I m a g e %
3219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3222 % sigmoidal contrast algorithm. Increase the contrast of the image using a
3223 % sigmoidal transfer function without saturating highlights or shadows.
3224 % Contrast indicates how much to increase the contrast (0 is none; 3 is
3225 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
3226 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
3227 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
3230 % The format of the SigmoidalContrastImage method is:
3232 % MagickBooleanType SigmoidalContrastImage(Image *image,
3233 % const MagickBooleanType sharpen,const char *levels,
3234 % ExceptionInfo *exception)
3236 % A description of each parameter follows:
3238 % o image: the image.
3240 % o sharpen: Increase or decrease image contrast.
3242 % o contrast: strength of the contrast, the larger the number the more
3243 % 'threshold-like' it becomes.
3245 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3247 % o exception: return any errors or warnings in this structure.
3252 ImageMagick 6 has a version of this function which uses LUTs.
3256 Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
3259 The first version, based on the hyperbolic tangent tanh, when combined with
3260 the scaling step, is an exact arithmetic clone of the the sigmoid function
3261 based on the logistic curve. The equivalence is based on the identity
3263 1/(1+exp(-t)) = (1+tanh(t/2))/2
3265 (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3266 scaled sigmoidal derivation is invariant under affine transformations of
3269 The tanh version is almost certainly more accurate and cheaper. The 0.5
3270 factor in the argument is to clone the legacy ImageMagick behavior. The
3271 reason for making the define depend on atanh even though it only uses tanh
3272 has to do with the construction of the inverse of the scaled sigmoidal.
3274 #if defined(MAGICKCORE_HAVE_ATANH)
3275 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3277 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3280 Scaled sigmoidal function:
3282 ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3283 ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3285 See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
3286 http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
3287 of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
3288 zero. This is fixed below by exiting immediately when contrast is small,
3289 leaving the image (or colormap) unmodified. This appears to be safe because
3290 the series expansion of the logistic sigmoidal function around x=b is
3294 so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3296 #define ScaledSigmoidal(a,b,x) ( \
3297 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3298 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3300 Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
3301 may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
3302 sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
3303 when creating a LUT from in gamut values, hence the branching. In
3304 addition, HDRI may have out of gamut values.
3305 InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
3306 It is only a right inverse. This is unavoidable.
3308 static inline double InverseScaledSigmoidal(const double a,const double b,
3311 const double sig0=Sigmoidal(a,b,0.0);
3312 const double sig1=Sigmoidal(a,b,1.0);
3313 const double argument=(sig1-sig0)*x+sig0;
3314 const double clamped=
3316 #if defined(MAGICKCORE_HAVE_ATANH)
3317 argument < -1+MagickEpsilon
3321 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3323 return(b+(2.0/a)*atanh(clamped));
3325 argument < MagickEpsilon
3329 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3331 return(b-log(1.0/clamped-1.0)/a);
3335 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3336 const MagickBooleanType sharpen,const double contrast,const double midpoint,
3337 ExceptionInfo *exception)
3339 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
3340 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3341 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3342 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3343 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3360 assert(image != (Image *) NULL);
3361 assert(image->signature == MagickSignature);
3362 if (image->debug != MagickFalse)
3363 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3365 Side effect: may clamp values unless contrast<MagickEpsilon, in which
3366 case nothing is done.
3368 if (contrast < MagickEpsilon)
3371 Sigmoidal-contrast enhance colormap.
3373 if (image->storage_class == PseudoClass)
3378 if (sharpen != MagickFalse)
3379 for (i=0; i < (ssize_t) image->colors; i++)
3381 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3382 image->colormap[i].red=ScaledSig(image->colormap[i].red);
3383 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3384 image->colormap[i].green=ScaledSig(image->colormap[i].green);
3385 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3386 image->colormap[i].blue=ScaledSig(image->colormap[i].blue);
3387 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3388 image->colormap[i].alpha=ScaledSig(image->colormap[i].alpha);
3391 for (i=0; i < (ssize_t) image->colors; i++)
3393 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3394 image->colormap[i].red=InverseScaledSig(image->colormap[i].red);
3395 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3396 image->colormap[i].green=InverseScaledSig(image->colormap[i].green);
3397 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3398 image->colormap[i].blue=InverseScaledSig(image->colormap[i].blue);
3399 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3400 image->colormap[i].alpha=InverseScaledSig(image->colormap[i].alpha);
3404 Sigmoidal-contrast enhance image.
3408 image_view=AcquireAuthenticCacheView(image,exception);
3409 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3410 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3411 magick_threads(image,image,image->rows,1)
3413 for (y=0; y < (ssize_t) image->rows; y++)
3421 if (status == MagickFalse)
3423 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3424 if (q == (Quantum *) NULL)
3429 for (x=0; x < (ssize_t) image->columns; x++)
3434 if (GetPixelMask(image,q) != 0)
3436 q+=GetPixelChannels(image);
3439 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3441 PixelChannel channel=GetPixelChannelChannel(image,i);
3442 PixelTrait traits=GetPixelChannelTraits(image,channel);
3443 if ((traits & UpdatePixelTrait) == 0)
3445 if (sharpen != MagickFalse)
3446 q[i]=ScaledSig(q[i]);
3448 q[i]=InverseScaledSig(q[i]);
3450 q+=GetPixelChannels(image);
3452 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3454 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3459 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3460 #pragma omp critical (MagickCore_SigmoidalContrastImage)
3462 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3464 if (proceed == MagickFalse)
3468 image_view=DestroyCacheView(image_view);