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-2014 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/attribute.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/color.h"
50 #include "MagickCore/color-private.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/composite-private.h"
54 #include "MagickCore/enhance.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/fx.h"
58 #include "MagickCore/gem.h"
59 #include "MagickCore/gem-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/memory_.h"
65 #include "MagickCore/monitor.h"
66 #include "MagickCore/monitor-private.h"
67 #include "MagickCore/option.h"
68 #include "MagickCore/pixel.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/quantum.h"
71 #include "MagickCore/quantum-private.h"
72 #include "MagickCore/resample.h"
73 #include "MagickCore/resample-private.h"
74 #include "MagickCore/resource_.h"
75 #include "MagickCore/statistic.h"
76 #include "MagickCore/string_.h"
77 #include "MagickCore/string-private.h"
78 #include "MagickCore/thread-private.h"
79 #include "MagickCore/threshold.h"
80 #include "MagickCore/token.h"
81 #include "MagickCore/xml-tree.h"
82 #include "MagickCore/xml-tree-private.h"
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89 % A u t o G a m m a I m a g e %
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95 % AutoGammaImage() extract the 'mean' from the image and adjust the image
96 % to try make set its gamma appropriatally.
98 % The format of the AutoGammaImage method is:
100 % MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception)
102 % A description of each parameter follows:
104 % o image: The image to auto-level
106 % o exception: return any errors or warnings in this structure.
109 MagickExport MagickBooleanType AutoGammaImage(Image *image,
110 ExceptionInfo *exception)
125 if (image->channel_mask == DefaultChannels)
128 Apply gamma correction equally across all given channels.
130 (void) GetImageMean(image,&mean,&sans,exception);
131 gamma=log(mean*QuantumScale)/log_mean;
132 return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception));
135 Auto-gamma each channel separately.
138 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
143 PixelChannel channel=GetPixelChannelChannel(image,i);
144 PixelTrait traits=GetPixelChannelTraits(image,channel);
145 if ((traits & UpdatePixelTrait) == 0)
147 channel_mask=SetImageChannelMask(image,(ChannelType) (1 << i));
148 status=GetImageMean(image,&mean,&sans,exception);
149 gamma=log(mean*QuantumScale)/log_mean;
150 status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception);
151 (void) SetImageChannelMask(image,channel_mask);
152 if( IfMagickFalse(status) )
155 return(status != 0 ? MagickTrue : MagickFalse);
159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163 % A u t o L e v e l I m a g e %
167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
169 % AutoLevelImage() adjusts the levels of a particular image channel by
170 % scaling the minimum and maximum values to the full quantum range.
172 % The format of the LevelImage method is:
174 % MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception)
176 % A description of each parameter follows:
178 % o image: The image to auto-level
180 % o exception: return any errors or warnings in this structure.
183 MagickExport MagickBooleanType AutoLevelImage(Image *image,
184 ExceptionInfo *exception)
186 return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
194 % B r i g h t n e s s C o n t r a s t I m a g e %
198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
200 % BrightnessContrastImage() changes the brightness and/or contrast of an
201 % image. It converts the brightness and contrast parameters into slope and
202 % intercept and calls a polynomical function to apply to the image.
204 % The format of the BrightnessContrastImage method is:
206 % MagickBooleanType BrightnessContrastImage(Image *image,
207 % const double brightness,const double contrast,ExceptionInfo *exception)
209 % A description of each parameter follows:
211 % o image: the image.
213 % o brightness: the brightness percent (-100 .. 100).
215 % o contrast: the contrast percent (-100 .. 100).
217 % o exception: return any errors or warnings in this structure.
220 MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
221 const double brightness,const double contrast,ExceptionInfo *exception)
223 #define BrightnessContastImageTag "BrightnessContast/Image"
235 Compute slope and intercept.
237 assert(image != (Image *) NULL);
238 assert(image->signature == MagickSignature);
239 if( IfMagickTrue(image->debug) )
240 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
242 slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
245 intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
246 coefficients[0]=slope;
247 coefficients[1]=intercept;
248 status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
257 % C l u t I m a g e %
261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
263 % ClutImage() replaces each color value in the given image, by using it as an
264 % index to lookup a replacement color value in a Color Look UP Table in the
265 % form of an image. The values are extracted along a diagonal of the CLUT
266 % image so either a horizontal or vertial gradient image can be used.
268 % Typically this is used to either re-color a gray-scale image according to a
269 % color gradient in the CLUT image, or to perform a freeform histogram
270 % (level) adjustment according to the (typically gray-scale) gradient in the
273 % When the 'channel' mask includes the matte/alpha transparency channel but
274 % one image has no such channel it is assumed that that image is a simple
275 % gray-scale image that will effect the alpha channel values, either for
276 % gray-scale coloring (with transparent or semi-transparent colors), or
277 % a histogram adjustment of existing alpha channel values. If both images
278 % have matte channels, direct and normal indexing is applied, which is rarely
281 % The format of the ClutImage method is:
283 % MagickBooleanType ClutImage(Image *image,Image *clut_image,
284 % const PixelInterpolateMethod method,ExceptionInfo *exception)
286 % A description of each parameter follows:
288 % o image: the image, which is replaced by indexed CLUT values
290 % o clut_image: the color lookup table image for replacement color values.
292 % o method: the pixel interpolation method.
294 % o exception: return any errors or warnings in this structure.
297 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image,
298 const PixelInterpolateMethod method,ExceptionInfo *exception)
300 #define ClutImageTag "Clut/Image"
321 assert(image != (Image *) NULL);
322 assert(image->signature == MagickSignature);
323 if( IfMagickTrue(image->debug) )
324 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
325 assert(clut_image != (Image *) NULL);
326 assert(clut_image->signature == MagickSignature);
327 if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
329 if( IfMagickTrue(IsGrayColorspace(image->colorspace)) &&
330 IfMagickFalse(IsGrayColorspace(clut_image->colorspace)))
331 (void) SetImageColorspace(image,sRGBColorspace,exception);
332 clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*clut_map));
333 if (clut_map == (PixelInfo *) NULL)
334 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
341 adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
342 clut_view=AcquireVirtualCacheView(clut_image,exception);
343 for (i=0; i <= (ssize_t) MaxMap; i++)
345 GetPixelInfo(clut_image,clut_map+i);
346 (void) InterpolatePixelInfo(clut_image,clut_view,method,
347 QuantumScale*i*(clut_image->columns-adjust),QuantumScale*i*
348 (clut_image->rows-adjust),clut_map+i,exception);
350 clut_view=DestroyCacheView(clut_view);
351 image_view=AcquireAuthenticCacheView(image,exception);
352 #if defined(MAGICKCORE_OPENMP_SUPPORT)
353 #pragma omp parallel for schedule(static,4) shared(progress,status) \
354 magick_threads(image,image,image->rows,1)
356 for (y=0; y < (ssize_t) image->rows; y++)
367 if( IfMagickFalse(status) )
369 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
370 if (q == (Quantum *) NULL)
375 GetPixelInfo(image,&pixel);
376 for (x=0; x < (ssize_t) image->columns; x++)
378 if (GetPixelReadMask(image,q) == 0)
380 q+=GetPixelChannels(image);
383 GetPixelInfoPixel(image,q,&pixel);
384 pixel.red=clut_map[ScaleQuantumToMap(
385 ClampToQuantum(pixel.red))].red;
386 pixel.green=clut_map[ScaleQuantumToMap(
387 ClampToQuantum(pixel.green))].green;
388 pixel.blue=clut_map[ScaleQuantumToMap(
389 ClampToQuantum(pixel.blue))].blue;
390 pixel.black=clut_map[ScaleQuantumToMap(
391 ClampToQuantum(pixel.black))].black;
392 pixel.alpha=clut_map[ScaleQuantumToMap(
393 ClampToQuantum(pixel.alpha))].alpha;
394 SetPixelInfoPixel(image,&pixel,q);
395 q+=GetPixelChannels(image);
397 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
399 if (image->progress_monitor != (MagickProgressMonitor) NULL)
404 #if defined(MAGICKCORE_OPENMP_SUPPORT)
405 #pragma omp critical (MagickCore_ClutImage)
407 proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
408 if( IfMagickFalse(proceed) )
412 image_view=DestroyCacheView(image_view);
413 clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
414 if ((clut_image->alpha_trait == BlendPixelTrait) &&
415 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
416 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425 % C o l o r D e c i s i o n L i s t I m a g e %
429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431 % ColorDecisionListImage() accepts a lightweight Color Correction Collection
432 % (CCC) file which solely contains one or more color corrections and applies
433 % the correction to the image. Here is a sample CCC file:
435 % <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
436 % <ColorCorrection id="cc03345">
438 % <Slope> 0.9 1.2 0.5 </Slope>
439 % <Offset> 0.4 -0.5 0.6 </Offset>
440 % <Power> 1.0 0.8 1.5 </Power>
443 % <Saturation> 0.85 </Saturation>
446 % </ColorCorrectionCollection>
448 % which includes the slop, offset, and power for each of the RGB channels
449 % as well as the saturation.
451 % The format of the ColorDecisionListImage method is:
453 % MagickBooleanType ColorDecisionListImage(Image *image,
454 % const char *color_correction_collection,ExceptionInfo *exception)
456 % A description of each parameter follows:
458 % o image: the image.
460 % o color_correction_collection: the color correction collection in XML.
462 % o exception: return any errors or warnings in this structure.
465 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
466 const char *color_correction_collection,ExceptionInfo *exception)
468 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
470 typedef struct _Correction
478 typedef struct _ColorCorrection
493 token[MaxTextExtent];
524 Allocate and initialize cdl maps.
526 assert(image != (Image *) NULL);
527 assert(image->signature == MagickSignature);
528 if( IfMagickTrue(image->debug) )
529 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
530 if (color_correction_collection == (const char *) NULL)
532 ccc=NewXMLTree((const char *) color_correction_collection,exception);
533 if (ccc == (XMLTreeInfo *) NULL)
535 cc=GetXMLTreeChild(ccc,"ColorCorrection");
536 if (cc == (XMLTreeInfo *) NULL)
538 ccc=DestroyXMLTree(ccc);
541 color_correction.red.slope=1.0;
542 color_correction.red.offset=0.0;
543 color_correction.red.power=1.0;
544 color_correction.green.slope=1.0;
545 color_correction.green.offset=0.0;
546 color_correction.green.power=1.0;
547 color_correction.blue.slope=1.0;
548 color_correction.blue.offset=0.0;
549 color_correction.blue.power=1.0;
550 color_correction.saturation=0.0;
551 sop=GetXMLTreeChild(cc,"SOPNode");
552 if (sop != (XMLTreeInfo *) NULL)
559 slope=GetXMLTreeChild(sop,"Slope");
560 if (slope != (XMLTreeInfo *) NULL)
562 content=GetXMLTreeContent(slope);
563 p=(const char *) content;
564 for (i=0; (*p != '\0') && (i < 3); i++)
566 GetMagickToken(p,&p,token);
568 GetMagickToken(p,&p,token);
573 color_correction.red.slope=StringToDouble(token,(char **) NULL);
578 color_correction.green.slope=StringToDouble(token,
584 color_correction.blue.slope=StringToDouble(token,
591 offset=GetXMLTreeChild(sop,"Offset");
592 if (offset != (XMLTreeInfo *) NULL)
594 content=GetXMLTreeContent(offset);
595 p=(const char *) content;
596 for (i=0; (*p != '\0') && (i < 3); i++)
598 GetMagickToken(p,&p,token);
600 GetMagickToken(p,&p,token);
605 color_correction.red.offset=StringToDouble(token,
611 color_correction.green.offset=StringToDouble(token,
617 color_correction.blue.offset=StringToDouble(token,
624 power=GetXMLTreeChild(sop,"Power");
625 if (power != (XMLTreeInfo *) NULL)
627 content=GetXMLTreeContent(power);
628 p=(const char *) content;
629 for (i=0; (*p != '\0') && (i < 3); i++)
631 GetMagickToken(p,&p,token);
633 GetMagickToken(p,&p,token);
638 color_correction.red.power=StringToDouble(token,(char **) NULL);
643 color_correction.green.power=StringToDouble(token,
649 color_correction.blue.power=StringToDouble(token,
657 sat=GetXMLTreeChild(cc,"SATNode");
658 if (sat != (XMLTreeInfo *) NULL)
663 saturation=GetXMLTreeChild(sat,"Saturation");
664 if (saturation != (XMLTreeInfo *) NULL)
666 content=GetXMLTreeContent(saturation);
667 p=(const char *) content;
668 GetMagickToken(p,&p,token);
669 color_correction.saturation=StringToDouble(token,(char **) NULL);
672 ccc=DestroyXMLTree(ccc);
673 if( IfMagickTrue(image->debug) )
675 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
676 " Color Correction Collection:");
677 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
678 " color_correction.red.slope: %g",color_correction.red.slope);
679 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
680 " color_correction.red.offset: %g",color_correction.red.offset);
681 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
682 " color_correction.red.power: %g",color_correction.red.power);
683 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
684 " color_correction.green.slope: %g",color_correction.green.slope);
685 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
686 " color_correction.green.offset: %g",color_correction.green.offset);
687 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
688 " color_correction.green.power: %g",color_correction.green.power);
689 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
690 " color_correction.blue.slope: %g",color_correction.blue.slope);
691 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
692 " color_correction.blue.offset: %g",color_correction.blue.offset);
693 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
694 " color_correction.blue.power: %g",color_correction.blue.power);
695 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
696 " color_correction.saturation: %g",color_correction.saturation);
698 cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
699 if (cdl_map == (PixelInfo *) NULL)
700 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
702 for (i=0; i <= (ssize_t) MaxMap; i++)
704 cdl_map[i].red=(double) ScaleMapToQuantum((double)
705 (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
706 color_correction.red.offset,color_correction.red.power))));
707 cdl_map[i].green=(double) ScaleMapToQuantum((double)
708 (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
709 color_correction.green.offset,color_correction.green.power))));
710 cdl_map[i].blue=(double) ScaleMapToQuantum((double)
711 (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
712 color_correction.blue.offset,color_correction.blue.power))));
714 if (image->storage_class == PseudoClass)
715 for (i=0; i < (ssize_t) image->colors; i++)
718 Apply transfer function to colormap.
723 luma=0.21267f*image->colormap[i].red+0.71526*image->colormap[i].green+
724 0.07217f*image->colormap[i].blue;
725 image->colormap[i].red=luma+color_correction.saturation*cdl_map[
726 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
727 image->colormap[i].green=luma+color_correction.saturation*cdl_map[
728 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
729 image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
730 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
733 Apply transfer function to image.
737 image_view=AcquireAuthenticCacheView(image,exception);
738 #if defined(MAGICKCORE_OPENMP_SUPPORT)
739 #pragma omp parallel for schedule(static,4) shared(progress,status) \
740 magick_threads(image,image,image->rows,1)
742 for (y=0; y < (ssize_t) image->rows; y++)
753 if( IfMagickFalse(status) )
755 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
756 if (q == (Quantum *) NULL)
761 for (x=0; x < (ssize_t) image->columns; x++)
763 luma=0.21267f*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+
764 0.07217f*GetPixelBlue(image,q);
765 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
766 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
767 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
768 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
769 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
770 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
771 q+=GetPixelChannels(image);
773 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
775 if (image->progress_monitor != (MagickProgressMonitor) NULL)
780 #if defined(MAGICKCORE_OPENMP_SUPPORT)
781 #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
783 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
784 progress++,image->rows);
785 if( IfMagickFalse(proceed) )
789 image_view=DestroyCacheView(image_view);
790 cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
795 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799 % C o n t r a s t I m a g e %
803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805 % ContrastImage() enhances the intensity differences between the lighter and
806 % darker elements of the image. Set sharpen to a MagickTrue to increase the
807 % image contrast otherwise the contrast is reduced.
809 % The format of the ContrastImage method is:
811 % MagickBooleanType ContrastImage(Image *image,
812 % const MagickBooleanType sharpen,ExceptionInfo *exception)
814 % A description of each parameter follows:
816 % o image: the image.
818 % o sharpen: Increase or decrease image contrast.
820 % o exception: return any errors or warnings in this structure.
824 static void Contrast(const int sign,double *red,double *green,double *blue)
832 Enhance contrast: dark color become darker, light color become lighter.
834 assert(red != (double *) NULL);
835 assert(green != (double *) NULL);
836 assert(blue != (double *) NULL);
840 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
841 brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
843 if (brightness > 1.0)
846 if (brightness < 0.0)
848 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
851 MagickExport MagickBooleanType ContrastImage(Image *image,
852 const MagickBooleanType sharpen,ExceptionInfo *exception)
854 #define ContrastImageTag "Contrast/Image"
874 assert(image != (Image *) NULL);
875 assert(image->signature == MagickSignature);
876 if( IfMagickTrue(image->debug) )
877 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
878 sign=IfMagickTrue(sharpen) ? 1 : -1;
879 if (image->storage_class == PseudoClass)
882 Contrast enhance colormap.
884 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].green=(MagickRealType) green;
897 image->colormap[i].blue=(MagickRealType) blue;
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 magick_threads(image,image,image->rows,1)
910 for (y=0; y < (ssize_t) image->rows; y++)
923 if( IfMagickFalse(status) )
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( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
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( IfMagickFalse(proceed) )
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( IfMagickTrue(image->debug) )
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",
1058 if( IfMagickTrue(IsImageGray(image,exception)) )
1059 (void) SetImageColorspace(image,GRAYColorspace,exception);
1061 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1062 sizeof(*histogram));
1063 image_view=AcquireVirtualCacheView(image,exception);
1064 for (y=0; y < (ssize_t) image->rows; y++)
1066 register const Quantum
1072 if( IfMagickFalse(status) )
1074 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1075 if (p == (const Quantum *) NULL)
1080 for (x=0; x < (ssize_t) image->columns; x++)
1088 pixel=GetPixelIntensity(image,p);
1089 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1091 if (image->channel_mask != DefaultChannels)
1092 pixel=(double) p[i];
1093 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1094 ClampToQuantum(pixel))+i]++;
1096 p+=GetPixelChannels(image);
1099 image_view=DestroyCacheView(image_view);
1101 Find the histogram boundaries by locating the black/white levels.
1103 number_channels=GetPixelChannels(image);
1104 for (i=0; i < (ssize_t) number_channels; i++)
1113 white[i]=MaxRange(QuantumRange);
1115 for (j=0; j <= (ssize_t) MaxMap; j++)
1117 intensity+=histogram[GetPixelChannels(image)*j+i];
1118 if (intensity > black_point)
1121 black[i]=(double) j;
1123 for (j=(ssize_t) MaxMap; j != 0; j--)
1125 intensity+=histogram[GetPixelChannels(image)*j+i];
1126 if (intensity > ((double) image->columns*image->rows-white_point))
1129 white[i]=(double) j;
1131 histogram=(double *) RelinquishMagickMemory(histogram);
1133 Stretch the histogram to create the stretched image mapping.
1135 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1136 sizeof(*stretch_map));
1137 number_channels=GetPixelChannels(image);
1138 for (i=0; i < (ssize_t) number_channels; i++)
1143 for (j=0; j <= (ssize_t) MaxMap; j++)
1145 if (j < (ssize_t) black[i])
1146 stretch_map[GetPixelChannels(image)*j+i]=0.0;
1148 if (j > (ssize_t) white[i])
1149 stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange;
1151 if (black[i] != white[i])
1152 stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum(
1153 (double) (MaxMap*(j-black[i])/(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 magick_threads(image,image,image->rows,1)
1206 for (y=0; y < (ssize_t) image->rows; y++)
1214 if( IfMagickFalse(status) )
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 (GetPixelReadMask(image,q) == 0)
1229 q+=GetPixelChannels(image);
1232 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1234 PixelChannel channel=GetPixelChannelChannel(image,i);
1235 PixelTrait traits=GetPixelChannelTraits(image,channel);
1236 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1238 q[i]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1239 ScaleQuantumToMap(q[i])+i]);
1241 q+=GetPixelChannels(image);
1243 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
1245 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1250 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1251 #pragma omp critical (MagickCore_ContrastStretchImage)
1253 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1255 if( IfMagickFalse(proceed) )
1259 image_view=DestroyCacheView(image_view);
1260 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1261 white=(double *) RelinquishMagickMemory(white);
1262 black=(double *) RelinquishMagickMemory(black);
1267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1271 % E n h a n c e I m a g e %
1275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277 % EnhanceImage() applies a digital filter that improves the quality of a
1280 % The format of the EnhanceImage method is:
1282 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1284 % A description of each parameter follows:
1286 % o image: the image.
1288 % o exception: return any errors or warnings in this structure.
1291 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1293 #define EnhancePixel(weight) \
1294 mean=((double) r[i]+GetPixelChannel(enhance_image,channel,q))/2.0; \
1295 distance=(double) r[i]-(double) GetPixelChannel(enhance_image,channel,q); \
1296 distance_squared=QuantumScale*(2.0*((double) QuantumRange+1.0)+mean)* \
1297 distance*distance; \
1298 if (distance_squared < ((double) QuantumRange*(double) QuantumRange/25.0f)) \
1300 aggregate+=(weight)*r[i]; \
1301 total_weight+=(weight); \
1303 r+=GetPixelChannels(image);
1304 #define EnhanceImageTag "Enhance/Image"
1323 Initialize enhanced image attributes.
1325 assert(image != (const Image *) NULL);
1326 assert(image->signature == MagickSignature);
1327 if( IfMagickTrue(image->debug) )
1328 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1329 assert(exception != (ExceptionInfo *) NULL);
1330 assert(exception->signature == MagickSignature);
1331 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1333 if (enhance_image == (Image *) NULL)
1334 return((Image *) NULL);
1335 if( IfMagickFalse(SetImageStorageClass(enhance_image,DirectClass,exception)) )
1337 enhance_image=DestroyImage(enhance_image);
1338 return((Image *) NULL);
1345 image_view=AcquireVirtualCacheView(image,exception);
1346 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1347 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1348 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1349 magick_threads(image,enhance_image,image->rows,1)
1351 for (y=0; y < (ssize_t) image->rows; y++)
1353 register const Quantum
1365 if( IfMagickFalse(status) )
1367 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1368 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1370 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1375 center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1376 for (x=0; x < (ssize_t) image->columns; x++)
1381 if (GetPixelReadMask(image,p) == 0)
1383 SetPixelBackgoundColor(enhance_image,q);
1384 p+=GetPixelChannels(image);
1385 q+=GetPixelChannels(enhance_image);
1388 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1397 register const Quantum
1400 PixelChannel channel=GetPixelChannelChannel(image,i);
1401 PixelTrait traits=GetPixelChannelTraits(image,channel);
1402 PixelTrait enhance_traits=GetPixelChannelTraits(enhance_image,channel);
1403 if ((traits == UndefinedPixelTrait) ||
1404 (enhance_traits == UndefinedPixelTrait))
1406 SetPixelChannel(enhance_image,channel,p[center+i],q);
1407 if ((enhance_traits & CopyPixelTrait) != 0)
1410 Compute weighted average of target pixel color components.
1415 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1416 EnhancePixel(8.0); EnhancePixel(5.0);
1417 r=p+1*GetPixelChannels(image)*(image->columns+4);
1418 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1419 EnhancePixel(20.0); EnhancePixel(8.0);
1420 r=p+2*GetPixelChannels(image)*(image->columns+4);
1421 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1422 EnhancePixel(40.0); EnhancePixel(10.0);
1423 r=p+3*GetPixelChannels(image)*(image->columns+4);
1424 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1425 EnhancePixel(20.0); EnhancePixel(8.0);
1426 r=p+4*GetPixelChannels(image)*(image->columns+4);
1427 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1428 EnhancePixel(8.0); EnhancePixel(5.0);
1429 SetPixelChannel(enhance_image,channel,ClampToQuantum(aggregate/
1432 p+=GetPixelChannels(image);
1433 q+=GetPixelChannels(enhance_image);
1435 if( IfMagickFalse(SyncCacheViewAuthenticPixels(enhance_view,exception)) )
1437 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1442 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1443 #pragma omp critical (MagickCore_EnhanceImage)
1445 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1446 if( IfMagickFalse(proceed) )
1450 enhance_view=DestroyCacheView(enhance_view);
1451 image_view=DestroyCacheView(image_view);
1452 if( IfMagickFalse(status) )
1453 enhance_image=DestroyImage(enhance_image);
1454 return(enhance_image);
1458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1462 % E q u a l i z e I m a g e %
1466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1468 % EqualizeImage() applies a histogram equalization to the image.
1470 % The format of the EqualizeImage method is:
1472 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
1474 % A description of each parameter follows:
1476 % o image: the image.
1478 % o exception: return any errors or warnings in this structure.
1481 MagickExport MagickBooleanType EqualizeImage(Image *image,
1482 ExceptionInfo *exception)
1484 #define EqualizeImageTag "Equalize/Image"
1496 black[CompositePixelChannel+1],
1500 white[CompositePixelChannel+1];
1512 Allocate and initialize histogram arrays.
1514 assert(image != (Image *) NULL);
1515 assert(image->signature == MagickSignature);
1516 if( IfMagickTrue(image->debug) )
1517 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1518 equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1519 GetPixelChannels(image)*sizeof(*equalize_map));
1520 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1521 sizeof(*histogram));
1522 map=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1524 if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
1525 (map == (double *) NULL))
1527 if (map != (double *) NULL)
1528 map=(double *) RelinquishMagickMemory(map);
1529 if (histogram != (double *) NULL)
1530 histogram=(double *) RelinquishMagickMemory(histogram);
1531 if (equalize_map != (double *) NULL)
1532 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1533 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1540 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1541 sizeof(*histogram));
1542 image_view=AcquireVirtualCacheView(image,exception);
1543 for (y=0; y < (ssize_t) image->rows; y++)
1545 register const Quantum
1551 if( IfMagickFalse(status) )
1553 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1554 if (p == (const Quantum *) NULL)
1559 for (x=0; x < (ssize_t) image->columns; x++)
1564 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1565 histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++;
1566 p+=GetPixelChannels(image);
1569 image_view=DestroyCacheView(image_view);
1571 Integrate the histogram to get the equalization map.
1573 number_channels=GetPixelChannels(image);
1574 for (i=0; i < (ssize_t) number_channels; i++)
1583 for (j=0; j <= (ssize_t) MaxMap; j++)
1585 intensity+=histogram[GetPixelChannels(image)*j+i];
1586 map[GetPixelChannels(image)*j+i]=intensity;
1589 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
1590 sizeof(*equalize_map));
1591 (void) ResetMagickMemory(black,0,sizeof(*black));
1592 (void) ResetMagickMemory(white,0,sizeof(*white));
1593 number_channels=GetPixelChannels(image);
1594 for (i=0; i < (ssize_t) number_channels; i++)
1600 white[i]=map[GetPixelChannels(image)*MaxMap+i];
1601 if (black[i] != white[i])
1602 for (j=0; j <= (ssize_t) MaxMap; j++)
1603 equalize_map[GetPixelChannels(image)*j+i]=(double)
1604 ScaleMapToQuantum((double) ((MaxMap*(map[
1605 GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
1607 histogram=(double *) RelinquishMagickMemory(histogram);
1608 map=(double *) RelinquishMagickMemory(map);
1609 if (image->storage_class == PseudoClass)
1617 for (j=0; j < (ssize_t) image->colors; j++)
1619 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1621 PixelChannel channel=GetPixelChannelChannel(image,RedPixelChannel);
1622 if (black[channel] != white[channel])
1623 image->colormap[j].red=equalize_map[GetPixelChannels(image)*
1624 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+
1627 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1629 PixelChannel channel=GetPixelChannelChannel(image,
1631 if (black[channel] != white[channel])
1632 image->colormap[j].green=equalize_map[GetPixelChannels(image)*
1633 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+
1636 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1638 PixelChannel channel=GetPixelChannelChannel(image,BluePixelChannel);
1639 if (black[channel] != white[channel])
1640 image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
1641 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+
1644 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1646 PixelChannel channel=GetPixelChannelChannel(image,
1648 if (black[channel] != white[channel])
1649 image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
1650 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+
1659 image_view=AcquireAuthenticCacheView(image,exception);
1660 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1661 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1662 magick_threads(image,image,image->rows,1)
1664 for (y=0; y < (ssize_t) image->rows; y++)
1672 if( IfMagickFalse(status) )
1674 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1675 if (q == (Quantum *) NULL)
1680 for (x=0; x < (ssize_t) image->columns; x++)
1685 if (GetPixelReadMask(image,q) == 0)
1687 q+=GetPixelChannels(image);
1690 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1692 PixelChannel channel=GetPixelChannelChannel(image,i);
1693 PixelTrait traits=GetPixelChannelTraits(image,channel);
1694 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1696 q[i]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
1697 ScaleQuantumToMap(q[i])+i]);
1699 q+=GetPixelChannels(image);
1701 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
1703 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1708 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1709 #pragma omp critical (MagickCore_EqualizeImage)
1711 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1712 if( IfMagickFalse(proceed) )
1716 image_view=DestroyCacheView(image_view);
1717 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726 % G a m m a I m a g e %
1730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1732 % GammaImage() gamma-corrects a particular image channel. The same
1733 % image viewed on different devices will have perceptual differences in the
1734 % way the image's intensities are represented on the screen. Specify
1735 % individual gamma levels for the red, green, and blue channels, or adjust
1736 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
1738 % You can also reduce the influence of a particular channel with a gamma
1741 % The format of the GammaImage method is:
1743 % MagickBooleanType GammaImage(Image *image,const double gamma,
1744 % ExceptionInfo *exception)
1746 % A description of each parameter follows:
1748 % o image: the image.
1750 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1752 % o gamma: the image gamma.
1756 static inline double gamma_pow(const double value,const double gamma)
1758 return(value < 0.0 ? value : pow(value,gamma));
1761 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
1762 ExceptionInfo *exception)
1764 #define GammaCorrectImageTag "GammaCorrect/Image"
1785 Allocate and initialize gamma maps.
1787 assert(image != (Image *) NULL);
1788 assert(image->signature == MagickSignature);
1789 if( IfMagickTrue(image->debug) )
1790 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1793 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1794 if (gamma_map == (Quantum *) NULL)
1795 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1797 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1799 for (i=0; i <= (ssize_t) MaxMap; i++)
1800 gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
1801 MaxMap,1.0/gamma)));
1802 if (image->storage_class == PseudoClass)
1803 for (i=0; i < (ssize_t) image->colors; i++)
1806 Gamma-correct colormap.
1808 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1809 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1810 image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
1811 ClampToQuantum(image->colormap[i].red))];
1812 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1813 image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
1814 ClampToQuantum(image->colormap[i].green))];
1815 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1816 image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
1817 ClampToQuantum(image->colormap[i].blue))];
1818 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1819 image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
1820 ClampToQuantum(image->colormap[i].alpha))];
1822 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1823 image->colormap[i].red=QuantumRange*gamma_pow(QuantumScale*
1824 image->colormap[i].red,1.0/gamma);
1825 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1826 image->colormap[i].green=QuantumRange*gamma_pow(QuantumScale*
1827 image->colormap[i].green,1.0/gamma);
1828 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1829 image->colormap[i].blue=QuantumRange*gamma_pow(QuantumScale*
1830 image->colormap[i].blue,1.0/gamma);
1831 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1832 image->colormap[i].alpha=QuantumRange*gamma_pow(QuantumScale*
1833 image->colormap[i].alpha,1.0/gamma);
1837 Gamma-correct image.
1841 image_view=AcquireAuthenticCacheView(image,exception);
1842 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1843 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1844 magick_threads(image,image,image->rows,1)
1846 for (y=0; y < (ssize_t) image->rows; y++)
1854 if( IfMagickFalse(status) )
1856 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1857 if (q == (Quantum *) NULL)
1862 for (x=0; x < (ssize_t) image->columns; x++)
1867 if (GetPixelReadMask(image,q) == 0)
1869 q+=GetPixelChannels(image);
1872 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1874 PixelChannel channel=GetPixelChannelChannel(image,i);
1875 PixelTrait traits=GetPixelChannelTraits(image,channel);
1876 if ((traits & UpdatePixelTrait) == 0)
1878 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1879 q[i]=gamma_map[ScaleQuantumToMap(q[i])];
1881 q[i]=QuantumRange*gamma_pow(QuantumScale*q[i],1.0/gamma);
1884 q+=GetPixelChannels(image);
1886 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
1888 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1893 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1894 #pragma omp critical (MagickCore_GammaImage)
1896 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
1898 if( IfMagickFalse(proceed) )
1902 image_view=DestroyCacheView(image_view);
1903 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
1904 if (image->gamma != 0.0)
1905 image->gamma*=gamma;
1910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1914 % G r a y s c a l e I m a g e %
1918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1920 % GrayscaleImage() converts the image to grayscale.
1922 % The format of the GrayscaleImage method is:
1924 % MagickBooleanType GrayscaleImage(Image *image,
1925 % const PixelIntensityMethod method ,ExceptionInfo *exception)
1927 % A description of each parameter follows:
1929 % o image: the image.
1931 % o method: the pixel intensity method.
1933 % o exception: return any errors or warnings in this structure.
1937 static inline MagickRealType MagickMax(const MagickRealType x,
1938 const MagickRealType y)
1945 static inline MagickRealType MagickMin(const MagickRealType x,
1946 const MagickRealType y)
1953 MagickExport MagickBooleanType GrayscaleImage(Image *image,
1954 const PixelIntensityMethod method,ExceptionInfo *exception)
1956 #define GrayscaleImageTag "Grayscale/Image"
1970 assert(image != (Image *) NULL);
1971 assert(image->signature == MagickSignature);
1972 if( IfMagickTrue(image->debug) )
1973 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1974 if (image->storage_class == PseudoClass)
1976 if( IfMagickFalse(SyncImage(image,exception)) )
1977 return(MagickFalse);
1978 if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
1979 return(MagickFalse);
1986 image_view=AcquireAuthenticCacheView(image,exception);
1987 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1988 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1989 magick_threads(image,image,image->rows,1)
1991 for (y=0; y < (ssize_t) image->rows; y++)
1999 if( IfMagickFalse(status) )
2001 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2002 if (q == (Quantum *) NULL)
2007 for (x=0; x < (ssize_t) image->columns; x++)
2015 if (GetPixelReadMask(image,q) == 0)
2017 q+=GetPixelChannels(image);
2020 red=(MagickRealType) GetPixelRed(image,q);
2021 green=(MagickRealType) GetPixelGreen(image,q);
2022 blue=(MagickRealType) GetPixelBlue(image,q);
2026 case AveragePixelIntensityMethod:
2028 intensity=(red+green+blue)/3.0;
2031 case BrightnessPixelIntensityMethod:
2033 intensity=MagickMax(MagickMax(red,green),blue);
2036 case LightnessPixelIntensityMethod:
2038 intensity=(MagickMin(MagickMin(red,green),blue)+
2039 MagickMax(MagickMax(red,green),blue))/2.0;
2042 case MSPixelIntensityMethod:
2044 intensity=(MagickRealType) (((double) red*red+green*green+
2048 case Rec601LumaPixelIntensityMethod:
2050 if (image->colorspace == RGBColorspace)
2052 red=EncodePixelGamma(red);
2053 green=EncodePixelGamma(green);
2054 blue=EncodePixelGamma(blue);
2056 intensity=0.298839*red+0.586811*green+0.114350*blue;
2059 case Rec601LuminancePixelIntensityMethod:
2061 if (image->colorspace == sRGBColorspace)
2063 red=DecodePixelGamma(red);
2064 green=DecodePixelGamma(green);
2065 blue=DecodePixelGamma(blue);
2067 intensity=0.298839*red+0.586811*green+0.114350*blue;
2070 case Rec709LumaPixelIntensityMethod:
2073 if (image->colorspace == RGBColorspace)
2075 red=EncodePixelGamma(red);
2076 green=EncodePixelGamma(green);
2077 blue=EncodePixelGamma(blue);
2079 intensity=0.212656*red+0.715158*green+0.072186*blue;
2082 case Rec709LuminancePixelIntensityMethod:
2084 if (image->colorspace == sRGBColorspace)
2086 red=DecodePixelGamma(red);
2087 green=DecodePixelGamma(green);
2088 blue=DecodePixelGamma(blue);
2090 intensity=0.212656*red+0.715158*green+0.072186*blue;
2093 case RMSPixelIntensityMethod:
2095 intensity=(MagickRealType) (sqrt((double) red*red+green*green+
2096 blue*blue)/sqrt(3.0));
2100 SetPixelGray(image,ClampToQuantum(intensity),q);
2101 q+=GetPixelChannels(image);
2103 if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)))
2105 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2110 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2111 #pragma omp critical (MagickCore_GrayscaleImage)
2113 proceed=SetImageProgress(image,GrayscaleImageTag,progress++,
2115 if( IfMagickFalse(proceed) )
2119 image_view=DestroyCacheView(image_view);
2120 image->intensity=method;
2121 image->type=GrayscaleType;
2122 return(SetImageColorspace(image,GRAYColorspace,exception));
2126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2130 % H a l d C l u t I m a g e %
2134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2136 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
2137 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2138 % Create it with the HALD coder. You can apply any color transformation to
2139 % the Hald image and then use this method to apply the transform to the
2142 % The format of the HaldClutImage method is:
2144 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
2145 % ExceptionInfo *exception)
2147 % A description of each parameter follows:
2149 % o image: the image, which is replaced by indexed CLUT values
2151 % o hald_image: the color lookup table image for replacement color values.
2153 % o exception: return any errors or warnings in this structure.
2156 MagickExport MagickBooleanType HaldClutImage(Image *image,
2157 const Image *hald_image,ExceptionInfo *exception)
2159 #define HaldClutImageTag "Clut/Image"
2161 typedef struct _HaldInfo
2193 assert(image != (Image *) NULL);
2194 assert(image->signature == MagickSignature);
2195 if( IfMagickTrue(image->debug) )
2196 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2197 assert(hald_image != (Image *) NULL);
2198 assert(hald_image->signature == MagickSignature);
2199 if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
2200 return(MagickFalse);
2201 if (image->alpha_trait != BlendPixelTrait)
2202 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2208 length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2209 (MagickRealType) hald_image->rows);
2210 for (level=2; (level*level*level) < length; level++) ;
2212 cube_size=level*level;
2213 width=(double) hald_image->columns;
2214 GetPixelInfo(hald_image,&zero);
2215 hald_view=AcquireVirtualCacheView(hald_image,exception);
2216 image_view=AcquireAuthenticCacheView(image,exception);
2217 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2218 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2219 magick_threads(image,image,image->rows,1)
2221 for (y=0; y < (ssize_t) image->rows; y++)
2229 if( IfMagickFalse(status) )
2231 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2232 if (q == (Quantum *) NULL)
2237 for (x=0; x < (ssize_t) image->columns; x++)
2252 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2253 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2254 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2255 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2256 point.x-=floor(point.x);
2257 point.y-=floor(point.y);
2258 point.z-=floor(point.z);
2260 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2261 fmod(offset,width),floor(offset/width),&pixel1,exception);
2263 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2264 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2266 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2269 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2270 fmod(offset,width),floor(offset/width),&pixel1,exception);
2271 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2272 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2274 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2277 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2279 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2280 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2281 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2282 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2283 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2284 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2285 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2286 (image->colorspace == CMYKColorspace))
2287 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2288 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2289 (image->alpha_trait == BlendPixelTrait))
2290 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2291 q+=GetPixelChannels(image);
2293 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2295 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2300 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2301 #pragma omp critical (MagickCore_HaldClutImage)
2303 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2304 if( IfMagickFalse(proceed) )
2308 hald_view=DestroyCacheView(hald_view);
2309 image_view=DestroyCacheView(image_view);
2314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2318 % L e v e l I m a g e %
2322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2324 % LevelImage() adjusts the levels of a particular image channel by
2325 % scaling the colors falling between specified white and black points to
2326 % the full available quantum range.
2328 % The parameters provided represent the black, and white points. The black
2329 % point specifies the darkest color in the image. Colors darker than the
2330 % black point are set to zero. White point specifies the lightest color in
2331 % the image. Colors brighter than the white point are set to the maximum
2334 % If a '!' flag is given, map black and white colors to the given levels
2335 % rather than mapping those levels to black and white. See
2336 % LevelizeImage() below.
2338 % Gamma specifies a gamma correction to apply to the image.
2340 % The format of the LevelImage method is:
2342 % MagickBooleanType LevelImage(Image *image,const double black_point,
2343 % const double white_point,const double gamma,ExceptionInfo *exception)
2345 % A description of each parameter follows:
2347 % o image: the image.
2349 % o black_point: The level to map zero (black) to.
2351 % o white_point: The level to map QuantumRange (white) to.
2353 % o exception: return any errors or warnings in this structure.
2357 static inline double LevelPixel(const double black_point,
2358 const double white_point,const double gamma,const double pixel)
2364 scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0;
2365 level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
2367 return(level_pixel);
2370 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2371 const double white_point,const double gamma,ExceptionInfo *exception)
2373 #define LevelImageTag "Level/Image"
2391 Allocate and initialize levels map.
2393 assert(image != (Image *) NULL);
2394 assert(image->signature == MagickSignature);
2395 if( IfMagickTrue(image->debug) )
2396 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2397 if (image->storage_class == PseudoClass)
2398 for (i=0; i < (ssize_t) image->colors; i++)
2403 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2404 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2405 white_point,gamma,image->colormap[i].red));
2406 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2407 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2408 white_point,gamma,image->colormap[i].green));
2409 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2410 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2411 white_point,gamma,image->colormap[i].blue));
2412 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2413 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2414 white_point,gamma,image->colormap[i].alpha));
2421 image_view=AcquireAuthenticCacheView(image,exception);
2422 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2423 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2424 magick_threads(image,image,image->rows,1)
2426 for (y=0; y < (ssize_t) image->rows; y++)
2434 if( IfMagickFalse(status) )
2436 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2437 if (q == (Quantum *) NULL)
2442 for (x=0; x < (ssize_t) image->columns; x++)
2447 if (GetPixelReadMask(image,q) == 0)
2449 q+=GetPixelChannels(image);
2452 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2454 PixelChannel channel=GetPixelChannelChannel(image,i);
2455 PixelTrait traits=GetPixelChannelTraits(image,channel);
2456 if ((traits & UpdatePixelTrait) == 0)
2458 q[i]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2461 q+=GetPixelChannels(image);
2463 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2465 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2470 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2471 #pragma omp critical (MagickCore_LevelImage)
2473 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2474 if( IfMagickFalse(proceed) )
2478 image_view=DestroyCacheView(image_view);
2479 (void) ClampImage(image,exception);
2484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2488 % L e v e l i z e I m a g e %
2492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2494 % LevelizeImage() applies the reversed LevelImage() operation to just
2495 % the specific channels specified. It compresses the full range of color
2496 % values, so that they lie between the given black and white points. Gamma is
2497 % applied before the values are mapped.
2499 % LevelizeImage() can be called with by using a +level command line
2500 % API option, or using a '!' on a -level or LevelImage() geometry string.
2502 % It can be used to de-contrast a greyscale image to the exact levels
2503 % specified. Or by using specific levels for each channel of an image you
2504 % can convert a gray-scale image to any linear color gradient, according to
2507 % The format of the LevelizeImage method is:
2509 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
2510 % const double white_point,const double gamma,ExceptionInfo *exception)
2512 % A description of each parameter follows:
2514 % o image: the image.
2516 % o black_point: The level to map zero (black) to.
2518 % o white_point: The level to map QuantumRange (white) to.
2520 % o gamma: adjust gamma by this factor before mapping values.
2522 % o exception: return any errors or warnings in this structure.
2525 MagickExport MagickBooleanType LevelizeImage(Image *image,
2526 const double black_point,const double white_point,const double gamma,
2527 ExceptionInfo *exception)
2529 #define LevelizeImageTag "Levelize/Image"
2530 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
2531 (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
2549 Allocate and initialize levels map.
2551 assert(image != (Image *) NULL);
2552 assert(image->signature == MagickSignature);
2553 if( IfMagickTrue(image->debug) )
2554 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2555 if (image->storage_class == PseudoClass)
2556 for (i=0; i < (ssize_t) image->colors; i++)
2561 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2562 image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
2563 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2564 image->colormap[i].green=(double) LevelizeValue(
2565 image->colormap[i].green);
2566 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2567 image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
2568 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2569 image->colormap[i].alpha=(double) LevelizeValue(
2570 image->colormap[i].alpha);
2577 image_view=AcquireAuthenticCacheView(image,exception);
2578 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2579 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2580 magick_threads(image,image,image->rows,1)
2582 for (y=0; y < (ssize_t) image->rows; y++)
2590 if( IfMagickFalse(status) )
2592 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2593 if (q == (Quantum *) NULL)
2598 for (x=0; x < (ssize_t) image->columns; x++)
2603 if (GetPixelReadMask(image,q) == 0)
2605 q+=GetPixelChannels(image);
2608 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2610 PixelChannel channel=GetPixelChannelChannel(image,i);
2611 PixelTrait traits=GetPixelChannelTraits(image,channel);
2612 if ((traits & UpdatePixelTrait) == 0)
2614 q[i]=LevelizeValue(q[i]);
2616 q+=GetPixelChannels(image);
2618 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2620 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2625 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2626 #pragma omp critical (MagickCore_LevelizeImage)
2628 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2629 if( IfMagickFalse(proceed) )
2633 image_view=DestroyCacheView(image_view);
2638 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2642 % L e v e l I m a g e C o l o r s %
2646 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2648 % LevelImageColors() maps the given color to "black" and "white" values,
2649 % linearly spreading out the colors, and level values on a channel by channel
2650 % bases, as per LevelImage(). The given colors allows you to specify
2651 % different level ranges for each of the color channels separately.
2653 % If the boolean 'invert' is set true the image values will modifyed in the
2654 % reverse direction. That is any existing "black" and "white" colors in the
2655 % image will become the color values given, with all other values compressed
2656 % appropriatally. This effectivally maps a greyscale gradient into the given
2659 % The format of the LevelImageColors method is:
2661 % MagickBooleanType LevelImageColors(Image *image,
2662 % const PixelInfo *black_color,const PixelInfo *white_color,
2663 % const MagickBooleanType invert,ExceptionInfo *exception)
2665 % A description of each parameter follows:
2667 % o image: the image.
2669 % o black_color: The color to map black to/from
2671 % o white_point: The color to map white to/from
2673 % o invert: if true map the colors (levelize), rather than from (level)
2675 % o exception: return any errors or warnings in this structure.
2678 MagickExport MagickBooleanType LevelImageColors(Image *image,
2679 const PixelInfo *black_color,const PixelInfo *white_color,
2680 const MagickBooleanType invert,ExceptionInfo *exception)
2689 Allocate and initialize levels map.
2691 assert(image != (Image *) NULL);
2692 assert(image->signature == MagickSignature);
2693 if( IfMagickTrue(image->debug) )
2694 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2695 if( IfMagickTrue(IsGrayColorspace(image->colorspace)) &&
2696 (IfMagickFalse(IsGrayColorspace(black_color->colorspace)) ||
2697 IfMagickFalse(IsGrayColorspace(white_color->colorspace))))
2698 (void) SetImageColorspace(image,sRGBColorspace,exception);
2700 if( IfMagickFalse(invert) )
2702 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2704 channel_mask=SetImageChannelMask(image,RedChannel);
2705 status&=LevelImage(image,black_color->red,white_color->red,1.0,
2707 (void) SetImageChannelMask(image,channel_mask);
2709 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2711 channel_mask=SetImageChannelMask(image,GreenChannel);
2712 status&=LevelImage(image,black_color->green,white_color->green,1.0,
2714 (void) SetImageChannelMask(image,channel_mask);
2716 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2718 channel_mask=SetImageChannelMask(image,BlueChannel);
2719 status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
2721 (void) SetImageChannelMask(image,channel_mask);
2723 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2724 (image->colorspace == CMYKColorspace))
2726 channel_mask=SetImageChannelMask(image,BlackChannel);
2727 status&=LevelImage(image,black_color->black,white_color->black,1.0,
2729 (void) SetImageChannelMask(image,channel_mask);
2731 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2732 (image->alpha_trait == BlendPixelTrait))
2734 channel_mask=SetImageChannelMask(image,AlphaChannel);
2735 status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2737 (void) SetImageChannelMask(image,channel_mask);
2742 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2744 channel_mask=SetImageChannelMask(image,RedChannel);
2745 status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
2747 (void) SetImageChannelMask(image,channel_mask);
2749 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2751 channel_mask=SetImageChannelMask(image,GreenChannel);
2752 status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
2754 (void) SetImageChannelMask(image,channel_mask);
2756 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2758 channel_mask=SetImageChannelMask(image,BlueChannel);
2759 status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2761 (void) SetImageChannelMask(image,channel_mask);
2763 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2764 (image->colorspace == CMYKColorspace))
2766 channel_mask=SetImageChannelMask(image,BlackChannel);
2767 status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
2769 (void) SetImageChannelMask(image,channel_mask);
2771 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2772 (image->alpha_trait == BlendPixelTrait))
2774 channel_mask=SetImageChannelMask(image,AlphaChannel);
2775 status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2777 (void) SetImageChannelMask(image,channel_mask);
2780 return(status != 0 ? MagickTrue : MagickFalse);
2784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2788 % L i n e a r S t r e t c h I m a g e %
2792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2794 % LinearStretchImage() discards any pixels below the black point and above
2795 % the white point and levels the remaining pixels.
2797 % The format of the LinearStretchImage method is:
2799 % MagickBooleanType LinearStretchImage(Image *image,
2800 % const double black_point,const double white_point,
2801 % ExceptionInfo *exception)
2803 % A description of each parameter follows:
2805 % o image: the image.
2807 % o black_point: the black point.
2809 % o white_point: the white point.
2811 % o exception: return any errors or warnings in this structure.
2814 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2815 const double black_point,const double white_point,ExceptionInfo *exception)
2817 #define LinearStretchImageTag "LinearStretch/Image"
2835 Allocate histogram and linear map.
2837 assert(image != (Image *) NULL);
2838 assert(image->signature == MagickSignature);
2839 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
2840 if (histogram == (double *) NULL)
2841 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2846 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2847 image_view=AcquireVirtualCacheView(image,exception);
2848 for (y=0; y < (ssize_t) image->rows; y++)
2850 register const Quantum
2856 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2857 if (p == (const Quantum *) NULL)
2859 for (x=0; x < (ssize_t) image->columns; x++)
2864 intensity=GetPixelIntensity(image,p);
2865 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2866 p+=GetPixelChannels(image);
2869 image_view=DestroyCacheView(image_view);
2871 Find the histogram boundaries by locating the black and white point levels.
2874 for (black=0; black < (ssize_t) MaxMap; black++)
2876 intensity+=histogram[black];
2877 if (intensity >= black_point)
2881 for (white=(ssize_t) MaxMap; white != 0; white--)
2883 intensity+=histogram[white];
2884 if (intensity >= white_point)
2887 histogram=(double *) RelinquishMagickMemory(histogram);
2888 status=LevelImage(image,(double) black,(double) white,1.0,exception);
2893 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2897 % M o d u l a t e I m a g e %
2901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2903 % ModulateImage() lets you control the brightness, saturation, and hue
2904 % of an image. Modulate represents the brightness, saturation, and hue
2905 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
2906 % modulation is lightness, saturation, and hue. For HWB, use blackness,
2907 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
2909 % The format of the ModulateImage method is:
2911 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
2912 % ExceptionInfo *exception)
2914 % A description of each parameter follows:
2916 % o image: the image.
2918 % o modulate: Define the percent change in brightness, saturation, and hue.
2920 % o exception: return any errors or warnings in this structure.
2924 static inline void ModulateHCL(const double percent_hue,
2925 const double percent_chroma,const double percent_luma,double *red,
2926 double *green,double *blue)
2934 Increase or decrease color luma, chroma, or hue.
2936 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2937 hue+=0.5*(0.01*percent_hue-1.0);
2942 chroma*=0.01*percent_chroma;
2943 luma*=0.01*percent_luma;
2944 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2947 static inline void ModulateHCLp(const double percent_hue,
2948 const double percent_chroma,const double percent_luma,double *red,
2949 double *green,double *blue)
2957 Increase or decrease color luma, chroma, or hue.
2959 ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
2960 hue+=0.5*(0.01*percent_hue-1.0);
2965 chroma*=0.01*percent_chroma;
2966 luma*=0.01*percent_luma;
2967 ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
2970 static inline void ModulateHSB(const double percent_hue,
2971 const double percent_saturation,const double percent_brightness,double *red,
2972 double *green,double *blue)
2980 Increase or decrease color brightness, saturation, or hue.
2982 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2983 hue+=0.5*(0.01*percent_hue-1.0);
2988 saturation*=0.01*percent_saturation;
2989 brightness*=0.01*percent_brightness;
2990 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2993 static inline void ModulateHSI(const double percent_hue,
2994 const double percent_saturation,const double percent_intensity,double *red,
2995 double *green,double *blue)
3003 Increase or decrease color intensity, saturation, or hue.
3005 ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3006 hue+=0.5*(0.01*percent_hue-1.0);
3011 saturation*=0.01*percent_saturation;
3012 intensity*=0.01*percent_intensity;
3013 ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3016 static inline void ModulateHSL(const double percent_hue,
3017 const double percent_saturation,const double percent_lightness,double *red,
3018 double *green,double *blue)
3026 Increase or decrease color lightness, saturation, or hue.
3028 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3029 hue+=0.5*(0.01*percent_hue-1.0);
3034 saturation*=0.01*percent_saturation;
3035 lightness*=0.01*percent_lightness;
3036 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3039 static inline void ModulateHSV(const double percent_hue,
3040 const double percent_saturation,const double percent_value,double *red,
3041 double *green,double *blue)
3049 Increase or decrease color value, saturation, or hue.
3051 ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3052 hue+=0.5*(0.01*percent_hue-1.0);
3057 saturation*=0.01*percent_saturation;
3058 value*=0.01*percent_value;
3059 ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3062 static inline void ModulateHWB(const double percent_hue,
3063 const double percent_whiteness,const double percent_blackness,double *red,
3064 double *green,double *blue)
3072 Increase or decrease color blackness, whiteness, or hue.
3074 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3075 hue+=0.5*(0.01*percent_hue-1.0);
3080 blackness*=0.01*percent_blackness;
3081 whiteness*=0.01*percent_whiteness;
3082 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3085 static inline void ModulateLCHab(const double percent_luma,
3086 const double percent_chroma,const double percent_hue,double *red,
3087 double *green,double *blue)
3095 Increase or decrease color luma, chroma, or hue.
3097 ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
3098 luma*=0.01*percent_luma;
3099 chroma*=0.01*percent_chroma;
3100 hue+=0.5*(0.01*percent_hue-1.0);
3105 ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
3108 static inline void ModulateLCHuv(const double percent_luma,
3109 const double percent_chroma,const double percent_hue,double *red,
3110 double *green,double *blue)
3118 Increase or decrease color luma, chroma, or hue.
3120 ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
3121 luma*=0.01*percent_luma;
3122 chroma*=0.01*percent_chroma;
3123 hue+=0.5*(0.01*percent_hue-1.0);
3128 ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
3131 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
3132 ExceptionInfo *exception)
3134 #define ModulateImageTag "Modulate/Image"
3169 Initialize modulate table.
3171 assert(image != (Image *) NULL);
3172 assert(image->signature == MagickSignature);
3173 if( IfMagickTrue(image->debug) )
3174 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3175 if (modulate == (char *) NULL)
3176 return(MagickFalse);
3177 if( IfMagickFalse(IssRGBCompatibleColorspace(image->colorspace)) )
3178 (void) SetImageColorspace(image,sRGBColorspace,exception);
3179 flags=ParseGeometry(modulate,&geometry_info);
3180 percent_brightness=geometry_info.rho;
3181 percent_saturation=geometry_info.sigma;
3182 if ((flags & SigmaValue) == 0)
3183 percent_saturation=100.0;
3184 percent_hue=geometry_info.xi;
3185 if ((flags & XiValue) == 0)
3187 colorspace=UndefinedColorspace;
3188 artifact=GetImageArtifact(image,"modulate:colorspace");
3189 if (artifact != (const char *) NULL)
3190 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3191 MagickFalse,artifact);
3192 if (image->storage_class == PseudoClass)
3193 for (i=0; i < (ssize_t) image->colors; i++)
3201 Modulate image colormap.
3203 red=(double) image->colormap[i].red;
3204 green=(double) image->colormap[i].green;
3205 blue=(double) image->colormap[i].blue;
3210 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3214 case HCLpColorspace:
3216 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3222 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3228 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3235 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3241 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3247 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3252 case LCHabColorspace:
3254 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3258 case LCHuvColorspace:
3260 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3265 image->colormap[i].red=red;
3266 image->colormap[i].green=green;
3267 image->colormap[i].blue=blue;
3274 image_view=AcquireAuthenticCacheView(image,exception);
3275 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3276 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3277 magick_threads(image,image,image->rows,1)
3279 for (y=0; y < (ssize_t) image->rows; y++)
3287 if( IfMagickFalse(status) )
3289 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3290 if (q == (Quantum *) NULL)
3295 for (x=0; x < (ssize_t) image->columns; x++)
3302 red=(double) GetPixelRed(image,q);
3303 green=(double) GetPixelGreen(image,q);
3304 blue=(double) GetPixelBlue(image,q);
3309 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3313 case HCLpColorspace:
3315 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3321 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3328 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3334 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3340 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3344 case LCHabColorspace:
3346 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3351 case LCHuvColorspace:
3353 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3358 SetPixelRed(image,ClampToQuantum(red),q);
3359 SetPixelGreen(image,ClampToQuantum(green),q);
3360 SetPixelBlue(image,ClampToQuantum(blue),q);
3361 q+=GetPixelChannels(image);
3363 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3365 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3370 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3371 #pragma omp critical (MagickCore_ModulateImage)
3373 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3374 if( IfMagickFalse(proceed) )
3378 image_view=DestroyCacheView(image_view);
3383 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3387 % N e g a t e I m a g e %
3391 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3393 % NegateImage() negates the colors in the reference image. The grayscale
3394 % option means that only grayscale values within the image are negated.
3396 % The format of the NegateImage method is:
3398 % MagickBooleanType NegateImage(Image *image,
3399 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3401 % A description of each parameter follows:
3403 % o image: the image.
3405 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3407 % o exception: return any errors or warnings in this structure.
3410 MagickExport MagickBooleanType NegateImage(Image *image,
3411 const MagickBooleanType grayscale,ExceptionInfo *exception)
3413 #define NegateImageTag "Negate/Image"
3430 assert(image != (Image *) NULL);
3431 assert(image->signature == MagickSignature);
3432 if( IfMagickTrue(image->debug) )
3433 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3434 if (image->storage_class == PseudoClass)
3435 for (i=0; i < (ssize_t) image->colors; i++)
3440 if( IfMagickTrue(grayscale) )
3441 if ((image->colormap[i].red != image->colormap[i].green) ||
3442 (image->colormap[i].green != image->colormap[i].blue))
3444 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3445 image->colormap[i].red=QuantumRange-image->colormap[i].red;
3446 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3447 image->colormap[i].green=QuantumRange-image->colormap[i].green;
3448 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3449 image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3456 image_view=AcquireAuthenticCacheView(image,exception);
3457 if( IfMagickTrue(grayscale) )
3459 for (y=0; y < (ssize_t) image->rows; y++)
3470 if( IfMagickFalse(status) )
3472 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3474 if (q == (Quantum *) NULL)
3479 for (x=0; x < (ssize_t) image->columns; x++)
3484 if ((GetPixelReadMask(image,q) == 0) ||
3485 IfMagickTrue(IsPixelGray(image,q)))
3487 q+=GetPixelChannels(image);
3490 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3492 PixelChannel channel=GetPixelChannelChannel(image,i);
3493 PixelTrait traits=GetPixelChannelTraits(image,channel);
3494 if ((traits & UpdatePixelTrait) == 0)
3496 q[i]=QuantumRange-q[i];
3498 q+=GetPixelChannels(image);
3500 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3501 if( IfMagickFalse(sync) )
3503 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3508 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3509 #pragma omp critical (MagickCore_NegateImage)
3511 proceed=SetImageProgress(image,NegateImageTag,progress++,
3513 if( IfMagickFalse(proceed) )
3517 image_view=DestroyCacheView(image_view);
3523 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3524 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3525 magick_threads(image,image,image->rows,1)
3527 for (y=0; y < (ssize_t) image->rows; y++)
3535 if( IfMagickFalse(status) )
3537 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3538 if (q == (Quantum *) NULL)
3543 for (x=0; x < (ssize_t) image->columns; x++)
3548 if (GetPixelReadMask(image,q) == 0)
3550 q+=GetPixelChannels(image);
3553 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3555 PixelChannel channel=GetPixelChannelChannel(image,i);
3556 PixelTrait traits=GetPixelChannelTraits(image,channel);
3557 if ((traits & UpdatePixelTrait) == 0)
3559 q[i]=QuantumRange-q[i];
3561 q+=GetPixelChannels(image);
3563 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3565 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3570 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3571 #pragma omp critical (MagickCore_NegateImage)
3573 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3574 if( IfMagickFalse(proceed) )
3578 image_view=DestroyCacheView(image_view);
3583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3587 % N o r m a l i z e I m a g e %
3591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3593 % The NormalizeImage() method enhances the contrast of a color image by
3594 % mapping the darkest 2 percent of all pixel to black and the brightest
3595 % 1 percent to white.
3597 % The format of the NormalizeImage method is:
3599 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3601 % A description of each parameter follows:
3603 % o image: the image.
3605 % o exception: return any errors or warnings in this structure.
3608 MagickExport MagickBooleanType NormalizeImage(Image *image,
3609 ExceptionInfo *exception)
3615 black_point=(double) image->columns*image->rows*0.0015;
3616 white_point=(double) image->columns*image->rows*0.9995;
3617 return(ContrastStretchImage(image,black_point,white_point,exception));
3621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3625 % S i g m o i d a l C o n t r a s t I m a g e %
3629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3631 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3632 % sigmoidal contrast algorithm. Increase the contrast of the image using a
3633 % sigmoidal transfer function without saturating highlights or shadows.
3634 % Contrast indicates how much to increase the contrast (0 is none; 3 is
3635 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
3636 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
3637 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
3640 % The format of the SigmoidalContrastImage method is:
3642 % MagickBooleanType SigmoidalContrastImage(Image *image,
3643 % const MagickBooleanType sharpen,const char *levels,
3644 % ExceptionInfo *exception)
3646 % A description of each parameter follows:
3648 % o image: the image.
3650 % o sharpen: Increase or decrease image contrast.
3652 % o contrast: strength of the contrast, the larger the number the more
3653 % 'threshold-like' it becomes.
3655 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3657 % o exception: return any errors or warnings in this structure.
3662 ImageMagick 6 has a version of this function which uses LUTs.
3666 Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
3669 The first version, based on the hyperbolic tangent tanh, when combined with
3670 the scaling step, is an exact arithmetic clone of the the sigmoid function
3671 based on the logistic curve. The equivalence is based on the identity
3673 1/(1+exp(-t)) = (1+tanh(t/2))/2
3675 (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3676 scaled sigmoidal derivation is invariant under affine transformations of
3679 The tanh version is almost certainly more accurate and cheaper. The 0.5
3680 factor in the argument is to clone the legacy ImageMagick behavior. The
3681 reason for making the define depend on atanh even though it only uses tanh
3682 has to do with the construction of the inverse of the scaled sigmoidal.
3684 #if defined(MAGICKCORE_HAVE_ATANH)
3685 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3687 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3690 Scaled sigmoidal function:
3692 ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3693 ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3695 See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
3696 http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
3697 of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
3698 zero. This is fixed below by exiting immediately when contrast is small,
3699 leaving the image (or colormap) unmodified. This appears to be safe because
3700 the series expansion of the logistic sigmoidal function around x=b is
3704 so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3706 #define ScaledSigmoidal(a,b,x) ( \
3707 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3708 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3710 Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
3711 may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
3712 sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
3713 when creating a LUT from in gamut values, hence the branching. In
3714 addition, HDRI may have out of gamut values.
3715 InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
3716 It is only a right inverse. This is unavoidable.
3718 static inline double InverseScaledSigmoidal(const double a,const double b,
3721 const double sig0=Sigmoidal(a,b,0.0);
3722 const double sig1=Sigmoidal(a,b,1.0);
3723 const double argument=(sig1-sig0)*x+sig0;
3724 const double clamped=
3726 #if defined(MAGICKCORE_HAVE_ATANH)
3727 argument < -1+MagickEpsilon
3731 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3733 return(b+(2.0/a)*atanh(clamped));
3735 argument < MagickEpsilon
3739 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3741 return(b-log(1.0/clamped-1.0)/a);
3745 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3746 const MagickBooleanType sharpen,const double contrast,const double midpoint,
3747 ExceptionInfo *exception)
3749 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
3750 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3751 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3752 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3753 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3770 assert(image != (Image *) NULL);
3771 assert(image->signature == MagickSignature);
3772 if( IfMagickTrue(image->debug) )
3773 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3775 Side effect: may clamp values unless contrast<MagickEpsilon, in which
3776 case nothing is done.
3778 if (contrast < MagickEpsilon)
3781 Sigmoidal-contrast enhance colormap.
3783 if (image->storage_class == PseudoClass)
3788 if( IfMagickTrue(sharpen) )
3789 for (i=0; i < (ssize_t) image->colors; i++)
3791 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3792 image->colormap[i].red=(MagickRealType) ScaledSig(
3793 image->colormap[i].red);
3794 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3795 image->colormap[i].green=(MagickRealType) ScaledSig(
3796 image->colormap[i].green);
3797 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3798 image->colormap[i].blue=(MagickRealType) ScaledSig(
3799 image->colormap[i].blue);
3800 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3801 image->colormap[i].alpha=(MagickRealType) ScaledSig(
3802 image->colormap[i].alpha);
3805 for (i=0; i < (ssize_t) image->colors; i++)
3807 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3808 image->colormap[i].red=(MagickRealType) InverseScaledSig(
3809 image->colormap[i].red);
3810 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3811 image->colormap[i].green=(MagickRealType) InverseScaledSig(
3812 image->colormap[i].green);
3813 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3814 image->colormap[i].blue=(MagickRealType) InverseScaledSig(
3815 image->colormap[i].blue);
3816 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3817 image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
3818 image->colormap[i].alpha);
3822 Sigmoidal-contrast enhance image.
3826 image_view=AcquireAuthenticCacheView(image,exception);
3827 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3828 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3829 magick_threads(image,image,image->rows,1)
3831 for (y=0; y < (ssize_t) image->rows; y++)
3839 if( IfMagickFalse(status) )
3841 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3842 if (q == (Quantum *) NULL)
3847 for (x=0; x < (ssize_t) image->columns; x++)
3852 if (GetPixelReadMask(image,q) == 0)
3854 q+=GetPixelChannels(image);
3857 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3859 PixelChannel channel=GetPixelChannelChannel(image,i);
3860 PixelTrait traits=GetPixelChannelTraits(image,channel);
3861 if ((traits & UpdatePixelTrait) == 0)
3863 if( IfMagickTrue(sharpen) )
3864 q[i]=ScaledSig(q[i]);
3866 q[i]=InverseScaledSig(q[i]);
3868 q+=GetPixelChannels(image);
3870 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3872 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3877 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3878 #pragma omp critical (MagickCore_SigmoidalContrastImage)
3880 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3882 if( IfMagickFalse(proceed) )
3886 image_view=DestroyCacheView(image_view);