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-2012 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++)
147 channel=GetPixelChannelChannel(image,i);
148 traits=GetPixelChannelTraits(image,channel);
149 if ((traits & UpdatePixelTrait) == 0)
151 channel_mask=SetImageChannelMask(image,(ChannelType) (1 << i));
152 status=GetImageMean(image,&mean,&sans,exception);
153 gamma=log(mean*QuantumScale)/log_mean;
154 status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception);
155 (void) SetImageChannelMask(image,channel_mask);
156 if (status == MagickFalse)
159 return(status != 0 ? MagickTrue : MagickFalse);
163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167 % A u t o L e v e l I m a g e %
171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
173 % AutoLevelImage() adjusts the levels of a particular image channel by
174 % scaling the minimum and maximum values to the full quantum range.
176 % The format of the LevelImage method is:
178 % MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception)
180 % A description of each parameter follows:
182 % o image: The image to auto-level
184 % o exception: return any errors or warnings in this structure.
187 MagickExport MagickBooleanType AutoLevelImage(Image *image,
188 ExceptionInfo *exception)
190 return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
198 % B r i g h t n e s s C o n t r a s t I m a g e %
202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
204 % BrightnessContrastImage() changes the brightness and/or contrast of an
205 % image. It converts the brightness and contrast parameters into slope and
206 % intercept and calls a polynomical function to apply to the image.
208 % The format of the BrightnessContrastImage method is:
210 % MagickBooleanType BrightnessContrastImage(Image *image,
211 % const double brightness,const double contrast,ExceptionInfo *exception)
213 % A description of each parameter follows:
215 % o image: the image.
217 % o brightness: the brightness percent (-100 .. 100).
219 % o contrast: the contrast percent (-100 .. 100).
221 % o exception: return any errors or warnings in this structure.
224 MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
225 const double brightness,const double contrast,ExceptionInfo *exception)
227 #define BrightnessContastImageTag "BrightnessContast/Image"
239 Compute slope and intercept.
241 assert(image != (Image *) NULL);
242 assert(image->signature == MagickSignature);
243 if (image->debug != MagickFalse)
244 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
246 slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
249 intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
250 coefficients[0]=slope;
251 coefficients[1]=intercept;
252 status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261 % C l u t I m a g e %
265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
267 % ClutImage() replaces each color value in the given image, by using it as an
268 % index to lookup a replacement color value in a Color Look UP Table in the
269 % form of an image. The values are extracted along a diagonal of the CLUT
270 % image so either a horizontal or vertial gradient image can be used.
272 % Typically this is used to either re-color a gray-scale image according to a
273 % color gradient in the CLUT image, or to perform a freeform histogram
274 % (level) adjustment according to the (typically gray-scale) gradient in the
277 % When the 'channel' mask includes the matte/alpha transparency channel but
278 % one image has no such channel it is assumed that that image is a simple
279 % gray-scale image that will effect the alpha channel values, either for
280 % gray-scale coloring (with transparent or semi-transparent colors), or
281 % a histogram adjustment of existing alpha channel values. If both images
282 % have matte channels, direct and normal indexing is applied, which is rarely
285 % The format of the ClutImage method is:
287 % MagickBooleanType ClutImage(Image *image,Image *clut_image,
288 % const PixelInterpolateMethod method,ExceptionInfo *exception)
290 % A description of each parameter follows:
292 % o image: the image, which is replaced by indexed CLUT values
294 % o clut_image: the color lookup table image for replacement color values.
296 % o method: the pixel interpolation method.
298 % o exception: return any errors or warnings in this structure.
301 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image,
302 const PixelInterpolateMethod method,ExceptionInfo *exception)
304 #define ClutImageTag "Clut/Image"
325 assert(image != (Image *) NULL);
326 assert(image->signature == MagickSignature);
327 if (image->debug != MagickFalse)
328 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
329 assert(clut_image != (Image *) NULL);
330 assert(clut_image->signature == MagickSignature);
331 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
333 if (IsGrayColorspace(image->colorspace) != MagickFalse)
334 (void) TransformImageColorspace(image,RGBColorspace,exception);
335 clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*clut_map));
336 if (clut_map == (PixelInfo *) NULL)
337 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
344 adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
345 clut_view=AcquireVirtualCacheView(clut_image,exception);
346 for (i=0; i <= (ssize_t) MaxMap; i++)
348 GetPixelInfo(clut_image,clut_map+i);
349 (void) InterpolatePixelInfo(clut_image,clut_view,method,
350 QuantumScale*i*(clut_image->columns-adjust),QuantumScale*i*
351 (clut_image->rows-adjust),clut_map+i,exception);
353 clut_view=DestroyCacheView(clut_view);
354 image_view=AcquireAuthenticCacheView(image,exception);
355 #if defined(MAGICKCORE_OPENMP_SUPPORT)
356 #pragma omp parallel for schedule(static,4) shared(progress,status) \
357 dynamic_number_threads(image,image->columns,image->rows,1)
359 for (y=0; y < (ssize_t) image->rows; y++)
370 if (status == MagickFalse)
372 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
373 if (q == (Quantum *) NULL)
378 GetPixelInfo(image,&pixel);
379 for (x=0; x < (ssize_t) image->columns; x++)
381 if (GetPixelMask(image,q) != 0)
383 q+=GetPixelChannels(image);
386 GetPixelInfoPixel(image,q,&pixel);
387 pixel.red=clut_map[ScaleQuantumToMap(
388 ClampToQuantum(pixel.red))].red;
389 pixel.green=clut_map[ScaleQuantumToMap(
390 ClampToQuantum(pixel.green))].green;
391 pixel.blue=clut_map[ScaleQuantumToMap(
392 ClampToQuantum(pixel.blue))].blue;
393 pixel.black=clut_map[ScaleQuantumToMap(
394 ClampToQuantum(pixel.black))].black;
395 pixel.alpha=clut_map[ScaleQuantumToMap(
396 ClampToQuantum(pixel.alpha))].alpha;
397 SetPixelInfoPixel(image,&pixel,q);
398 q+=GetPixelChannels(image);
400 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
402 if (image->progress_monitor != (MagickProgressMonitor) NULL)
407 #if defined(MAGICKCORE_OPENMP_SUPPORT)
408 #pragma omp critical (MagickCore_ClutImage)
410 proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
411 if (proceed == MagickFalse)
415 image_view=DestroyCacheView(image_view);
416 clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
417 if ((clut_image->alpha_trait == BlendPixelTrait) &&
418 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
419 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428 % C o l o r D e c i s i o n L i s t I m a g e %
432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434 % ColorDecisionListImage() accepts a lightweight Color Correction Collection
435 % (CCC) file which solely contains one or more color corrections and applies
436 % the correction to the image. Here is a sample CCC file:
438 % <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
439 % <ColorCorrection id="cc03345">
441 % <Slope> 0.9 1.2 0.5 </Slope>
442 % <Offset> 0.4 -0.5 0.6 </Offset>
443 % <Power> 1.0 0.8 1.5 </Power>
446 % <Saturation> 0.85 </Saturation>
449 % </ColorCorrectionCollection>
451 % which includes the slop, offset, and power for each of the RGB channels
452 % as well as the saturation.
454 % The format of the ColorDecisionListImage method is:
456 % MagickBooleanType ColorDecisionListImage(Image *image,
457 % const char *color_correction_collection,ExceptionInfo *exception)
459 % A description of each parameter follows:
461 % o image: the image.
463 % o color_correction_collection: the color correction collection in XML.
465 % o exception: return any errors or warnings in this structure.
468 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
469 const char *color_correction_collection,ExceptionInfo *exception)
471 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
473 typedef struct _Correction
481 typedef struct _ColorCorrection
496 token[MaxTextExtent];
527 Allocate and initialize cdl maps.
529 assert(image != (Image *) NULL);
530 assert(image->signature == MagickSignature);
531 if (image->debug != MagickFalse)
532 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
533 if (color_correction_collection == (const char *) NULL)
535 ccc=NewXMLTree((const char *) color_correction_collection,exception);
536 if (ccc == (XMLTreeInfo *) NULL)
538 cc=GetXMLTreeChild(ccc,"ColorCorrection");
539 if (cc == (XMLTreeInfo *) NULL)
541 ccc=DestroyXMLTree(ccc);
544 color_correction.red.slope=1.0;
545 color_correction.red.offset=0.0;
546 color_correction.red.power=1.0;
547 color_correction.green.slope=1.0;
548 color_correction.green.offset=0.0;
549 color_correction.green.power=1.0;
550 color_correction.blue.slope=1.0;
551 color_correction.blue.offset=0.0;
552 color_correction.blue.power=1.0;
553 color_correction.saturation=0.0;
554 sop=GetXMLTreeChild(cc,"SOPNode");
555 if (sop != (XMLTreeInfo *) NULL)
562 slope=GetXMLTreeChild(sop,"Slope");
563 if (slope != (XMLTreeInfo *) NULL)
565 content=GetXMLTreeContent(slope);
566 p=(const char *) content;
567 for (i=0; (*p != '\0') && (i < 3); i++)
569 GetMagickToken(p,&p,token);
571 GetMagickToken(p,&p,token);
576 color_correction.red.slope=StringToDouble(token,(char **) NULL);
581 color_correction.green.slope=StringToDouble(token,
587 color_correction.blue.slope=StringToDouble(token,
594 offset=GetXMLTreeChild(sop,"Offset");
595 if (offset != (XMLTreeInfo *) NULL)
597 content=GetXMLTreeContent(offset);
598 p=(const char *) content;
599 for (i=0; (*p != '\0') && (i < 3); i++)
601 GetMagickToken(p,&p,token);
603 GetMagickToken(p,&p,token);
608 color_correction.red.offset=StringToDouble(token,
614 color_correction.green.offset=StringToDouble(token,
620 color_correction.blue.offset=StringToDouble(token,
627 power=GetXMLTreeChild(sop,"Power");
628 if (power != (XMLTreeInfo *) NULL)
630 content=GetXMLTreeContent(power);
631 p=(const char *) content;
632 for (i=0; (*p != '\0') && (i < 3); i++)
634 GetMagickToken(p,&p,token);
636 GetMagickToken(p,&p,token);
641 color_correction.red.power=StringToDouble(token,(char **) NULL);
646 color_correction.green.power=StringToDouble(token,
652 color_correction.blue.power=StringToDouble(token,
660 sat=GetXMLTreeChild(cc,"SATNode");
661 if (sat != (XMLTreeInfo *) NULL)
666 saturation=GetXMLTreeChild(sat,"Saturation");
667 if (saturation != (XMLTreeInfo *) NULL)
669 content=GetXMLTreeContent(saturation);
670 p=(const char *) content;
671 GetMagickToken(p,&p,token);
672 color_correction.saturation=StringToDouble(token,(char **) NULL);
675 ccc=DestroyXMLTree(ccc);
676 if (image->debug != MagickFalse)
678 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
679 " Color Correction Collection:");
680 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
681 " color_correction.red.slope: %g",color_correction.red.slope);
682 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
683 " color_correction.red.offset: %g",color_correction.red.offset);
684 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
685 " color_correction.red.power: %g",color_correction.red.power);
686 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
687 " color_correction.green.slope: %g",color_correction.green.slope);
688 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
689 " color_correction.green.offset: %g",color_correction.green.offset);
690 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
691 " color_correction.green.power: %g",color_correction.green.power);
692 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
693 " color_correction.blue.slope: %g",color_correction.blue.slope);
694 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
695 " color_correction.blue.offset: %g",color_correction.blue.offset);
696 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
697 " color_correction.blue.power: %g",color_correction.blue.power);
698 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
699 " color_correction.saturation: %g",color_correction.saturation);
701 cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
702 if (cdl_map == (PixelInfo *) NULL)
703 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
705 for (i=0; i <= (ssize_t) MaxMap; i++)
707 cdl_map[i].red=(double) ScaleMapToQuantum((double)
708 (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
709 color_correction.red.offset,color_correction.red.power))));
710 cdl_map[i].green=(double) ScaleMapToQuantum((double)
711 (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
712 color_correction.green.offset,color_correction.green.power))));
713 cdl_map[i].blue=(double) ScaleMapToQuantum((double)
714 (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
715 color_correction.blue.offset,color_correction.blue.power))));
717 if (image->storage_class == PseudoClass)
718 for (i=0; i < (ssize_t) image->colors; i++)
721 Apply transfer function to colormap.
726 luma=0.21267*image->colormap[i].red+0.71526*image->colormap[i].green+
727 0.07217*image->colormap[i].blue;
728 image->colormap[i].red=luma+color_correction.saturation*cdl_map[
729 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
730 image->colormap[i].green=luma+color_correction.saturation*cdl_map[
731 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
732 image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
733 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
736 Apply transfer function to image.
740 image_view=AcquireAuthenticCacheView(image,exception);
741 #if defined(MAGICKCORE_OPENMP_SUPPORT)
742 #pragma omp parallel for schedule(static,4) shared(progress,status) \
743 dynamic_number_threads(image,image->columns,image->rows,1)
745 for (y=0; y < (ssize_t) image->rows; y++)
756 if (status == MagickFalse)
758 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
759 if (q == (Quantum *) NULL)
764 for (x=0; x < (ssize_t) image->columns; x++)
766 luma=0.21267*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+0.07217*
767 GetPixelBlue(image,q);
768 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
769 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
770 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
771 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
772 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
773 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
774 q+=GetPixelChannels(image);
776 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
778 if (image->progress_monitor != (MagickProgressMonitor) NULL)
783 #if defined(MAGICKCORE_OPENMP_SUPPORT)
784 #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
786 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
787 progress++,image->rows);
788 if (proceed == MagickFalse)
792 image_view=DestroyCacheView(image_view);
793 cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802 % C o n t r a s t I m a g e %
806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808 % ContrastImage() enhances the intensity differences between the lighter and
809 % darker elements of the image. Set sharpen to a MagickTrue to increase the
810 % image contrast otherwise the contrast is reduced.
812 % The format of the ContrastImage method is:
814 % MagickBooleanType ContrastImage(Image *image,
815 % const MagickBooleanType sharpen,ExceptionInfo *exception)
817 % A description of each parameter follows:
819 % o image: the image.
821 % o sharpen: Increase or decrease image contrast.
823 % o exception: return any errors or warnings in this structure.
827 static void Contrast(const int sign,double *red,double *green,double *blue)
835 Enhance contrast: dark color become darker, light color become lighter.
837 assert(red != (double *) NULL);
838 assert(green != (double *) NULL);
839 assert(blue != (double *) NULL);
843 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
844 brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
846 if (brightness > 1.0)
849 if (brightness < 0.0)
851 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
854 MagickExport MagickBooleanType ContrastImage(Image *image,
855 const MagickBooleanType sharpen,ExceptionInfo *exception)
857 #define ContrastImageTag "Contrast/Image"
877 assert(image != (Image *) NULL);
878 assert(image->signature == MagickSignature);
879 if (image->debug != MagickFalse)
880 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
881 sign=sharpen != MagickFalse ? 1 : -1;
882 if (image->storage_class == PseudoClass)
885 Contrast enhance colormap.
887 for (i=0; i < (ssize_t) image->colors; i++)
888 Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
889 &image->colormap[i].blue);
892 Contrast enhance image.
896 image_view=AcquireAuthenticCacheView(image,exception);
897 #if defined(MAGICKCORE_OPENMP_SUPPORT)
898 #pragma omp parallel for schedule(static,4) shared(progress,status) \
899 dynamic_number_threads(image,image->columns,image->rows,1)
901 for (y=0; y < (ssize_t) image->rows; y++)
914 if (status == MagickFalse)
916 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
917 if (q == (Quantum *) NULL)
922 for (x=0; x < (ssize_t) image->columns; x++)
924 red=(double) GetPixelRed(image,q);
925 green=(double) GetPixelGreen(image,q);
926 blue=(double) GetPixelBlue(image,q);
927 Contrast(sign,&red,&green,&blue);
928 SetPixelRed(image,ClampToQuantum(red),q);
929 SetPixelGreen(image,ClampToQuantum(green),q);
930 SetPixelBlue(image,ClampToQuantum(blue),q);
931 q+=GetPixelChannels(image);
933 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
935 if (image->progress_monitor != (MagickProgressMonitor) NULL)
940 #if defined(MAGICKCORE_OPENMP_SUPPORT)
941 #pragma omp critical (MagickCore_ContrastImage)
943 proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
944 if (proceed == MagickFalse)
948 image_view=DestroyCacheView(image_view);
953 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
957 % C o n t r a s t S t r e t c h I m a g e %
961 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
963 % ContrastStretchImage() is a simple image enhancement technique that attempts
964 % to improve the contrast in an image by 'stretching' the range of intensity
965 % values it contains to span a desired range of values. It differs from the
966 % more sophisticated histogram equalization in that it can only apply a
967 % linear scaling function to the image pixel values. As a result the
968 % 'enhancement' is less harsh.
970 % The format of the ContrastStretchImage method is:
972 % MagickBooleanType ContrastStretchImage(Image *image,
973 % const char *levels,ExceptionInfo *exception)
975 % A description of each parameter follows:
977 % o image: the image.
979 % o black_point: the black point.
981 % o white_point: the white point.
983 % o levels: Specify the levels where the black and white points have the
984 % range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
986 % o exception: return any errors or warnings in this structure.
989 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
990 const double black_point,const double white_point,ExceptionInfo *exception)
992 #define MaxRange(color) ((double) ScaleQuantumToMap((Quantum) (color)))
993 #define ContrastStretchImageTag "ContrastStretch/Image"
1020 Allocate histogram and stretch map.
1022 assert(image != (Image *) NULL);
1023 assert(image->signature == MagickSignature);
1024 if (image->debug != MagickFalse)
1025 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1026 black=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*black));
1027 white=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*white));
1028 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1029 sizeof(*histogram));
1030 stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1031 GetPixelChannels(image)*sizeof(*stretch_map));
1032 if ((black == (double *) NULL) || (white == (double *) NULL) ||
1033 (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1035 if (stretch_map != (double *) NULL)
1036 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1037 if (histogram != (double *) NULL)
1038 histogram=(double *) RelinquishMagickMemory(histogram);
1039 if (white != (double *) NULL)
1040 white=(double *) RelinquishMagickMemory(white);
1041 if (black != (double *) NULL)
1042 black=(double *) RelinquishMagickMemory(black);
1043 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1050 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1051 sizeof(*histogram));
1052 image_view=AcquireVirtualCacheView(image,exception);
1053 for (y=0; y < (ssize_t) image->rows; y++)
1055 register const Quantum
1061 if (status == MagickFalse)
1063 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1064 if (p == (const Quantum *) NULL)
1069 for (x=0; x < (ssize_t) image->columns; x++)
1077 pixel=GetPixelIntensity(image,p);
1078 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1080 if (image->channel_mask != DefaultChannels)
1081 pixel=(double) p[i];
1082 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1083 ClampToQuantum(pixel))+i]++;
1085 p+=GetPixelChannels(image);
1088 image_view=DestroyCacheView(image_view);
1090 Find the histogram boundaries by locating the black/white levels.
1092 number_channels=GetPixelChannels(image);
1093 for (i=0; i < (ssize_t) number_channels; i++)
1102 white[i]=MaxRange(QuantumRange);
1104 for (j=0; j <= (ssize_t) MaxMap; j++)
1106 intensity+=histogram[GetPixelChannels(image)*j+i];
1107 if (intensity > black_point)
1110 black[i]=(double) j;
1112 for (j=(ssize_t) MaxMap; j != 0; j--)
1114 intensity+=histogram[GetPixelChannels(image)*j+i];
1115 if (intensity > ((double) image->columns*image->rows-white_point))
1118 white[i]=(double) j;
1120 histogram=(double *) RelinquishMagickMemory(histogram);
1122 Stretch the histogram to create the stretched image mapping.
1124 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1125 sizeof(*stretch_map));
1126 number_channels=GetPixelChannels(image);
1127 for (i=0; i < (ssize_t) number_channels; i++)
1132 for (j=0; j <= (ssize_t) MaxMap; j++)
1134 if (j < (ssize_t) black[i])
1135 stretch_map[GetPixelChannels(image)*j+i]=0.0;
1137 if (j > (ssize_t) white[i])
1138 stretch_map[GetPixelChannels(image)*j+i]=(double)
1141 if (black[i] != white[i])
1142 stretch_map[GetPixelChannels(image)*j+i]=(double)
1143 ScaleMapToQuantum((double) (MaxMap*(j-black[i])/
1144 (white[i]-black[i])));
1147 if (image->storage_class == PseudoClass)
1153 Stretch-contrast colormap.
1155 for (j=0; j < (ssize_t) image->colors; j++)
1157 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1159 i=GetPixelChannelChannel(image,RedPixelChannel);
1160 if (black[i] != white[i])
1161 image->colormap[j].red=stretch_map[GetPixelChannels(image)*
1162 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+i;
1164 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1166 i=GetPixelChannelChannel(image,GreenPixelChannel);
1167 if (black[i] != white[i])
1168 image->colormap[j].green=stretch_map[GetPixelChannels(image)*
1169 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+i;
1171 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1173 i=GetPixelChannelChannel(image,BluePixelChannel);
1174 if (black[i] != white[i])
1175 image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
1176 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+i;
1178 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1180 i=GetPixelChannelChannel(image,AlphaPixelChannel);
1181 if (black[i] != white[i])
1182 image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
1183 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+i;
1188 Stretch-contrast image.
1192 image_view=AcquireAuthenticCacheView(image,exception);
1193 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1194 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1195 dynamic_number_threads(image,image->columns,image->rows,1)
1197 for (y=0; y < (ssize_t) image->rows; y++)
1205 if (status == MagickFalse)
1207 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1208 if (q == (Quantum *) NULL)
1213 for (x=0; x < (ssize_t) image->columns; x++)
1218 if (GetPixelMask(image,q) != 0)
1220 q+=GetPixelChannels(image);
1223 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1231 channel=GetPixelChannelChannel(image,i);
1232 traits=GetPixelChannelTraits(image,channel);
1233 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1235 q[i]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1236 ScaleQuantumToMap(q[i])+i]);
1238 q+=GetPixelChannels(image);
1240 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1242 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1247 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1248 #pragma omp critical (MagickCore_ContrastStretchImage)
1250 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1252 if (proceed == MagickFalse)
1256 image_view=DestroyCacheView(image_view);
1257 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1258 white=(double *) RelinquishMagickMemory(white);
1259 black=(double *) RelinquishMagickMemory(black);
1264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1268 % E n h a n c e I m a g e %
1272 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1274 % EnhanceImage() applies a digital filter that improves the quality of a
1277 % The format of the EnhanceImage method is:
1279 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1281 % A description of each parameter follows:
1283 % o image: the image.
1285 % o exception: return any errors or warnings in this structure.
1288 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1290 #define EnhancePixel(weight) \
1291 mean=((double) r[i]+GetPixelChannel(enhance_image,channel,q))/2.0; \
1292 distance=(double) r[i]-(double) GetPixelChannel( \
1293 enhance_image,channel,q); \
1294 distance_squared=QuantumScale*(2.0*((double) QuantumRange+1.0)+ \
1295 mean)*distance*distance; \
1296 if (distance_squared < ((double) QuantumRange*(double) \
1297 QuantumRange/25.0f)) \
1299 aggregate+=(weight)*r[i]; \
1300 total_weight+=(weight); \
1302 r+=GetPixelChannels(image);
1303 #define EnhanceImageTag "Enhance/Image"
1322 Initialize enhanced image attributes.
1324 assert(image != (const Image *) NULL);
1325 assert(image->signature == MagickSignature);
1326 if (image->debug != MagickFalse)
1327 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1328 assert(exception != (ExceptionInfo *) NULL);
1329 assert(exception->signature == MagickSignature);
1330 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1332 if (enhance_image == (Image *) NULL)
1333 return((Image *) NULL);
1334 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1336 enhance_image=DestroyImage(enhance_image);
1337 return((Image *) NULL);
1344 image_view=AcquireVirtualCacheView(image,exception);
1345 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1346 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1347 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1348 dynamic_number_threads(image,image->columns,image->rows,1)
1350 for (y=0; y < (ssize_t) image->rows; y++)
1352 register const Quantum
1364 if (status == MagickFalse)
1366 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1367 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1369 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1374 center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1375 for (x=0; x < (ssize_t) image->columns; x++)
1380 if (GetPixelMask(image,p) != 0)
1382 p+=GetPixelChannels(image);
1383 q+=GetPixelChannels(enhance_image);
1386 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1402 register const Quantum
1405 channel=GetPixelChannelChannel(image,i);
1406 traits=GetPixelChannelTraits(image,channel);
1407 enhance_traits=GetPixelChannelTraits(enhance_image,channel);
1408 if ((traits == UndefinedPixelTrait) ||
1409 (enhance_traits == UndefinedPixelTrait))
1411 SetPixelChannel(enhance_image,channel,p[center+i],q);
1412 if ((enhance_traits & CopyPixelTrait) != 0)
1415 Compute weighted average of target pixel color components.
1420 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1421 EnhancePixel(8.0); EnhancePixel(5.0);
1422 r=p+1*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+2*GetPixelChannels(image)*(image->columns+4);
1426 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1427 EnhancePixel(40.0); EnhancePixel(10.0);
1428 r=p+3*GetPixelChannels(image)*(image->columns+4);
1429 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1430 EnhancePixel(20.0); EnhancePixel(8.0);
1431 r=p+4*GetPixelChannels(image)*(image->columns+4);
1432 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1433 EnhancePixel(8.0); EnhancePixel(5.0);
1434 SetPixelChannel(enhance_image,channel,ClampToQuantum(aggregate/
1437 p+=GetPixelChannels(image);
1438 q+=GetPixelChannels(enhance_image);
1440 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1442 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1447 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1448 #pragma omp critical (MagickCore_EnhanceImage)
1450 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1451 if (proceed == MagickFalse)
1455 enhance_view=DestroyCacheView(enhance_view);
1456 image_view=DestroyCacheView(image_view);
1457 return(enhance_image);
1461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465 % E q u a l i z e I m a g e %
1469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1471 % EqualizeImage() applies a histogram equalization to the image.
1473 % The format of the EqualizeImage method is:
1475 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
1477 % A description of each parameter follows:
1479 % o image: the image.
1481 % o exception: return any errors or warnings in this structure.
1484 MagickExport MagickBooleanType EqualizeImage(Image *image,
1485 ExceptionInfo *exception)
1487 #define EqualizeImageTag "Equalize/Image"
1499 black[CompositePixelChannel],
1503 white[CompositePixelChannel];
1515 Allocate and initialize histogram arrays.
1517 assert(image != (Image *) NULL);
1518 assert(image->signature == MagickSignature);
1519 if (image->debug != MagickFalse)
1520 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1521 equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1522 GetPixelChannels(image)*sizeof(*equalize_map));
1523 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,
1524 GetPixelChannels(image)*sizeof(*histogram));
1525 map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1526 GetPixelChannels(image)*sizeof(*map));
1527 if ((equalize_map == (double *) NULL) ||
1528 (histogram == (double *) NULL) ||
1529 (map == (double *) NULL))
1531 if (map != (double *) NULL)
1532 map=(double *) RelinquishMagickMemory(map);
1533 if (histogram != (double *) NULL)
1534 histogram=(double *) RelinquishMagickMemory(histogram);
1535 if (equalize_map != (double *) NULL)
1536 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1537 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1544 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1545 sizeof(*histogram));
1546 image_view=AcquireVirtualCacheView(image,exception);
1547 for (y=0; y < (ssize_t) image->rows; y++)
1549 register const Quantum
1555 if (status == MagickFalse)
1557 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1558 if (p == (const Quantum *) NULL)
1563 for (x=0; x < (ssize_t) image->columns; x++)
1568 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1569 histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++;
1570 p+=GetPixelChannels(image);
1573 image_view=DestroyCacheView(image_view);
1575 Integrate the histogram to get the equalization map.
1577 number_channels=GetPixelChannels(image);
1578 for (i=0; i < (ssize_t) number_channels; i++)
1587 for (j=0; j <= (ssize_t) MaxMap; j++)
1589 intensity+=histogram[GetPixelChannels(image)*j+i];
1590 map[GetPixelChannels(image)*j+i]=intensity;
1593 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
1594 sizeof(*equalize_map));
1595 number_channels=GetPixelChannels(image);
1596 for (i=0; i < (ssize_t) number_channels; i++)
1602 white[i]=map[GetPixelChannels(image)*MaxMap+i];
1603 if (black[i] != white[i])
1604 for (j=0; j <= (ssize_t) MaxMap; j++)
1605 equalize_map[GetPixelChannels(image)*j+i]=(double)
1606 ScaleMapToQuantum((double) ((MaxMap*(map[
1607 GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
1609 histogram=(double *) RelinquishMagickMemory(histogram);
1610 map=(double *) RelinquishMagickMemory(map);
1611 if (image->storage_class == PseudoClass)
1622 for (j=0; j < (ssize_t) image->colors; j++)
1624 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1626 channel=GetPixelChannelChannel(image,RedPixelChannel);
1627 if (black[channel] != white[channel])
1628 image->colormap[j].red=equalize_map[GetPixelChannels(image)*
1629 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+
1632 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1634 channel=GetPixelChannelChannel(image,GreenPixelChannel);
1635 if (black[channel] != white[channel])
1636 image->colormap[j].green=equalize_map[GetPixelChannels(image)*
1637 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+
1640 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1642 channel=GetPixelChannelChannel(image,BluePixelChannel);
1643 if (black[channel] != white[channel])
1644 image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
1645 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+
1648 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1650 channel=GetPixelChannelChannel(image,AlphaPixelChannel);
1651 if (black[channel] != white[channel])
1652 image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
1653 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+
1662 image_view=AcquireAuthenticCacheView(image,exception);
1663 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1664 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1665 dynamic_number_threads(image,image->columns,image->rows,1)
1667 for (y=0; y < (ssize_t) image->rows; y++)
1675 if (status == MagickFalse)
1677 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1678 if (q == (Quantum *) NULL)
1683 for (x=0; x < (ssize_t) image->columns; x++)
1688 if (GetPixelMask(image,q) != 0)
1690 q+=GetPixelChannels(image);
1693 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1701 channel=GetPixelChannelChannel(image,i);
1702 traits=GetPixelChannelTraits(image,channel);
1703 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1705 q[i]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
1706 ScaleQuantumToMap(q[i])+i]);
1708 q+=GetPixelChannels(image);
1710 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1712 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1717 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1718 #pragma omp critical (MagickCore_EqualizeImage)
1720 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1721 if (proceed == MagickFalse)
1725 image_view=DestroyCacheView(image_view);
1726 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1735 % G a m m a I m a g e %
1739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1741 % GammaImage() gamma-corrects a particular image channel. The same
1742 % image viewed on different devices will have perceptual differences in the
1743 % way the image's intensities are represented on the screen. Specify
1744 % individual gamma levels for the red, green, and blue channels, or adjust
1745 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
1747 % You can also reduce the influence of a particular channel with a gamma
1750 % The format of the GammaImage method is:
1752 % MagickBooleanType GammaImage(Image *image,const double gamma,
1753 % ExceptionInfo *exception)
1755 % A description of each parameter follows:
1757 % o image: the image.
1759 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1761 % o gamma: the image gamma.
1764 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
1765 ExceptionInfo *exception)
1767 #define GammaCorrectImageTag "GammaCorrect/Image"
1788 Allocate and initialize gamma maps.
1790 assert(image != (Image *) NULL);
1791 assert(image->signature == MagickSignature);
1792 if (image->debug != MagickFalse)
1793 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1796 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1797 if (gamma_map == (Quantum *) NULL)
1798 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1800 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1802 for (i=0; i <= (ssize_t) MaxMap; i++)
1803 gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
1804 MaxMap,1.0/gamma)));
1805 if (image->storage_class == PseudoClass)
1806 for (i=0; i < (ssize_t) image->colors; i++)
1809 Gamma-correct colormap.
1811 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1812 image->colormap[i].red=(double) gamma_map[
1813 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))];
1814 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1815 image->colormap[i].green=(double) gamma_map[
1816 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))];
1817 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1818 image->colormap[i].blue=(double) gamma_map[
1819 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))];
1820 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1821 image->colormap[i].alpha=(double) gamma_map[
1822 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].alpha))];
1825 Gamma-correct image.
1829 image_view=AcquireAuthenticCacheView(image,exception);
1830 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1831 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1832 dynamic_number_threads(image,image->columns,image->rows,1)
1834 for (y=0; y < (ssize_t) image->rows; y++)
1842 if (status == MagickFalse)
1844 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1845 if (q == (Quantum *) NULL)
1850 for (x=0; x < (ssize_t) image->columns; x++)
1855 if (GetPixelMask(image,q) != 0)
1857 q+=GetPixelChannels(image);
1860 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1868 channel=GetPixelChannelChannel(image,i);
1869 traits=GetPixelChannelTraits(image,channel);
1870 if ((traits & UpdatePixelTrait) == 0)
1872 q[i]=gamma_map[ScaleQuantumToMap(q[i])];
1874 q+=GetPixelChannels(image);
1876 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1878 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1883 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1884 #pragma omp critical (MagickCore_GammaImage)
1886 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
1888 if (proceed == MagickFalse)
1892 image_view=DestroyCacheView(image_view);
1893 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
1894 if (image->gamma != 0.0)
1895 image->gamma*=gamma;
1900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1904 % H a l d C l u t I m a g e %
1908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1910 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
1911 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
1912 % Create it with the HALD coder. You can apply any color transformation to
1913 % the Hald image and then use this method to apply the transform to the
1916 % The format of the HaldClutImage method is:
1918 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
1919 % ExceptionInfo *exception)
1921 % A description of each parameter follows:
1923 % o image: the image, which is replaced by indexed CLUT values
1925 % o hald_image: the color lookup table image for replacement color values.
1927 % o exception: return any errors or warnings in this structure.
1931 static inline size_t MagickMin(const size_t x,const size_t y)
1938 MagickExport MagickBooleanType HaldClutImage(Image *image,
1939 const Image *hald_image,ExceptionInfo *exception)
1941 #define HaldClutImageTag "Clut/Image"
1943 typedef struct _HaldInfo
1975 assert(image != (Image *) NULL);
1976 assert(image->signature == MagickSignature);
1977 if (image->debug != MagickFalse)
1978 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1979 assert(hald_image != (Image *) NULL);
1980 assert(hald_image->signature == MagickSignature);
1981 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1982 return(MagickFalse);
1983 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1984 (void) TransformImageColorspace(image,RGBColorspace,exception);
1985 if (image->alpha_trait != BlendPixelTrait)
1986 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1992 length=MagickMin(hald_image->columns,hald_image->rows);
1993 for (level=2; (level*level*level) < length; level++) ;
1995 cube_size=level*level;
1996 width=(double) hald_image->columns;
1997 GetPixelInfo(hald_image,&zero);
1998 hald_view=AcquireVirtualCacheView(hald_image,exception);
1999 image_view=AcquireAuthenticCacheView(image,exception);
2000 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2001 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2002 dynamic_number_threads(image,image->columns,image->rows,1)
2004 for (y=0; y < (ssize_t) image->rows; y++)
2012 if (status == MagickFalse)
2014 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2015 if (q == (Quantum *) NULL)
2020 for (x=0; x < (ssize_t) image->columns; x++)
2035 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2036 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2037 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2038 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2039 point.x-=floor(point.x);
2040 point.y-=floor(point.y);
2041 point.z-=floor(point.z);
2043 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2044 fmod(offset,width),floor(offset/width),&pixel1,exception);
2046 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2047 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2049 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2052 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2053 fmod(offset,width),floor(offset/width),&pixel1,exception);
2054 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2055 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2057 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2060 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2062 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2063 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2064 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2065 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2066 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2067 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2068 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2069 (image->colorspace == CMYKColorspace))
2070 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2071 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2072 (image->alpha_trait == BlendPixelTrait))
2073 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2074 q+=GetPixelChannels(image);
2076 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2078 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2083 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2084 #pragma omp critical (MagickCore_HaldClutImage)
2086 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2087 if (proceed == MagickFalse)
2091 hald_view=DestroyCacheView(hald_view);
2092 image_view=DestroyCacheView(image_view);
2097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2101 % L e v e l I m a g e %
2105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2107 % LevelImage() adjusts the levels of a particular image channel by
2108 % scaling the colors falling between specified white and black points to
2109 % the full available quantum range.
2111 % The parameters provided represent the black, and white points. The black
2112 % point specifies the darkest color in the image. Colors darker than the
2113 % black point are set to zero. White point specifies the lightest color in
2114 % the image. Colors brighter than the white point are set to the maximum
2117 % If a '!' flag is given, map black and white colors to the given levels
2118 % rather than mapping those levels to black and white. See
2119 % LevelizeImage() below.
2121 % Gamma specifies a gamma correction to apply to the image.
2123 % The format of the LevelImage method is:
2125 % MagickBooleanType LevelImage(Image *image,const double black_point,
2126 % const double white_point,const double gamma,ExceptionInfo *exception)
2128 % A description of each parameter follows:
2130 % o image: the image.
2132 % o black_point: The level to map zero (black) to.
2134 % o white_point: The level to map QuantumRange (white) to.
2136 % o exception: return any errors or warnings in this structure.
2140 static inline double LevelPixel(const double black_point,
2141 const double white_point,const double gamma,const double pixel)
2147 scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0;
2148 level_pixel=(double) QuantumRange*pow(scale*((double) pixel-
2149 black_point),1.0/gamma);
2150 return(level_pixel);
2153 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2154 const double white_point,const double gamma,ExceptionInfo *exception)
2156 #define LevelImageTag "Level/Image"
2174 Allocate and initialize levels map.
2176 assert(image != (Image *) NULL);
2177 assert(image->signature == MagickSignature);
2178 if (image->debug != MagickFalse)
2179 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2180 if (image->storage_class == PseudoClass)
2181 for (i=0; i < (ssize_t) image->colors; i++)
2186 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2187 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2188 white_point,gamma,image->colormap[i].red));
2189 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2190 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2191 white_point,gamma,image->colormap[i].green));
2192 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2193 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2194 white_point,gamma,image->colormap[i].blue));
2195 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2196 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2197 white_point,gamma,image->colormap[i].alpha));
2204 image_view=AcquireAuthenticCacheView(image,exception);
2205 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2206 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2207 dynamic_number_threads(image,image->columns,image->rows,1)
2209 for (y=0; y < (ssize_t) image->rows; y++)
2217 if (status == MagickFalse)
2219 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2220 if (q == (Quantum *) NULL)
2225 for (x=0; x < (ssize_t) image->columns; x++)
2230 if (GetPixelMask(image,q) != 0)
2232 q+=GetPixelChannels(image);
2235 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2243 channel=GetPixelChannelChannel(image,i);
2244 traits=GetPixelChannelTraits(image,channel);
2245 if ((traits & UpdatePixelTrait) == 0)
2247 q[i]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2250 q+=GetPixelChannels(image);
2252 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2254 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2259 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2260 #pragma omp critical (MagickCore_LevelImage)
2262 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2263 if (proceed == MagickFalse)
2267 image_view=DestroyCacheView(image_view);
2268 if (status != MagickFalse)
2269 (void) ClampImage(image,exception);
2274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2278 % L e v e l i z e I m a g e %
2282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2284 % LevelizeImage() applies the reversed LevelImage() operation to just
2285 % the specific channels specified. It compresses the full range of color
2286 % values, so that they lie between the given black and white points. Gamma is
2287 % applied before the values are mapped.
2289 % LevelizeImage() can be called with by using a +level command line
2290 % API option, or using a '!' on a -level or LevelImage() geometry string.
2292 % It can be used to de-contrast a greyscale image to the exact levels
2293 % specified. Or by using specific levels for each channel of an image you
2294 % can convert a gray-scale image to any linear color gradient, according to
2297 % The format of the LevelizeImage method is:
2299 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
2300 % const double white_point,const double gamma,ExceptionInfo *exception)
2302 % A description of each parameter follows:
2304 % o image: the image.
2306 % o black_point: The level to map zero (black) to.
2308 % o white_point: The level to map QuantumRange (white) to.
2310 % o gamma: adjust gamma by this factor before mapping values.
2312 % o exception: return any errors or warnings in this structure.
2315 MagickExport MagickBooleanType LevelizeImage(Image *image,
2316 const double black_point,const double white_point,const double gamma,
2317 ExceptionInfo *exception)
2319 #define LevelizeImageTag "Levelize/Image"
2320 #define LevelizeValue(x) (ClampToQuantum((pow((double) (QuantumScale*(x)), \
2321 1.0/gamma))*(white_point-black_point)+black_point))
2339 Allocate and initialize levels map.
2341 assert(image != (Image *) NULL);
2342 assert(image->signature == MagickSignature);
2343 if (image->debug != MagickFalse)
2344 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2345 if (IsGrayColorspace(image->colorspace) != MagickFalse)
2346 (void) SetImageColorspace(image,RGBColorspace,exception);
2347 if (image->storage_class == PseudoClass)
2348 for (i=0; i < (ssize_t) image->colors; i++)
2353 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2354 image->colormap[i].red=(double) LevelizeValue(
2355 image->colormap[i].red);
2356 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2357 image->colormap[i].green=(double) LevelizeValue(
2358 image->colormap[i].green);
2359 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2360 image->colormap[i].blue=(double) LevelizeValue(
2361 image->colormap[i].blue);
2362 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2363 image->colormap[i].alpha=(double) LevelizeValue(
2364 image->colormap[i].alpha);
2371 image_view=AcquireAuthenticCacheView(image,exception);
2372 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2373 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2374 dynamic_number_threads(image,image->columns,image->rows,1)
2376 for (y=0; y < (ssize_t) image->rows; y++)
2384 if (status == MagickFalse)
2386 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2387 if (q == (Quantum *) NULL)
2392 for (x=0; x < (ssize_t) image->columns; x++)
2397 if (GetPixelMask(image,q) != 0)
2399 q+=GetPixelChannels(image);
2402 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2410 channel=GetPixelChannelChannel(image,i);
2411 traits=GetPixelChannelTraits(image,channel);
2412 if ((traits & UpdatePixelTrait) == 0)
2414 q[i]=LevelizeValue(q[i]);
2416 q+=GetPixelChannels(image);
2418 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2420 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2425 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2426 #pragma omp critical (MagickCore_LevelizeImage)
2428 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2429 if (proceed == MagickFalse)
2433 image_view=DestroyCacheView(image_view);
2438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2442 % L e v e l I m a g e C o l o r s %
2446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2448 % LevelImageColors() maps the given color to "black" and "white" values,
2449 % linearly spreading out the colors, and level values on a channel by channel
2450 % bases, as per LevelImage(). The given colors allows you to specify
2451 % different level ranges for each of the color channels separately.
2453 % If the boolean 'invert' is set true the image values will modifyed in the
2454 % reverse direction. That is any existing "black" and "white" colors in the
2455 % image will become the color values given, with all other values compressed
2456 % appropriatally. This effectivally maps a greyscale gradient into the given
2459 % The format of the LevelImageColors method is:
2461 % MagickBooleanType LevelImageColors(Image *image,
2462 % const PixelInfo *black_color,const PixelInfo *white_color,
2463 % const MagickBooleanType invert,ExceptionInfo *exception)
2465 % A description of each parameter follows:
2467 % o image: the image.
2469 % o black_color: The color to map black to/from
2471 % o white_point: The color to map white to/from
2473 % o invert: if true map the colors (levelize), rather than from (level)
2475 % o exception: return any errors or warnings in this structure.
2478 MagickExport MagickBooleanType LevelImageColors(Image *image,
2479 const PixelInfo *black_color,const PixelInfo *white_color,
2480 const MagickBooleanType invert,ExceptionInfo *exception)
2489 Allocate and initialize levels map.
2491 assert(image != (Image *) NULL);
2492 assert(image->signature == MagickSignature);
2493 if (image->debug != MagickFalse)
2494 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2496 if (invert == MagickFalse)
2498 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2500 channel_mask=SetImageChannelMask(image,RedChannel);
2501 status|=LevelImage(image,black_color->red,white_color->red,1.0,
2503 (void) SetImageChannelMask(image,channel_mask);
2505 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2507 channel_mask=SetImageChannelMask(image,GreenChannel);
2508 status|=LevelImage(image,black_color->green,white_color->green,1.0,
2510 (void) SetImageChannelMask(image,channel_mask);
2512 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2514 channel_mask=SetImageChannelMask(image,BlueChannel);
2515 status|=LevelImage(image,black_color->blue,white_color->blue,1.0,
2517 (void) SetImageChannelMask(image,channel_mask);
2519 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2520 (image->colorspace == CMYKColorspace))
2522 channel_mask=SetImageChannelMask(image,BlackChannel);
2523 status|=LevelImage(image,black_color->black,white_color->black,1.0,
2525 (void) SetImageChannelMask(image,channel_mask);
2527 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2528 (image->alpha_trait == BlendPixelTrait))
2530 channel_mask=SetImageChannelMask(image,AlphaChannel);
2531 status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2533 (void) SetImageChannelMask(image,channel_mask);
2538 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2540 channel_mask=SetImageChannelMask(image,RedChannel);
2541 status|=LevelizeImage(image,black_color->red,white_color->red,1.0,
2543 (void) SetImageChannelMask(image,channel_mask);
2545 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2547 channel_mask=SetImageChannelMask(image,GreenChannel);
2548 status|=LevelizeImage(image,black_color->green,white_color->green,1.0,
2550 (void) SetImageChannelMask(image,channel_mask);
2552 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2554 channel_mask=SetImageChannelMask(image,BlueChannel);
2555 status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2557 (void) SetImageChannelMask(image,channel_mask);
2559 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2560 (image->colorspace == CMYKColorspace))
2562 channel_mask=SetImageChannelMask(image,BlackChannel);
2563 status|=LevelizeImage(image,black_color->black,white_color->black,1.0,
2565 (void) SetImageChannelMask(image,channel_mask);
2567 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2568 (image->alpha_trait == BlendPixelTrait))
2570 channel_mask=SetImageChannelMask(image,AlphaChannel);
2571 status|=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2573 (void) SetImageChannelMask(image,channel_mask);
2576 return(status == 0 ? MagickFalse : MagickTrue);
2580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2584 % L i n e a r S t r e t c h I m a g e %
2588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2590 % LinearStretchImage() discards any pixels below the black point and above
2591 % the white point and levels the remaining pixels.
2593 % The format of the LinearStretchImage method is:
2595 % MagickBooleanType LinearStretchImage(Image *image,
2596 % const double black_point,const double white_point,
2597 % ExceptionInfo *exception)
2599 % A description of each parameter follows:
2601 % o image: the image.
2603 % o black_point: the black point.
2605 % o white_point: the white point.
2607 % o exception: return any errors or warnings in this structure.
2610 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2611 const double black_point,const double white_point,ExceptionInfo *exception)
2613 #define LinearStretchImageTag "LinearStretch/Image"
2631 Allocate histogram and linear map.
2633 assert(image != (Image *) NULL);
2634 assert(image->signature == MagickSignature);
2635 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,
2636 sizeof(*histogram));
2637 if (histogram == (double *) NULL)
2638 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2643 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2644 image_view=AcquireVirtualCacheView(image,exception);
2645 for (y=0; y < (ssize_t) image->rows; y++)
2647 register const Quantum
2653 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2654 if (p == (const Quantum *) NULL)
2656 for (x=0; x < (ssize_t) image->columns; x++)
2661 intensity=GetPixelIntensity(image,p);
2662 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2663 p+=GetPixelChannels(image);
2666 image_view=DestroyCacheView(image_view);
2668 Find the histogram boundaries by locating the black and white point levels.
2671 for (black=0; black < (ssize_t) MaxMap; black++)
2673 intensity+=histogram[black];
2674 if (intensity >= black_point)
2678 for (white=(ssize_t) MaxMap; white != 0; white--)
2680 intensity+=histogram[white];
2681 if (intensity >= white_point)
2684 histogram=(double *) RelinquishMagickMemory(histogram);
2685 status=LevelImage(image,(double) black,(double) white,1.0,exception);
2690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2694 % M o d u l a t e I m a g e %
2698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2700 % ModulateImage() lets you control the brightness, saturation, and hue
2701 % of an image. Modulate represents the brightness, saturation, and hue
2702 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
2703 % modulation is lightness, saturation, and hue. For HWB, use blackness,
2704 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
2706 % The format of the ModulateImage method is:
2708 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
2709 % ExceptionInfo *exception)
2711 % A description of each parameter follows:
2713 % o image: the image.
2715 % o modulate: Define the percent change in brightness, saturation, and hue.
2717 % o exception: return any errors or warnings in this structure.
2721 static inline void ModulateHCL(const double percent_hue,
2722 const double percent_chroma,const double percent_luma,double *red,
2723 double *green,double *blue)
2731 Increase or decrease color luma, chroma, or hue.
2733 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2734 hue+=0.5*(0.01*percent_hue-1.0);
2739 chroma*=0.01*percent_chroma;
2740 luma*=0.01*percent_luma;
2741 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2744 static inline void ModulateHSB(const double percent_hue,
2745 const double percent_saturation,const double percent_brightness,double *red,
2746 double *green,double *blue)
2754 Increase or decrease color brightness, saturation, or hue.
2756 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2757 hue+=0.5*(0.01*percent_hue-1.0);
2762 saturation*=0.01*percent_saturation;
2763 brightness*=0.01*percent_brightness;
2764 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2767 static inline void ModulateHSL(const double percent_hue,
2768 const double percent_saturation,const double percent_lightness,double *red,
2769 double *green,double *blue)
2777 Increase or decrease color lightness, saturation, or hue.
2779 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
2780 hue+=0.5*(0.01*percent_hue-1.0);
2785 saturation*=0.01*percent_saturation;
2786 lightness*=0.01*percent_lightness;
2787 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
2790 static inline void ModulateHWB(const double percent_hue,
2791 const double percent_whiteness,const double percent_blackness,double *red,
2792 double *green,double *blue)
2800 Increase or decrease color blackness, whiteness, or hue.
2802 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
2803 hue+=0.5*(0.01*percent_hue-1.0);
2808 blackness*=0.01*percent_blackness;
2809 whiteness*=0.01*percent_whiteness;
2810 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
2813 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
2814 ExceptionInfo *exception)
2816 #define ModulateImageTag "Modulate/Image"
2851 Initialize modulate table.
2853 assert(image != (Image *) NULL);
2854 assert(image->signature == MagickSignature);
2855 if (image->debug != MagickFalse)
2856 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2857 if (modulate == (char *) NULL)
2858 return(MagickFalse);
2859 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
2860 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2861 flags=ParseGeometry(modulate,&geometry_info);
2862 percent_brightness=geometry_info.rho;
2863 percent_saturation=geometry_info.sigma;
2864 if ((flags & SigmaValue) == 0)
2865 percent_saturation=100.0;
2866 percent_hue=geometry_info.xi;
2867 if ((flags & XiValue) == 0)
2869 colorspace=UndefinedColorspace;
2870 artifact=GetImageArtifact(image,"modulate:colorspace");
2871 if (artifact != (const char *) NULL)
2872 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
2873 MagickFalse,artifact);
2874 if (image->storage_class == PseudoClass)
2875 for (i=0; i < (ssize_t) image->colors; i++)
2885 red=image->colormap[i].red;
2886 green=image->colormap[i].green;
2887 blue=image->colormap[i].blue;
2892 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
2898 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2905 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2911 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2922 image_view=AcquireAuthenticCacheView(image,exception);
2923 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2924 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2925 dynamic_number_threads(image,image->columns,image->rows,1)
2927 for (y=0; y < (ssize_t) image->rows; y++)
2935 if (status == MagickFalse)
2937 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2938 if (q == (Quantum *) NULL)
2943 for (x=0; x < (ssize_t) image->columns; x++)
2950 red=(double) GetPixelRed(image,q);
2951 green=(double) GetPixelGreen(image,q);
2952 blue=(double) GetPixelBlue(image,q);
2957 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
2963 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2970 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2976 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2981 SetPixelRed(image,ClampToQuantum(red),q);
2982 SetPixelGreen(image,ClampToQuantum(green),q);
2983 SetPixelBlue(image,ClampToQuantum(blue),q);
2984 q+=GetPixelChannels(image);
2986 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2988 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2993 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2994 #pragma omp critical (MagickCore_ModulateImage)
2996 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
2997 if (proceed == MagickFalse)
3001 image_view=DestroyCacheView(image_view);
3006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3010 % N e g a t e I m a g e %
3014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3016 % NegateImage() negates the colors in the reference image. The grayscale
3017 % option means that only grayscale values within the image are negated.
3019 % The format of the NegateImage method is:
3021 % MagickBooleanType NegateImage(Image *image,
3022 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3024 % A description of each parameter follows:
3026 % o image: the image.
3028 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3030 % o exception: return any errors or warnings in this structure.
3033 MagickExport MagickBooleanType NegateImage(Image *image,
3034 const MagickBooleanType grayscale,ExceptionInfo *exception)
3036 #define NegateImageTag "Negate/Image"
3053 assert(image != (Image *) NULL);
3054 assert(image->signature == MagickSignature);
3055 if (image->debug != MagickFalse)
3056 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3057 if (image->storage_class == PseudoClass)
3058 for (i=0; i < (ssize_t) image->colors; i++)
3063 if (grayscale != MagickFalse)
3064 if ((image->colormap[i].red != image->colormap[i].green) ||
3065 (image->colormap[i].green != image->colormap[i].blue))
3067 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3068 image->colormap[i].red=QuantumRange-image->colormap[i].red;
3069 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3070 image->colormap[i].green=QuantumRange-image->colormap[i].green;
3071 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3072 image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3079 image_view=AcquireAuthenticCacheView(image,exception);
3080 if (grayscale != MagickFalse)
3082 for (y=0; y < (ssize_t) image->rows; y++)
3093 if (status == MagickFalse)
3095 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3097 if (q == (Quantum *) NULL)
3102 for (x=0; x < (ssize_t) image->columns; x++)
3107 if ((GetPixelMask(image,q) != 0) ||
3108 (IsPixelGray(image,q) != MagickFalse))
3110 q+=GetPixelChannels(image);
3113 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3121 channel=GetPixelChannelChannel(image,i);
3122 traits=GetPixelChannelTraits(image,channel);
3123 if ((traits & UpdatePixelTrait) == 0)
3125 q[i]=QuantumRange-q[i];
3127 q+=GetPixelChannels(image);
3129 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3130 if (sync == MagickFalse)
3132 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3137 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3138 #pragma omp critical (MagickCore_NegateImage)
3140 proceed=SetImageProgress(image,NegateImageTag,progress++,
3142 if (proceed == MagickFalse)
3146 image_view=DestroyCacheView(image_view);
3152 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3153 #pragma omp parallel for schedule(static) shared(progress,status) \
3154 dynamic_number_threads(image,image->columns,image->rows,1)
3156 for (y=0; y < (ssize_t) image->rows; y++)
3164 if (status == MagickFalse)
3166 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3167 if (q == (Quantum *) NULL)
3172 for (x=0; x < (ssize_t) image->columns; x++)
3177 if (GetPixelMask(image,q) != 0)
3179 q+=GetPixelChannels(image);
3182 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3190 channel=GetPixelChannelChannel(image,i);
3191 traits=GetPixelChannelTraits(image,channel);
3192 if ((traits & UpdatePixelTrait) == 0)
3194 q[i]=QuantumRange-q[i];
3196 q+=GetPixelChannels(image);
3198 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3200 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3205 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3206 #pragma omp critical (MagickCore_NegateImage)
3208 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3209 if (proceed == MagickFalse)
3213 image_view=DestroyCacheView(image_view);
3218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3222 % N o r m a l i z e I m a g e %
3226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3228 % The NormalizeImage() method enhances the contrast of a color image by
3229 % mapping the darkest 2 percent of all pixel to black and the brightest
3230 % 1 percent to white.
3232 % The format of the NormalizeImage method is:
3234 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3236 % A description of each parameter follows:
3238 % o image: the image.
3240 % o exception: return any errors or warnings in this structure.
3243 MagickExport MagickBooleanType NormalizeImage(Image *image,
3244 ExceptionInfo *exception)
3250 black_point=(double) image->columns*image->rows*0.0015;
3251 white_point=(double) image->columns*image->rows*0.9995;
3252 return(ContrastStretchImage(image,black_point,white_point,exception));
3256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3260 % S i g m o i d a l C o n t r a s t I m a g e %
3264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3266 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3267 % sigmoidal contrast algorithm. Increase the contrast of the image using a
3268 % sigmoidal transfer function without saturating highlights or shadows.
3269 % Contrast indicates how much to increase the contrast (0 is none; 3 is
3270 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
3271 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
3272 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
3275 % The format of the SigmoidalContrastImage method is:
3277 % MagickBooleanType SigmoidalContrastImage(Image *image,
3278 % const MagickBooleanType sharpen,const char *levels,
3279 % ExceptionInfo *exception)
3281 % A description of each parameter follows:
3283 % o image: the image.
3285 % o sharpen: Increase or decrease image contrast.
3287 % o contrast: strength of the contrast, the larger the number the more
3288 % 'threshold-like' it becomes.
3290 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3292 % o exception: return any errors or warnings in this structure.
3295 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3296 const MagickBooleanType sharpen,const double contrast,const double midpoint,
3297 ExceptionInfo *exception)
3299 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
3314 Side effect: clamps values unless contrast<MagickEpsilon, in which
3315 case nothing is done.
3317 if (contrast<MagickEpsilon)
3321 Sigmoidal function with inflexion point moved to b and "slope
3323 The first version, based on the hyperbolic tangent tanh, when
3324 combined with the scaling step, is an exact arithmetic clone of the
3325 the sigmoid function based on the logistic curve. The equivalence is
3326 based on the identity
3327 1/(1+exp(-t)) = (1+tanh(t/2))/2
3328 (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3329 scaled sigmoidal derivation is invariant under affine transformations
3331 The tanh version is almost certainly more accurate and cheaper.
3332 The 0.5 factor in its argument is to clone the legacy ImageMagick
3333 behavior. The reason for making the define depend on atanh even
3334 though it only uses tanh has to do with the construction of the
3335 inverse of the scaled sigmoidal.
3337 #if defined(MAGICKCORE_HAVE_ATANH)
3338 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3340 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3343 Scaled sigmoidal formula:
3344 ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3345 ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3346 See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html
3347 and http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf.
3348 The limit of ScaledSigmoidal as a->0 is the identity, but a=0 gives a
3349 division by zero. This is fixed above by exiting immediately when
3350 contrast is small, leaving the image (or colormap) unmodified. This
3351 appears to be safe because the series expansion of the logistic
3352 sigmoidal function around x=b is 1/2-a*(b-x)/4+... so that the key
3353 denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3355 #define ScaledSigmoidal(a,b,x) ( \
3356 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3357 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3359 Inverse of ScaledSigmoidal, used for +sigmoidal-contrast.
3360 Because b may be 0 or 1, the argument of the hyperbolic tangent
3361 (resp. logistic sigmoidal) may be outside of the interval (-1,1)
3362 (resp. (0,1)), even when creating a LUT, hence the branching.
3363 In addition, HDRI may have out of gamut values.
3364 InverseScaledSigmoidal is not a two-side inverse of
3365 ScaledSigmoidal: It is only a right inverse. This is unavoidable.
3367 #if defined(MAGICKCORE_HAVE_ATANH)
3368 #define InverseScaledSigmoidal(a,b,x) ({ \
3369 const double _argument = \
3370 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) * (x) + \
3371 Sigmoidal((a),(b),0.0); \
3372 const double _clamped_argument = \
3373 ( _argument < -1+MagickEpsilon ? -1+MagickEpsilon : \
3374 ( _argument > 1-MagickEpsilon ? 1-MagickEpsilon : _argument ) ); \
3375 (b) + (2.0/(a)) * atanh(_clamped_argument); })
3377 #define InverseScaledSigmoidal(a,b,x) ({ \
3378 const double _argument = \
3379 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) * (x) + \
3380 Sigmoidal((a),(b),0.0); \
3381 const double _clamped_argument = \
3382 ( _argument < MagickEpsilon ? MagickEpsilon : \
3383 ( _argument > 1-MagickEpsilon ? 1-MagickEpsilon : _argument ) ); \
3384 (b) + (-1.0/(a)) * log(1.0/_clamped_argument+-1.0); })
3389 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3390 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3391 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3392 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3394 assert(image != (Image *) NULL);
3395 assert(image->signature == MagickSignature);
3396 if (image->debug != MagickFalse)
3397 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3399 Sigmoidal-contrast enhance colormap.
3401 if (image->storage_class == PseudoClass)
3406 if (sharpen != MagickFalse)
3407 for (i=0; i < (ssize_t) image->colors; i++)
3409 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3410 image->colormap[i].red=ScaledSig(image->colormap[i].red);
3411 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3412 image->colormap[i].green=ScaledSig(image->colormap[i].green);
3413 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3414 image->colormap[i].blue=ScaledSig(image->colormap[i].blue);
3415 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3416 image->colormap[i].alpha=ScaledSig(image->colormap[i].alpha);
3419 for (i=0; i < (ssize_t) image->colors; i++)
3421 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3422 image->colormap[i].red=InverseScaledSig(image->colormap[i].red);
3423 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3424 image->colormap[i].green=InverseScaledSig(image->colormap[i].green);
3425 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3426 image->colormap[i].blue=InverseScaledSig(image->colormap[i].blue);
3427 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3428 image->colormap[i].alpha=InverseScaledSig(image->colormap[i].alpha);
3432 Sigmoidal-contrast enhance image.
3436 image_view=AcquireAuthenticCacheView(image,exception);
3437 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3438 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3439 dynamic_number_threads(image,image->columns,image->rows,1)
3441 for (y=0; y < (ssize_t) image->rows; y++)
3449 if (status == MagickFalse)
3451 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3452 if (q == (Quantum *) NULL)
3457 for (x=0; x < (ssize_t) image->columns; x++)
3462 if (GetPixelMask(image,q) != 0)
3464 q+=GetPixelChannels(image);
3467 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3475 channel=GetPixelChannelChannel(image,i);
3476 traits=GetPixelChannelTraits(image,channel);
3477 if ((traits & UpdatePixelTrait) == 0)
3479 if (sharpen != MagickFalse)
3480 q[i]=ScaledSig(q[i]);
3482 q[i]=InverseScaledSig(q[i]);
3484 q+=GetPixelChannels(image);
3486 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3488 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3493 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3494 #pragma omp critical (MagickCore_SigmoidalContrastImage)
3496 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3498 if (proceed == MagickFalse)
3502 image_view=DestroyCacheView(image_view);