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++)
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.21267f*image->colormap[i].red+0.71526*image->colormap[i].green+
727 0.07217f*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.21267f*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+0.07217f*
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++)
894 Contrast(sign,&red,&green,&blue);
895 image->colormap[i].red=(MagickRealType) red;
896 image->colormap[i].red=(MagickRealType) red;
897 image->colormap[i].red=(MagickRealType) red;
901 Contrast enhance image.
905 image_view=AcquireAuthenticCacheView(image,exception);
906 #if defined(MAGICKCORE_OPENMP_SUPPORT)
907 #pragma omp parallel for schedule(static,4) shared(progress,status) \
908 dynamic_number_threads(image,image->columns,image->rows,1)
910 for (y=0; y < (ssize_t) image->rows; y++)
923 if (status == MagickFalse)
925 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
926 if (q == (Quantum *) NULL)
931 for (x=0; x < (ssize_t) image->columns; x++)
933 red=(double) GetPixelRed(image,q);
934 green=(double) GetPixelGreen(image,q);
935 blue=(double) GetPixelBlue(image,q);
936 Contrast(sign,&red,&green,&blue);
937 SetPixelRed(image,ClampToQuantum(red),q);
938 SetPixelGreen(image,ClampToQuantum(green),q);
939 SetPixelBlue(image,ClampToQuantum(blue),q);
940 q+=GetPixelChannels(image);
942 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
944 if (image->progress_monitor != (MagickProgressMonitor) NULL)
949 #if defined(MAGICKCORE_OPENMP_SUPPORT)
950 #pragma omp critical (MagickCore_ContrastImage)
952 proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
953 if (proceed == MagickFalse)
957 image_view=DestroyCacheView(image_view);
962 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
966 % C o n t r a s t S t r e t c h I m a g e %
970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
972 % ContrastStretchImage() is a simple image enhancement technique that attempts
973 % to improve the contrast in an image by 'stretching' the range of intensity
974 % values it contains to span a desired range of values. It differs from the
975 % more sophisticated histogram equalization in that it can only apply a
976 % linear scaling function to the image pixel values. As a result the
977 % 'enhancement' is less harsh.
979 % The format of the ContrastStretchImage method is:
981 % MagickBooleanType ContrastStretchImage(Image *image,
982 % const char *levels,ExceptionInfo *exception)
984 % A description of each parameter follows:
986 % o image: the image.
988 % o black_point: the black point.
990 % o white_point: the white point.
992 % o levels: Specify the levels where the black and white points have the
993 % range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
995 % o exception: return any errors or warnings in this structure.
998 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
999 const double black_point,const double white_point,ExceptionInfo *exception)
1001 #define MaxRange(color) ((double) ScaleQuantumToMap((Quantum) (color)))
1002 #define ContrastStretchImageTag "ContrastStretch/Image"
1029 Allocate histogram and stretch map.
1031 assert(image != (Image *) NULL);
1032 assert(image->signature == MagickSignature);
1033 if (image->debug != MagickFalse)
1034 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1035 black=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*black));
1036 white=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*white));
1037 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1038 sizeof(*histogram));
1039 stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1040 GetPixelChannels(image)*sizeof(*stretch_map));
1041 if ((black == (double *) NULL) || (white == (double *) NULL) ||
1042 (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1044 if (stretch_map != (double *) NULL)
1045 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1046 if (histogram != (double *) NULL)
1047 histogram=(double *) RelinquishMagickMemory(histogram);
1048 if (white != (double *) NULL)
1049 white=(double *) RelinquishMagickMemory(white);
1050 if (black != (double *) NULL)
1051 black=(double *) RelinquishMagickMemory(black);
1052 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
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)
1150 if (black[i] != white[i])
1151 stretch_map[GetPixelChannels(image)*j+i]=(double)
1152 ScaleMapToQuantum((double) (MaxMap*(j-black[i])/
1153 (white[i]-black[i])));
1156 if (image->storage_class == PseudoClass)
1162 Stretch-contrast colormap.
1164 for (j=0; j < (ssize_t) image->colors; j++)
1166 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1168 i=GetPixelChannelChannel(image,RedPixelChannel);
1169 if (black[i] != white[i])
1170 image->colormap[j].red=stretch_map[GetPixelChannels(image)*
1171 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+i;
1173 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1175 i=GetPixelChannelChannel(image,GreenPixelChannel);
1176 if (black[i] != white[i])
1177 image->colormap[j].green=stretch_map[GetPixelChannels(image)*
1178 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+i;
1180 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1182 i=GetPixelChannelChannel(image,BluePixelChannel);
1183 if (black[i] != white[i])
1184 image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
1185 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+i;
1187 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1189 i=GetPixelChannelChannel(image,AlphaPixelChannel);
1190 if (black[i] != white[i])
1191 image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
1192 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+i;
1197 Stretch-contrast image.
1201 image_view=AcquireAuthenticCacheView(image,exception);
1202 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1203 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1204 dynamic_number_threads(image,image->columns,image->rows,1)
1206 for (y=0; y < (ssize_t) image->rows; y++)
1214 if (status == MagickFalse)
1216 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1217 if (q == (Quantum *) NULL)
1222 for (x=0; x < (ssize_t) image->columns; x++)
1227 if (GetPixelMask(image,q) != 0)
1229 q+=GetPixelChannels(image);
1232 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1240 channel=GetPixelChannelChannel(image,i);
1241 traits=GetPixelChannelTraits(image,channel);
1242 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1244 q[i]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1245 ScaleQuantumToMap(q[i])+i]);
1247 q+=GetPixelChannels(image);
1249 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1251 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1256 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1257 #pragma omp critical (MagickCore_ContrastStretchImage)
1259 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1261 if (proceed == MagickFalse)
1265 image_view=DestroyCacheView(image_view);
1266 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1267 white=(double *) RelinquishMagickMemory(white);
1268 black=(double *) RelinquishMagickMemory(black);
1273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277 % E n h a n c e I m a g e %
1281 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1283 % EnhanceImage() applies a digital filter that improves the quality of a
1286 % The format of the EnhanceImage method is:
1288 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1290 % A description of each parameter follows:
1292 % o image: the image.
1294 % o exception: return any errors or warnings in this structure.
1297 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1299 #define EnhancePixel(weight) \
1300 mean=((double) r[i]+GetPixelChannel(enhance_image,channel,q))/2.0; \
1301 distance=(double) r[i]-(double) GetPixelChannel(enhance_image,channel,q); \
1302 distance_squared=QuantumScale*(2.0*((double) QuantumRange+1.0)+mean)* \
1303 distance*distance; \
1304 if (distance_squared < ((double) QuantumRange*(double) QuantumRange/25.0f)) \
1306 aggregate+=(weight)*r[i]; \
1307 total_weight+=(weight); \
1309 r+=GetPixelChannels(image);
1310 #define EnhanceImageTag "Enhance/Image"
1329 Initialize enhanced image attributes.
1331 assert(image != (const Image *) NULL);
1332 assert(image->signature == MagickSignature);
1333 if (image->debug != MagickFalse)
1334 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1335 assert(exception != (ExceptionInfo *) NULL);
1336 assert(exception->signature == MagickSignature);
1337 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1339 if (enhance_image == (Image *) NULL)
1340 return((Image *) NULL);
1341 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1343 enhance_image=DestroyImage(enhance_image);
1344 return((Image *) NULL);
1351 image_view=AcquireVirtualCacheView(image,exception);
1352 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1353 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1354 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1355 dynamic_number_threads(image,image->columns,image->rows,1)
1357 for (y=0; y < (ssize_t) image->rows; y++)
1359 register const Quantum
1371 if (status == MagickFalse)
1373 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1374 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1376 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1381 center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1382 for (x=0; x < (ssize_t) image->columns; x++)
1387 if (GetPixelMask(image,p) != 0)
1389 p+=GetPixelChannels(image);
1390 q+=GetPixelChannels(enhance_image);
1393 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1409 register const Quantum
1412 channel=GetPixelChannelChannel(image,i);
1413 traits=GetPixelChannelTraits(image,channel);
1414 enhance_traits=GetPixelChannelTraits(enhance_image,channel);
1415 if ((traits == UndefinedPixelTrait) ||
1416 (enhance_traits == UndefinedPixelTrait))
1418 SetPixelChannel(enhance_image,channel,p[center+i],q);
1419 if ((enhance_traits & CopyPixelTrait) != 0)
1422 Compute weighted average of target pixel color components.
1427 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1428 EnhancePixel(8.0); EnhancePixel(5.0);
1429 r=p+1*GetPixelChannels(image)*(image->columns+4);
1430 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1431 EnhancePixel(20.0); EnhancePixel(8.0);
1432 r=p+2*GetPixelChannels(image)*(image->columns+4);
1433 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1434 EnhancePixel(40.0); EnhancePixel(10.0);
1435 r=p+3*GetPixelChannels(image)*(image->columns+4);
1436 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1437 EnhancePixel(20.0); EnhancePixel(8.0);
1438 r=p+4*GetPixelChannels(image)*(image->columns+4);
1439 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1440 EnhancePixel(8.0); EnhancePixel(5.0);
1441 SetPixelChannel(enhance_image,channel,ClampToQuantum(aggregate/
1444 p+=GetPixelChannels(image);
1445 q+=GetPixelChannels(enhance_image);
1447 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1449 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1454 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1455 #pragma omp critical (MagickCore_EnhanceImage)
1457 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1458 if (proceed == MagickFalse)
1462 enhance_view=DestroyCacheView(enhance_view);
1463 image_view=DestroyCacheView(image_view);
1464 return(enhance_image);
1468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1472 % E q u a l i z e I m a g e %
1476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1478 % EqualizeImage() applies a histogram equalization to the image.
1480 % The format of the EqualizeImage method is:
1482 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
1484 % A description of each parameter follows:
1486 % o image: the image.
1488 % o exception: return any errors or warnings in this structure.
1491 MagickExport MagickBooleanType EqualizeImage(Image *image,
1492 ExceptionInfo *exception)
1494 #define EqualizeImageTag "Equalize/Image"
1506 black[CompositePixelChannel],
1510 white[CompositePixelChannel];
1522 Allocate and initialize histogram arrays.
1524 assert(image != (Image *) NULL);
1525 assert(image->signature == MagickSignature);
1526 if (image->debug != MagickFalse)
1527 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1528 equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1529 GetPixelChannels(image)*sizeof(*equalize_map));
1530 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,
1531 GetPixelChannels(image)*sizeof(*histogram));
1532 map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1533 GetPixelChannels(image)*sizeof(*map));
1534 if ((equalize_map == (double *) NULL) ||
1535 (histogram == (double *) NULL) ||
1536 (map == (double *) NULL))
1538 if (map != (double *) NULL)
1539 map=(double *) RelinquishMagickMemory(map);
1540 if (histogram != (double *) NULL)
1541 histogram=(double *) RelinquishMagickMemory(histogram);
1542 if (equalize_map != (double *) NULL)
1543 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1544 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1551 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1552 sizeof(*histogram));
1553 image_view=AcquireVirtualCacheView(image,exception);
1554 for (y=0; y < (ssize_t) image->rows; y++)
1556 register const Quantum
1562 if (status == MagickFalse)
1564 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1565 if (p == (const Quantum *) NULL)
1570 for (x=0; x < (ssize_t) image->columns; x++)
1575 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1576 histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++;
1577 p+=GetPixelChannels(image);
1580 image_view=DestroyCacheView(image_view);
1582 Integrate the histogram to get the equalization map.
1584 number_channels=GetPixelChannels(image);
1585 for (i=0; i < (ssize_t) number_channels; i++)
1594 for (j=0; j <= (ssize_t) MaxMap; j++)
1596 intensity+=histogram[GetPixelChannels(image)*j+i];
1597 map[GetPixelChannels(image)*j+i]=intensity;
1600 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
1601 sizeof(*equalize_map));
1602 number_channels=GetPixelChannels(image);
1603 for (i=0; i < (ssize_t) number_channels; i++)
1609 white[i]=map[GetPixelChannels(image)*MaxMap+i];
1610 if (black[i] != white[i])
1611 for (j=0; j <= (ssize_t) MaxMap; j++)
1612 equalize_map[GetPixelChannels(image)*j+i]=(double)
1613 ScaleMapToQuantum((double) ((MaxMap*(map[
1614 GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
1616 histogram=(double *) RelinquishMagickMemory(histogram);
1617 map=(double *) RelinquishMagickMemory(map);
1618 if (image->storage_class == PseudoClass)
1629 for (j=0; j < (ssize_t) image->colors; j++)
1631 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1633 channel=GetPixelChannelChannel(image,RedPixelChannel);
1634 if (black[channel] != white[channel])
1635 image->colormap[j].red=equalize_map[GetPixelChannels(image)*
1636 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+
1639 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1641 channel=GetPixelChannelChannel(image,GreenPixelChannel);
1642 if (black[channel] != white[channel])
1643 image->colormap[j].green=equalize_map[GetPixelChannels(image)*
1644 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+
1647 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1649 channel=GetPixelChannelChannel(image,BluePixelChannel);
1650 if (black[channel] != white[channel])
1651 image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
1652 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+
1655 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1657 channel=GetPixelChannelChannel(image,AlphaPixelChannel);
1658 if (black[channel] != white[channel])
1659 image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
1660 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+
1669 image_view=AcquireAuthenticCacheView(image,exception);
1670 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1671 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1672 dynamic_number_threads(image,image->columns,image->rows,1)
1674 for (y=0; y < (ssize_t) image->rows; y++)
1682 if (status == MagickFalse)
1684 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1685 if (q == (Quantum *) NULL)
1690 for (x=0; x < (ssize_t) image->columns; x++)
1695 if (GetPixelMask(image,q) != 0)
1697 q+=GetPixelChannels(image);
1700 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1708 channel=GetPixelChannelChannel(image,i);
1709 traits=GetPixelChannelTraits(image,channel);
1710 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1712 q[i]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
1713 ScaleQuantumToMap(q[i])+i]);
1715 q+=GetPixelChannels(image);
1717 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1719 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1724 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1725 #pragma omp critical (MagickCore_EqualizeImage)
1727 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1728 if (proceed == MagickFalse)
1732 image_view=DestroyCacheView(image_view);
1733 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1742 % G a m m a I m a g e %
1746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1748 % GammaImage() gamma-corrects a particular image channel. The same
1749 % image viewed on different devices will have perceptual differences in the
1750 % way the image's intensities are represented on the screen. Specify
1751 % individual gamma levels for the red, green, and blue channels, or adjust
1752 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
1754 % You can also reduce the influence of a particular channel with a gamma
1757 % The format of the GammaImage method is:
1759 % MagickBooleanType GammaImage(Image *image,const double gamma,
1760 % ExceptionInfo *exception)
1762 % A description of each parameter follows:
1764 % o image: the image.
1766 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1768 % o gamma: the image gamma.
1771 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
1772 ExceptionInfo *exception)
1774 #define GammaCorrectImageTag "GammaCorrect/Image"
1795 Allocate and initialize gamma maps.
1797 assert(image != (Image *) NULL);
1798 assert(image->signature == MagickSignature);
1799 if (image->debug != MagickFalse)
1800 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1803 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1804 if (gamma_map == (Quantum *) NULL)
1805 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1807 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1809 for (i=0; i <= (ssize_t) MaxMap; i++)
1810 gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
1811 MaxMap,1.0/gamma)));
1812 if (image->storage_class == PseudoClass)
1813 for (i=0; i < (ssize_t) image->colors; i++)
1816 Gamma-correct colormap.
1818 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1819 image->colormap[i].red=(double) gamma_map[
1820 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))];
1821 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1822 image->colormap[i].green=(double) gamma_map[
1823 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))];
1824 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1825 image->colormap[i].blue=(double) gamma_map[
1826 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))];
1827 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1828 image->colormap[i].alpha=(double) gamma_map[
1829 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].alpha))];
1832 Gamma-correct image.
1836 image_view=AcquireAuthenticCacheView(image,exception);
1837 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1838 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1839 dynamic_number_threads(image,image->columns,image->rows,1)
1841 for (y=0; y < (ssize_t) image->rows; y++)
1849 if (status == MagickFalse)
1851 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1852 if (q == (Quantum *) NULL)
1857 for (x=0; x < (ssize_t) image->columns; x++)
1862 if (GetPixelMask(image,q) != 0)
1864 q+=GetPixelChannels(image);
1867 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1875 channel=GetPixelChannelChannel(image,i);
1876 traits=GetPixelChannelTraits(image,channel);
1877 if ((traits & UpdatePixelTrait) == 0)
1879 q[i]=gamma_map[ScaleQuantumToMap(q[i])];
1881 q+=GetPixelChannels(image);
1883 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1885 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1890 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1891 #pragma omp critical (MagickCore_GammaImage)
1893 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
1895 if (proceed == MagickFalse)
1899 image_view=DestroyCacheView(image_view);
1900 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
1901 if (image->gamma != 0.0)
1902 image->gamma*=gamma;
1907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1911 % H a l d C l u t I m a g e %
1915 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1917 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
1918 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
1919 % Create it with the HALD coder. You can apply any color transformation to
1920 % the Hald image and then use this method to apply the transform to the
1923 % The format of the HaldClutImage method is:
1925 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
1926 % ExceptionInfo *exception)
1928 % A description of each parameter follows:
1930 % o image: the image, which is replaced by indexed CLUT values
1932 % o hald_image: the color lookup table image for replacement color values.
1934 % o exception: return any errors or warnings in this structure.
1938 static inline size_t MagickMin(const size_t x,const size_t y)
1945 MagickExport MagickBooleanType HaldClutImage(Image *image,
1946 const Image *hald_image,ExceptionInfo *exception)
1948 #define HaldClutImageTag "Clut/Image"
1950 typedef struct _HaldInfo
1982 assert(image != (Image *) NULL);
1983 assert(image->signature == MagickSignature);
1984 if (image->debug != MagickFalse)
1985 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1986 assert(hald_image != (Image *) NULL);
1987 assert(hald_image->signature == MagickSignature);
1988 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1989 return(MagickFalse);
1990 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1991 (void) TransformImageColorspace(image,RGBColorspace,exception);
1992 if (image->alpha_trait != BlendPixelTrait)
1993 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1999 length=MagickMin(hald_image->columns,hald_image->rows);
2000 for (level=2; (level*level*level) < length; level++) ;
2002 cube_size=level*level;
2003 width=(double) hald_image->columns;
2004 GetPixelInfo(hald_image,&zero);
2005 hald_view=AcquireVirtualCacheView(hald_image,exception);
2006 image_view=AcquireAuthenticCacheView(image,exception);
2007 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2008 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2009 dynamic_number_threads(image,image->columns,image->rows,1)
2011 for (y=0; y < (ssize_t) image->rows; y++)
2019 if (status == MagickFalse)
2021 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2022 if (q == (Quantum *) NULL)
2027 for (x=0; x < (ssize_t) image->columns; x++)
2042 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2043 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2044 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2045 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2046 point.x-=floor(point.x);
2047 point.y-=floor(point.y);
2048 point.z-=floor(point.z);
2050 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2051 fmod(offset,width),floor(offset/width),&pixel1,exception);
2053 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2054 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2056 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2059 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2060 fmod(offset,width),floor(offset/width),&pixel1,exception);
2061 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2062 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2064 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2067 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2069 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2070 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2071 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2072 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2073 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2074 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2075 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2076 (image->colorspace == CMYKColorspace))
2077 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2078 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2079 (image->alpha_trait == BlendPixelTrait))
2080 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2081 q+=GetPixelChannels(image);
2083 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2085 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2090 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2091 #pragma omp critical (MagickCore_HaldClutImage)
2093 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2094 if (proceed == MagickFalse)
2098 hald_view=DestroyCacheView(hald_view);
2099 image_view=DestroyCacheView(image_view);
2104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2108 % L e v e l I m a g e %
2112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2114 % LevelImage() adjusts the levels of a particular image channel by
2115 % scaling the colors falling between specified white and black points to
2116 % the full available quantum range.
2118 % The parameters provided represent the black, and white points. The black
2119 % point specifies the darkest color in the image. Colors darker than the
2120 % black point are set to zero. White point specifies the lightest color in
2121 % the image. Colors brighter than the white point are set to the maximum
2124 % If a '!' flag is given, map black and white colors to the given levels
2125 % rather than mapping those levels to black and white. See
2126 % LevelizeImage() below.
2128 % Gamma specifies a gamma correction to apply to the image.
2130 % The format of the LevelImage method is:
2132 % MagickBooleanType LevelImage(Image *image,const double black_point,
2133 % const double white_point,const double gamma,ExceptionInfo *exception)
2135 % A description of each parameter follows:
2137 % o image: the image.
2139 % o black_point: The level to map zero (black) to.
2141 % o white_point: The level to map QuantumRange (white) to.
2143 % o exception: return any errors or warnings in this structure.
2147 static inline double LevelPixel(const double black_point,
2148 const double white_point,const double gamma,const double pixel)
2154 scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0;
2155 level_pixel=(double) QuantumRange*pow(scale*((double) pixel-
2156 black_point),1.0/gamma);
2157 return(level_pixel);
2160 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2161 const double white_point,const double gamma,ExceptionInfo *exception)
2163 #define LevelImageTag "Level/Image"
2181 Allocate and initialize levels map.
2183 assert(image != (Image *) NULL);
2184 assert(image->signature == MagickSignature);
2185 if (image->debug != MagickFalse)
2186 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2187 if (image->storage_class == PseudoClass)
2188 for (i=0; i < (ssize_t) image->colors; i++)
2193 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2194 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2195 white_point,gamma,image->colormap[i].red));
2196 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2197 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2198 white_point,gamma,image->colormap[i].green));
2199 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2200 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2201 white_point,gamma,image->colormap[i].blue));
2202 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2203 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2204 white_point,gamma,image->colormap[i].alpha));
2211 image_view=AcquireAuthenticCacheView(image,exception);
2212 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2213 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2214 dynamic_number_threads(image,image->columns,image->rows,1)
2216 for (y=0; y < (ssize_t) image->rows; y++)
2224 if (status == MagickFalse)
2226 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2227 if (q == (Quantum *) NULL)
2232 for (x=0; x < (ssize_t) image->columns; x++)
2237 if (GetPixelMask(image,q) != 0)
2239 q+=GetPixelChannels(image);
2242 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2250 channel=GetPixelChannelChannel(image,i);
2251 traits=GetPixelChannelTraits(image,channel);
2252 if ((traits & UpdatePixelTrait) == 0)
2254 q[i]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2257 q+=GetPixelChannels(image);
2259 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2261 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2266 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2267 #pragma omp critical (MagickCore_LevelImage)
2269 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2270 if (proceed == MagickFalse)
2274 image_view=DestroyCacheView(image_view);
2279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2283 % L e v e l i z e I m a g e %
2287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2289 % LevelizeImage() applies the reversed LevelImage() operation to just
2290 % the specific channels specified. It compresses the full range of color
2291 % values, so that they lie between the given black and white points. Gamma is
2292 % applied before the values are mapped.
2294 % LevelizeImage() can be called with by using a +level command line
2295 % API option, or using a '!' on a -level or LevelImage() geometry string.
2297 % It can be used to de-contrast a greyscale image to the exact levels
2298 % specified. Or by using specific levels for each channel of an image you
2299 % can convert a gray-scale image to any linear color gradient, according to
2302 % The format of the LevelizeImage method is:
2304 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
2305 % const double white_point,const double gamma,ExceptionInfo *exception)
2307 % A description of each parameter follows:
2309 % o image: the image.
2311 % o black_point: The level to map zero (black) to.
2313 % o white_point: The level to map QuantumRange (white) to.
2315 % o gamma: adjust gamma by this factor before mapping values.
2317 % o exception: return any errors or warnings in this structure.
2320 MagickExport MagickBooleanType LevelizeImage(Image *image,
2321 const double black_point,const double white_point,const double gamma,
2322 ExceptionInfo *exception)
2324 #define LevelizeImageTag "Levelize/Image"
2325 #define LevelizeValue(x) (ClampToQuantum((pow((double) (QuantumScale*(x)), \
2326 1.0/gamma))*(white_point-black_point)+black_point))
2344 Allocate and initialize levels map.
2346 assert(image != (Image *) NULL);
2347 assert(image->signature == MagickSignature);
2348 if (image->debug != MagickFalse)
2349 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2350 if (IsGrayColorspace(image->colorspace) != MagickFalse)
2351 (void) SetImageColorspace(image,RGBColorspace,exception);
2352 if (image->storage_class == PseudoClass)
2353 for (i=0; i < (ssize_t) image->colors; i++)
2358 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2359 image->colormap[i].red=(double) LevelizeValue(
2360 image->colormap[i].red);
2361 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2362 image->colormap[i].green=(double) LevelizeValue(
2363 image->colormap[i].green);
2364 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2365 image->colormap[i].blue=(double) LevelizeValue(
2366 image->colormap[i].blue);
2367 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2368 image->colormap[i].alpha=(double) LevelizeValue(
2369 image->colormap[i].alpha);
2376 image_view=AcquireAuthenticCacheView(image,exception);
2377 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2378 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2379 dynamic_number_threads(image,image->columns,image->rows,1)
2381 for (y=0; y < (ssize_t) image->rows; y++)
2389 if (status == MagickFalse)
2391 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2392 if (q == (Quantum *) NULL)
2397 for (x=0; x < (ssize_t) image->columns; x++)
2402 if (GetPixelMask(image,q) != 0)
2404 q+=GetPixelChannels(image);
2407 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2415 channel=GetPixelChannelChannel(image,i);
2416 traits=GetPixelChannelTraits(image,channel);
2417 if ((traits & UpdatePixelTrait) == 0)
2419 q[i]=LevelizeValue(q[i]);
2421 q+=GetPixelChannels(image);
2423 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2425 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2430 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2431 #pragma omp critical (MagickCore_LevelizeImage)
2433 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2434 if (proceed == MagickFalse)
2438 image_view=DestroyCacheView(image_view);
2443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2447 % L e v e l I m a g e C o l o r s %
2451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2453 % LevelImageColors() maps the given color to "black" and "white" values,
2454 % linearly spreading out the colors, and level values on a channel by channel
2455 % bases, as per LevelImage(). The given colors allows you to specify
2456 % different level ranges for each of the color channels separately.
2458 % If the boolean 'invert' is set true the image values will modifyed in the
2459 % reverse direction. That is any existing "black" and "white" colors in the
2460 % image will become the color values given, with all other values compressed
2461 % appropriatally. This effectivally maps a greyscale gradient into the given
2464 % The format of the LevelImageColors method is:
2466 % MagickBooleanType LevelImageColors(Image *image,
2467 % const PixelInfo *black_color,const PixelInfo *white_color,
2468 % const MagickBooleanType invert,ExceptionInfo *exception)
2470 % A description of each parameter follows:
2472 % o image: the image.
2474 % o black_color: The color to map black to/from
2476 % o white_point: The color to map white to/from
2478 % o invert: if true map the colors (levelize), rather than from (level)
2480 % o exception: return any errors or warnings in this structure.
2483 MagickExport MagickBooleanType LevelImageColors(Image *image,
2484 const PixelInfo *black_color,const PixelInfo *white_color,
2485 const MagickBooleanType invert,ExceptionInfo *exception)
2494 Allocate and initialize levels map.
2496 assert(image != (Image *) NULL);
2497 assert(image->signature == MagickSignature);
2498 if (image->debug != MagickFalse)
2499 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2501 if (invert == MagickFalse)
2503 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2505 channel_mask=SetImageChannelMask(image,RedChannel);
2506 status|=LevelImage(image,black_color->red,white_color->red,1.0,
2508 (void) SetImageChannelMask(image,channel_mask);
2510 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2512 channel_mask=SetImageChannelMask(image,GreenChannel);
2513 status|=LevelImage(image,black_color->green,white_color->green,1.0,
2515 (void) SetImageChannelMask(image,channel_mask);
2517 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2519 channel_mask=SetImageChannelMask(image,BlueChannel);
2520 status|=LevelImage(image,black_color->blue,white_color->blue,1.0,
2522 (void) SetImageChannelMask(image,channel_mask);
2524 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2525 (image->colorspace == CMYKColorspace))
2527 channel_mask=SetImageChannelMask(image,BlackChannel);
2528 status|=LevelImage(image,black_color->black,white_color->black,1.0,
2530 (void) SetImageChannelMask(image,channel_mask);
2532 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2533 (image->alpha_trait == BlendPixelTrait))
2535 channel_mask=SetImageChannelMask(image,AlphaChannel);
2536 status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2538 (void) SetImageChannelMask(image,channel_mask);
2543 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2545 channel_mask=SetImageChannelMask(image,RedChannel);
2546 status|=LevelizeImage(image,black_color->red,white_color->red,1.0,
2548 (void) SetImageChannelMask(image,channel_mask);
2550 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2552 channel_mask=SetImageChannelMask(image,GreenChannel);
2553 status|=LevelizeImage(image,black_color->green,white_color->green,1.0,
2555 (void) SetImageChannelMask(image,channel_mask);
2557 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2559 channel_mask=SetImageChannelMask(image,BlueChannel);
2560 status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2562 (void) SetImageChannelMask(image,channel_mask);
2564 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2565 (image->colorspace == CMYKColorspace))
2567 channel_mask=SetImageChannelMask(image,BlackChannel);
2568 status|=LevelizeImage(image,black_color->black,white_color->black,1.0,
2570 (void) SetImageChannelMask(image,channel_mask);
2572 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2573 (image->alpha_trait == BlendPixelTrait))
2575 channel_mask=SetImageChannelMask(image,AlphaChannel);
2576 status|=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2578 (void) SetImageChannelMask(image,channel_mask);
2581 return(status == 0 ? MagickFalse : MagickTrue);
2585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2589 % L i n e a r S t r e t c h I m a g e %
2593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2595 % LinearStretchImage() discards any pixels below the black point and above
2596 % the white point and levels the remaining pixels.
2598 % The format of the LinearStretchImage method is:
2600 % MagickBooleanType LinearStretchImage(Image *image,
2601 % const double black_point,const double white_point,
2602 % ExceptionInfo *exception)
2604 % A description of each parameter follows:
2606 % o image: the image.
2608 % o black_point: the black point.
2610 % o white_point: the white point.
2612 % o exception: return any errors or warnings in this structure.
2615 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2616 const double black_point,const double white_point,ExceptionInfo *exception)
2618 #define LinearStretchImageTag "LinearStretch/Image"
2636 Allocate histogram and linear map.
2638 assert(image != (Image *) NULL);
2639 assert(image->signature == MagickSignature);
2640 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,
2641 sizeof(*histogram));
2642 if (histogram == (double *) NULL)
2643 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2648 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2649 image_view=AcquireVirtualCacheView(image,exception);
2650 for (y=0; y < (ssize_t) image->rows; y++)
2652 register const Quantum
2658 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2659 if (p == (const Quantum *) NULL)
2661 for (x=0; x < (ssize_t) image->columns; x++)
2666 intensity=GetPixelIntensity(image,p);
2667 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2668 p+=GetPixelChannels(image);
2671 image_view=DestroyCacheView(image_view);
2673 Find the histogram boundaries by locating the black and white point levels.
2676 for (black=0; black < (ssize_t) MaxMap; black++)
2678 intensity+=histogram[black];
2679 if (intensity >= black_point)
2683 for (white=(ssize_t) MaxMap; white != 0; white--)
2685 intensity+=histogram[white];
2686 if (intensity >= white_point)
2689 histogram=(double *) RelinquishMagickMemory(histogram);
2690 status=LevelImage(image,(double) black,(double) white,1.0,exception);
2695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2699 % M o d u l a t e I m a g e %
2703 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2705 % ModulateImage() lets you control the brightness, saturation, and hue
2706 % of an image. Modulate represents the brightness, saturation, and hue
2707 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
2708 % modulation is lightness, saturation, and hue. For HWB, use blackness,
2709 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
2711 % The format of the ModulateImage method is:
2713 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
2714 % ExceptionInfo *exception)
2716 % A description of each parameter follows:
2718 % o image: the image.
2720 % o modulate: Define the percent change in brightness, saturation, and hue.
2722 % o exception: return any errors or warnings in this structure.
2726 static inline void ModulateHCL(const double percent_hue,
2727 const double percent_chroma,const double percent_luma,double *red,
2728 double *green,double *blue)
2736 Increase or decrease color luma, chroma, or hue.
2738 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2739 hue+=0.5*(0.01*percent_hue-1.0);
2744 chroma*=0.01*percent_chroma;
2745 luma*=0.01*percent_luma;
2746 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2749 static inline void ModulateHSB(const double percent_hue,
2750 const double percent_saturation,const double percent_brightness,double *red,
2751 double *green,double *blue)
2759 Increase or decrease color brightness, saturation, or hue.
2761 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2762 hue+=0.5*(0.01*percent_hue-1.0);
2767 saturation*=0.01*percent_saturation;
2768 brightness*=0.01*percent_brightness;
2769 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2772 static inline void ModulateHSL(const double percent_hue,
2773 const double percent_saturation,const double percent_lightness,double *red,
2774 double *green,double *blue)
2782 Increase or decrease color lightness, saturation, or hue.
2784 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
2785 hue+=0.5*(0.01*percent_hue-1.0);
2790 saturation*=0.01*percent_saturation;
2791 lightness*=0.01*percent_lightness;
2792 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
2795 static inline void ModulateHWB(const double percent_hue,
2796 const double percent_whiteness,const double percent_blackness,double *red,
2797 double *green,double *blue)
2805 Increase or decrease color blackness, whiteness, or hue.
2807 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
2808 hue+=0.5*(0.01*percent_hue-1.0);
2813 blackness*=0.01*percent_blackness;
2814 whiteness*=0.01*percent_whiteness;
2815 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
2818 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
2819 ExceptionInfo *exception)
2821 #define ModulateImageTag "Modulate/Image"
2856 Initialize modulate table.
2858 assert(image != (Image *) NULL);
2859 assert(image->signature == MagickSignature);
2860 if (image->debug != MagickFalse)
2861 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2862 if (modulate == (char *) NULL)
2863 return(MagickFalse);
2864 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
2865 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2866 flags=ParseGeometry(modulate,&geometry_info);
2867 percent_brightness=geometry_info.rho;
2868 percent_saturation=geometry_info.sigma;
2869 if ((flags & SigmaValue) == 0)
2870 percent_saturation=100.0;
2871 percent_hue=geometry_info.xi;
2872 if ((flags & XiValue) == 0)
2874 colorspace=UndefinedColorspace;
2875 artifact=GetImageArtifact(image,"modulate:colorspace");
2876 if (artifact != (const char *) NULL)
2877 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
2878 MagickFalse,artifact);
2879 if (image->storage_class == PseudoClass)
2880 for (i=0; i < (ssize_t) image->colors; i++)
2890 red=image->colormap[i].red;
2891 green=image->colormap[i].green;
2892 blue=image->colormap[i].blue;
2897 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
2903 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2910 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2916 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2927 image_view=AcquireAuthenticCacheView(image,exception);
2928 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2929 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2930 dynamic_number_threads(image,image->columns,image->rows,1)
2932 for (y=0; y < (ssize_t) image->rows; y++)
2940 if (status == MagickFalse)
2942 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2943 if (q == (Quantum *) NULL)
2948 for (x=0; x < (ssize_t) image->columns; x++)
2955 red=(double) GetPixelRed(image,q);
2956 green=(double) GetPixelGreen(image,q);
2957 blue=(double) GetPixelBlue(image,q);
2962 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
2968 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
2975 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
2981 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
2986 SetPixelRed(image,ClampToQuantum(red),q);
2987 SetPixelGreen(image,ClampToQuantum(green),q);
2988 SetPixelBlue(image,ClampToQuantum(blue),q);
2989 q+=GetPixelChannels(image);
2991 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2993 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2998 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2999 #pragma omp critical (MagickCore_ModulateImage)
3001 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3002 if (proceed == MagickFalse)
3006 image_view=DestroyCacheView(image_view);
3011 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3015 % N e g a t e I m a g e %
3019 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3021 % NegateImage() negates the colors in the reference image. The grayscale
3022 % option means that only grayscale values within the image are negated.
3024 % The format of the NegateImage method is:
3026 % MagickBooleanType NegateImage(Image *image,
3027 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3029 % A description of each parameter follows:
3031 % o image: the image.
3033 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3035 % o exception: return any errors or warnings in this structure.
3038 MagickExport MagickBooleanType NegateImage(Image *image,
3039 const MagickBooleanType grayscale,ExceptionInfo *exception)
3041 #define NegateImageTag "Negate/Image"
3058 assert(image != (Image *) NULL);
3059 assert(image->signature == MagickSignature);
3060 if (image->debug != MagickFalse)
3061 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3062 if (image->storage_class == PseudoClass)
3063 for (i=0; i < (ssize_t) image->colors; i++)
3068 if (grayscale != MagickFalse)
3069 if ((image->colormap[i].red != image->colormap[i].green) ||
3070 (image->colormap[i].green != image->colormap[i].blue))
3072 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3073 image->colormap[i].red=QuantumRange-image->colormap[i].red;
3074 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3075 image->colormap[i].green=QuantumRange-image->colormap[i].green;
3076 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3077 image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3084 image_view=AcquireAuthenticCacheView(image,exception);
3085 if (grayscale != MagickFalse)
3087 for (y=0; y < (ssize_t) image->rows; y++)
3098 if (status == MagickFalse)
3100 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3102 if (q == (Quantum *) NULL)
3107 for (x=0; x < (ssize_t) image->columns; x++)
3112 if ((GetPixelMask(image,q) != 0) ||
3113 (IsPixelGray(image,q) != MagickFalse))
3115 q+=GetPixelChannels(image);
3118 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3126 channel=GetPixelChannelChannel(image,i);
3127 traits=GetPixelChannelTraits(image,channel);
3128 if ((traits & UpdatePixelTrait) == 0)
3130 q[i]=QuantumRange-q[i];
3132 q+=GetPixelChannels(image);
3134 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3135 if (sync == MagickFalse)
3137 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3142 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3143 #pragma omp critical (MagickCore_NegateImage)
3145 proceed=SetImageProgress(image,NegateImageTag,progress++,
3147 if (proceed == MagickFalse)
3151 image_view=DestroyCacheView(image_view);
3157 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3158 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3159 dynamic_number_threads(image,image->columns,image->rows,1)
3161 for (y=0; y < (ssize_t) image->rows; y++)
3169 if (status == MagickFalse)
3171 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3172 if (q == (Quantum *) NULL)
3177 for (x=0; x < (ssize_t) image->columns; x++)
3182 if (GetPixelMask(image,q) != 0)
3184 q+=GetPixelChannels(image);
3187 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3195 channel=GetPixelChannelChannel(image,i);
3196 traits=GetPixelChannelTraits(image,channel);
3197 if ((traits & UpdatePixelTrait) == 0)
3199 q[i]=QuantumRange-q[i];
3201 q+=GetPixelChannels(image);
3203 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3205 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3210 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3211 #pragma omp critical (MagickCore_NegateImage)
3213 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3214 if (proceed == MagickFalse)
3218 image_view=DestroyCacheView(image_view);
3223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3227 % N o r m a l i z e I m a g e %
3231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3233 % The NormalizeImage() method enhances the contrast of a color image by
3234 % mapping the darkest 2 percent of all pixel to black and the brightest
3235 % 1 percent to white.
3237 % The format of the NormalizeImage method is:
3239 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3241 % A description of each parameter follows:
3243 % o image: the image.
3245 % o exception: return any errors or warnings in this structure.
3248 MagickExport MagickBooleanType NormalizeImage(Image *image,
3249 ExceptionInfo *exception)
3255 black_point=(double) image->columns*image->rows*0.0015;
3256 white_point=(double) image->columns*image->rows*0.9995;
3257 return(ContrastStretchImage(image,black_point,white_point,exception));
3261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265 % S i g m o i d a l C o n t r a s t I m a g e %
3269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3271 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3272 % sigmoidal contrast algorithm. Increase the contrast of the image using a
3273 % sigmoidal transfer function without saturating highlights or shadows.
3274 % Contrast indicates how much to increase the contrast (0 is none; 3 is
3275 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
3276 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
3277 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
3280 % The format of the SigmoidalContrastImage method is:
3282 % MagickBooleanType SigmoidalContrastImage(Image *image,
3283 % const MagickBooleanType sharpen,const char *levels,
3284 % ExceptionInfo *exception)
3286 % A description of each parameter follows:
3288 % o image: the image.
3290 % o sharpen: Increase or decrease image contrast.
3292 % o contrast: strength of the contrast, the larger the number the more
3293 % 'threshold-like' it becomes.
3295 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3297 % o exception: return any errors or warnings in this structure.
3302 ImageMagick 6 has a version of this function which uses LUTs.
3306 Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
3309 The first version, based on the hyperbolic tangent tanh, when combined with
3310 the scaling step, is an exact arithmetic clone of the the sigmoid function
3311 based on the logistic curve. The equivalence is based on the identity
3313 1/(1+exp(-t)) = (1+tanh(t/2))/2
3315 (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3316 scaled sigmoidal derivation is invariant under affine transformations of
3319 The tanh version is almost certainly more accurate and cheaper. The 0.5
3320 factor in the argument is to clone the legacy ImageMagick behavior. The
3321 reason for making the define depend on atanh even though it only uses tanh
3322 has to do with the construction of the inverse of the scaled sigmoidal.
3324 #if defined(MAGICKCORE_HAVE_ATANH)
3325 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3327 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3330 Scaled sigmoidal function:
3332 ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3333 ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3335 See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
3336 http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
3337 of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
3338 zero. This is fixed below by exiting immediately when contrast is small,
3339 leaving the image (or colormap) unmodified. This appears to be safe because
3340 the series expansion of the logistic sigmoidal function around x=b is
3344 so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3346 #define ScaledSigmoidal(a,b,x) ( \
3347 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3348 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3350 Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
3351 may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
3352 sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
3353 when creating a LUT from in gamut values, hence the branching. In
3354 addition, HDRI may have out of gamut values.
3355 InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
3356 It is only a right inverse. This is unavoidable.
3358 static inline double InverseScaledSigmoidal(const double a,const double b,
3361 const double sig0=Sigmoidal(a,b,0.0);
3362 const double sig1=Sigmoidal(a,b,1.0);
3363 const double argument=(sig1-sig0)*x+sig0;
3364 const double clamped=
3366 #if defined(MAGICKCORE_HAVE_ATANH)
3367 argument < -1+MagickEpsilon
3371 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3373 return(b+(2.0/a)*atanh(clamped));
3375 argument < MagickEpsilon
3379 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3381 return(b-log(1.0/clamped-1.0)/a);
3385 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3386 const MagickBooleanType sharpen,const double contrast,const double midpoint,
3387 ExceptionInfo *exception)
3389 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
3390 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3391 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3392 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3393 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3410 assert(image != (Image *) NULL);
3411 assert(image->signature == MagickSignature);
3412 if (image->debug != MagickFalse)
3413 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3415 Side effect: may clamp values unless contrast<MagickEpsilon, in which
3416 case nothing is done.
3418 if (contrast < MagickEpsilon)
3421 Sigmoidal-contrast enhance colormap.
3423 if (image->storage_class == PseudoClass)
3428 if (sharpen != MagickFalse)
3429 for (i=0; i < (ssize_t) image->colors; i++)
3431 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3432 image->colormap[i].red=ScaledSig(image->colormap[i].red);
3433 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3434 image->colormap[i].green=ScaledSig(image->colormap[i].green);
3435 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3436 image->colormap[i].blue=ScaledSig(image->colormap[i].blue);
3437 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3438 image->colormap[i].alpha=ScaledSig(image->colormap[i].alpha);
3441 for (i=0; i < (ssize_t) image->colors; i++)
3443 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3444 image->colormap[i].red=InverseScaledSig(image->colormap[i].red);
3445 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3446 image->colormap[i].green=InverseScaledSig(image->colormap[i].green);
3447 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3448 image->colormap[i].blue=InverseScaledSig(image->colormap[i].blue);
3449 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3450 image->colormap[i].alpha=InverseScaledSig(image->colormap[i].alpha);
3454 Sigmoidal-contrast enhance image.
3458 image_view=AcquireAuthenticCacheView(image,exception);
3459 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3460 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3461 dynamic_number_threads(image,image->columns,image->rows,1)
3463 for (y=0; y < (ssize_t) image->rows; y++)
3471 if (status == MagickFalse)
3473 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3474 if (q == (Quantum *) NULL)
3479 for (x=0; x < (ssize_t) image->columns; x++)
3484 if (GetPixelMask(image,q) != 0)
3486 q+=GetPixelChannels(image);
3489 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3497 channel=GetPixelChannelChannel(image,i);
3498 traits=GetPixelChannelTraits(image,channel);
3499 if ((traits & UpdatePixelTrait) == 0)
3501 if (sharpen != MagickFalse)
3502 q[i]=ScaledSig(q[i]);
3504 q[i]=InverseScaledSig(q[i]);
3506 q+=GetPixelChannels(image);
3508 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3510 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3515 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3516 #pragma omp critical (MagickCore_SigmoidalContrastImage)
3518 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3520 if (proceed == MagickFalse)
3524 image_view=DestroyCacheView(image_view);