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 (image->debug != MagickFalse)
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 (image->debug != MagickFalse)
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 (double) i*(clut_image->columns-adjust)/MaxMap,(double) i*
348 (clut_image->rows-adjust)/MaxMap,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 (image->debug != MagickFalse)
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 (image->debug != MagickFalse)
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 (image->debug != MagickFalse)
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 (image->debug != MagickFalse)
1034 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1035 number_channels=GetPixelChannels(image);
1036 black=(double *) AcquireQuantumMemory(number_channels,sizeof(*black));
1037 white=(double *) AcquireQuantumMemory(number_channels,sizeof(*white));
1038 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,number_channels*
1039 sizeof(*histogram));
1040 stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,number_channels*
1041 sizeof(*stretch_map));
1042 if ((black == (double *) NULL) || (white == (double *) NULL) ||
1043 (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1045 if (stretch_map != (double *) NULL)
1046 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1047 if (histogram != (double *) NULL)
1048 histogram=(double *) RelinquishMagickMemory(histogram);
1049 if (white != (double *) NULL)
1050 white=(double *) RelinquishMagickMemory(white);
1051 if (black != (double *) NULL)
1052 black=(double *) RelinquishMagickMemory(black);
1053 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1059 if( IfMagickTrue(IsImageGray(image,exception)) )
1060 (void) SetImageColorspace(image,GRAYColorspace,exception);
1062 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*number_channels*
1063 sizeof(*histogram));
1064 image_view=AcquireVirtualCacheView(image,exception);
1065 for (y=0; y < (ssize_t) image->rows; y++)
1067 register const Quantum
1073 if( IfMagickFalse(status) )
1075 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1076 if (p == (const Quantum *) NULL)
1081 for (x=0; x < (ssize_t) image->columns; x++)
1089 pixel=GetPixelIntensity(image,p);
1090 for (i=0; i < (ssize_t) number_channels; i++)
1092 if (image->channel_mask != DefaultChannels)
1093 pixel=(double) p[i];
1094 histogram[number_channels*ScaleQuantumToMap(ClampToQuantum(pixel))+i]++;
1099 image_view=DestroyCacheView(image_view);
1101 Find the histogram boundaries by locating the black/white levels.
1103 for (i=0; i < (ssize_t) number_channels; i++)
1112 white[i]=MaxRange(QuantumRange);
1114 for (j=0; j <= (ssize_t) MaxMap; j++)
1116 intensity+=histogram[number_channels*j+i];
1117 if (intensity > black_point)
1120 black[i]=(double) j;
1122 for (j=(ssize_t) MaxMap; j != 0; j--)
1124 intensity+=histogram[number_channels*j+i];
1125 if (intensity > ((double) image->columns*image->rows-white_point))
1128 white[i]=(double) j;
1130 histogram=(double *) RelinquishMagickMemory(histogram);
1132 Stretch the histogram to create the stretched image mapping.
1134 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*number_channels*
1135 sizeof(*stretch_map));
1136 for (i=0; i < (ssize_t) number_channels; i++)
1141 for (j=0; j <= (ssize_t) MaxMap; j++)
1143 if (j < (ssize_t) black[i])
1144 stretch_map[number_channels*j+i]=0.0;
1146 if (j > (ssize_t) white[i])
1147 stretch_map[number_channels*j+i]=(double) QuantumRange;
1149 if (black[i] != white[i])
1150 stretch_map[number_channels*j+i]=(double) ScaleMapToQuantum(
1151 (double) (MaxMap*(j-black[i])/(white[i]-black[i])));
1154 if (image->storage_class == PseudoClass)
1160 Stretch-contrast colormap.
1162 for (j=0; j < (ssize_t) image->colors; j++)
1164 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1166 i=GetPixelChannelChannel(image,RedPixelChannel);
1167 if (black[i] != white[i])
1168 image->colormap[j].red=stretch_map[number_channels*
1169 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+i];
1171 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1173 i=GetPixelChannelChannel(image,GreenPixelChannel);
1174 if (black[i] != white[i])
1175 image->colormap[j].green=stretch_map[number_channels*
1176 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+i];
1178 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1180 i=GetPixelChannelChannel(image,BluePixelChannel);
1181 if (black[i] != white[i])
1182 image->colormap[j].blue=stretch_map[number_channels*
1183 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+i];
1185 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1187 i=GetPixelChannelChannel(image,AlphaPixelChannel);
1188 if (black[i] != white[i])
1189 image->colormap[j].alpha=stretch_map[number_channels*
1190 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+i];
1195 Stretch-contrast image.
1199 image_view=AcquireAuthenticCacheView(image,exception);
1200 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1201 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1202 magick_threads(image,image,image->rows,1)
1204 for (y=0; y < (ssize_t) image->rows; y++)
1212 if( IfMagickFalse(status) )
1214 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1215 if (q == (Quantum *) NULL)
1220 for (x=0; x < (ssize_t) image->columns; x++)
1225 if (GetPixelReadMask(image,q) == 0)
1230 for (i=0; i < (ssize_t) number_channels; i++)
1232 PixelChannel channel=GetPixelChannelChannel(image,i);
1233 PixelTrait traits=GetPixelChannelTraits(image,channel);
1234 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1236 q[i]=ClampToQuantum(stretch_map[number_channels*
1237 ScaleQuantumToMap(q[i])+i]);
1241 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
1243 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1248 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1249 #pragma omp critical (MagickCore_ContrastStretchImage)
1251 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1253 if( IfMagickFalse(proceed) )
1257 image_view=DestroyCacheView(image_view);
1258 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1259 white=(double *) RelinquishMagickMemory(white);
1260 black=(double *) RelinquishMagickMemory(black);
1265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1269 % E n h a n c e I m a g e %
1273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1275 % EnhanceImage() applies a digital filter that improves the quality of a
1278 % The format of the EnhanceImage method is:
1280 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1282 % A description of each parameter follows:
1284 % o image: the image.
1286 % o exception: return any errors or warnings in this structure.
1289 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1291 #define EnhancePixel(weight) \
1292 mean=((double) r[i]+GetPixelChannel(enhance_image,channel,q))/2.0; \
1293 distance=(double) r[i]-(double) GetPixelChannel(enhance_image,channel,q); \
1294 distance_squared=QuantumScale*(2.0*((double) QuantumRange+1.0)+mean)* \
1295 distance*distance; \
1296 if (distance_squared < ((double) QuantumRange*(double) QuantumRange/25.0f)) \
1298 aggregate+=(weight)*r[i]; \
1299 total_weight+=(weight); \
1301 r+=GetPixelChannels(image);
1302 #define EnhanceImageTag "Enhance/Image"
1321 Initialize enhanced image attributes.
1323 assert(image != (const Image *) NULL);
1324 assert(image->signature == MagickSignature);
1325 if (image->debug != MagickFalse)
1326 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1327 assert(exception != (ExceptionInfo *) NULL);
1328 assert(exception->signature == MagickSignature);
1329 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1331 if (enhance_image == (Image *) NULL)
1332 return((Image *) NULL);
1333 if( IfMagickFalse(SetImageStorageClass(enhance_image,DirectClass,exception)) )
1335 enhance_image=DestroyImage(enhance_image);
1336 return((Image *) NULL);
1343 image_view=AcquireVirtualCacheView(image,exception);
1344 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1345 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1346 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1347 magick_threads(image,enhance_image,image->rows,1)
1349 for (y=0; y < (ssize_t) image->rows; y++)
1351 register const Quantum
1363 if( IfMagickFalse(status) )
1365 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1366 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1368 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1373 center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1374 for (x=0; x < (ssize_t) image->columns; x++)
1379 if (GetPixelReadMask(image,p) == 0)
1381 SetPixelBackgoundColor(enhance_image,q);
1382 p+=GetPixelChannels(image);
1383 q+=GetPixelChannels(enhance_image);
1386 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1395 register const Quantum
1398 PixelChannel channel=GetPixelChannelChannel(image,i);
1399 PixelTrait traits=GetPixelChannelTraits(image,channel);
1400 PixelTrait enhance_traits=GetPixelChannelTraits(enhance_image,channel);
1401 if ((traits == UndefinedPixelTrait) ||
1402 (enhance_traits == UndefinedPixelTrait))
1404 SetPixelChannel(enhance_image,channel,p[center+i],q);
1405 if ((enhance_traits & CopyPixelTrait) != 0)
1408 Compute weighted average of target pixel color components.
1413 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1414 EnhancePixel(8.0); EnhancePixel(5.0);
1415 r=p+1*GetPixelChannels(image)*(image->columns+4);
1416 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1417 EnhancePixel(20.0); EnhancePixel(8.0);
1418 r=p+2*GetPixelChannels(image)*(image->columns+4);
1419 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1420 EnhancePixel(40.0); EnhancePixel(10.0);
1421 r=p+3*GetPixelChannels(image)*(image->columns+4);
1422 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1423 EnhancePixel(20.0); EnhancePixel(8.0);
1424 r=p+4*GetPixelChannels(image)*(image->columns+4);
1425 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1426 EnhancePixel(8.0); EnhancePixel(5.0);
1427 SetPixelChannel(enhance_image,channel,ClampToQuantum(aggregate/
1430 p+=GetPixelChannels(image);
1431 q+=GetPixelChannels(enhance_image);
1433 if( IfMagickFalse(SyncCacheViewAuthenticPixels(enhance_view,exception)) )
1435 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1440 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1441 #pragma omp critical (MagickCore_EnhanceImage)
1443 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1444 if( IfMagickFalse(proceed) )
1448 enhance_view=DestroyCacheView(enhance_view);
1449 image_view=DestroyCacheView(image_view);
1450 if( IfMagickFalse(status) )
1451 enhance_image=DestroyImage(enhance_image);
1452 return(enhance_image);
1456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1460 % E q u a l i z e I m a g e %
1464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466 % EqualizeImage() applies a histogram equalization to the image.
1468 % The format of the EqualizeImage method is:
1470 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
1472 % A description of each parameter follows:
1474 % o image: the image.
1476 % o exception: return any errors or warnings in this structure.
1479 MagickExport MagickBooleanType EqualizeImage(Image *image,
1480 ExceptionInfo *exception)
1482 #define EqualizeImageTag "Equalize/Image"
1494 black[CompositePixelChannel+1],
1498 white[CompositePixelChannel+1];
1510 Allocate and initialize histogram arrays.
1512 assert(image != (Image *) NULL);
1513 assert(image->signature == MagickSignature);
1514 if (image->debug != MagickFalse)
1515 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1516 equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1517 GetPixelChannels(image)*sizeof(*equalize_map));
1518 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1519 sizeof(*histogram));
1520 map=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1522 if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
1523 (map == (double *) NULL))
1525 if (map != (double *) NULL)
1526 map=(double *) RelinquishMagickMemory(map);
1527 if (histogram != (double *) NULL)
1528 histogram=(double *) RelinquishMagickMemory(histogram);
1529 if (equalize_map != (double *) NULL)
1530 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1531 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1538 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1539 sizeof(*histogram));
1540 image_view=AcquireVirtualCacheView(image,exception);
1541 for (y=0; y < (ssize_t) image->rows; y++)
1543 register const Quantum
1549 if( IfMagickFalse(status) )
1551 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1552 if (p == (const Quantum *) NULL)
1557 for (x=0; x < (ssize_t) image->columns; x++)
1562 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1563 histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++;
1564 p+=GetPixelChannels(image);
1567 image_view=DestroyCacheView(image_view);
1569 Integrate the histogram to get the equalization map.
1571 number_channels=GetPixelChannels(image);
1572 for (i=0; i < (ssize_t) number_channels; i++)
1581 for (j=0; j <= (ssize_t) MaxMap; j++)
1583 intensity+=histogram[GetPixelChannels(image)*j+i];
1584 map[GetPixelChannels(image)*j+i]=intensity;
1587 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
1588 sizeof(*equalize_map));
1589 (void) ResetMagickMemory(black,0,sizeof(*black));
1590 (void) ResetMagickMemory(white,0,sizeof(*white));
1591 number_channels=GetPixelChannels(image);
1592 for (i=0; i < (ssize_t) number_channels; i++)
1598 white[i]=map[GetPixelChannels(image)*MaxMap+i];
1599 if (black[i] != white[i])
1600 for (j=0; j <= (ssize_t) MaxMap; j++)
1601 equalize_map[GetPixelChannels(image)*j+i]=(double)
1602 ScaleMapToQuantum((double) ((MaxMap*(map[
1603 GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
1605 histogram=(double *) RelinquishMagickMemory(histogram);
1606 map=(double *) RelinquishMagickMemory(map);
1607 if (image->storage_class == PseudoClass)
1615 for (j=0; j < (ssize_t) image->colors; j++)
1617 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1619 PixelChannel channel=GetPixelChannelChannel(image,RedPixelChannel);
1620 if (black[channel] != white[channel])
1621 image->colormap[j].red=equalize_map[GetPixelChannels(image)*
1622 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))]+
1625 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1627 PixelChannel channel=GetPixelChannelChannel(image,
1629 if (black[channel] != white[channel])
1630 image->colormap[j].green=equalize_map[GetPixelChannels(image)*
1631 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))]+
1634 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1636 PixelChannel channel=GetPixelChannelChannel(image,BluePixelChannel);
1637 if (black[channel] != white[channel])
1638 image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
1639 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))]+
1642 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1644 PixelChannel channel=GetPixelChannelChannel(image,
1646 if (black[channel] != white[channel])
1647 image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
1648 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))]+
1657 image_view=AcquireAuthenticCacheView(image,exception);
1658 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1659 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1660 magick_threads(image,image,image->rows,1)
1662 for (y=0; y < (ssize_t) image->rows; y++)
1670 if( IfMagickFalse(status) )
1672 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1673 if (q == (Quantum *) NULL)
1678 for (x=0; x < (ssize_t) image->columns; x++)
1683 if (GetPixelReadMask(image,q) == 0)
1685 q+=GetPixelChannels(image);
1688 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1690 PixelChannel channel=GetPixelChannelChannel(image,i);
1691 PixelTrait traits=GetPixelChannelTraits(image,channel);
1692 if (((traits & UpdatePixelTrait) == 0) || (black[i] == white[i]))
1694 q[i]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
1695 ScaleQuantumToMap(q[i])+i]);
1697 q+=GetPixelChannels(image);
1699 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
1701 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1706 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1707 #pragma omp critical (MagickCore_EqualizeImage)
1709 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1710 if( IfMagickFalse(proceed) )
1714 image_view=DestroyCacheView(image_view);
1715 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724 % G a m m a I m a g e %
1728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1730 % GammaImage() gamma-corrects a particular image channel. The same
1731 % image viewed on different devices will have perceptual differences in the
1732 % way the image's intensities are represented on the screen. Specify
1733 % individual gamma levels for the red, green, and blue channels, or adjust
1734 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
1736 % You can also reduce the influence of a particular channel with a gamma
1739 % The format of the GammaImage method is:
1741 % MagickBooleanType GammaImage(Image *image,const double gamma,
1742 % ExceptionInfo *exception)
1744 % A description of each parameter follows:
1746 % o image: the image.
1748 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1750 % o gamma: the image gamma.
1754 static inline double gamma_pow(const double value,const double gamma)
1756 return(value < 0.0 ? value : pow(value,gamma));
1759 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
1760 ExceptionInfo *exception)
1762 #define GammaCorrectImageTag "GammaCorrect/Image"
1783 Allocate and initialize gamma maps.
1785 assert(image != (Image *) NULL);
1786 assert(image->signature == MagickSignature);
1787 if (image->debug != MagickFalse)
1788 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1791 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1792 if (gamma_map == (Quantum *) NULL)
1793 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1795 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1797 for (i=0; i <= (ssize_t) MaxMap; i++)
1798 gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
1799 MaxMap,1.0/gamma)));
1800 if (image->storage_class == PseudoClass)
1801 for (i=0; i < (ssize_t) image->colors; i++)
1804 Gamma-correct colormap.
1806 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1807 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1808 image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
1809 ClampToQuantum(image->colormap[i].red))];
1810 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1811 image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
1812 ClampToQuantum(image->colormap[i].green))];
1813 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1814 image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
1815 ClampToQuantum(image->colormap[i].blue))];
1816 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1817 image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
1818 ClampToQuantum(image->colormap[i].alpha))];
1820 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1821 image->colormap[i].red=QuantumRange*gamma_pow(QuantumScale*
1822 image->colormap[i].red,1.0/gamma);
1823 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1824 image->colormap[i].green=QuantumRange*gamma_pow(QuantumScale*
1825 image->colormap[i].green,1.0/gamma);
1826 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1827 image->colormap[i].blue=QuantumRange*gamma_pow(QuantumScale*
1828 image->colormap[i].blue,1.0/gamma);
1829 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1830 image->colormap[i].alpha=QuantumRange*gamma_pow(QuantumScale*
1831 image->colormap[i].alpha,1.0/gamma);
1835 Gamma-correct image.
1839 image_view=AcquireAuthenticCacheView(image,exception);
1840 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1841 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1842 magick_threads(image,image,image->rows,1)
1844 for (y=0; y < (ssize_t) image->rows; y++)
1852 if( IfMagickFalse(status) )
1854 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1855 if (q == (Quantum *) NULL)
1860 for (x=0; x < (ssize_t) image->columns; x++)
1865 if (GetPixelReadMask(image,q) == 0)
1867 q+=GetPixelChannels(image);
1870 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1872 PixelChannel channel=GetPixelChannelChannel(image,i);
1873 PixelTrait traits=GetPixelChannelTraits(image,channel);
1874 if ((traits & UpdatePixelTrait) == 0)
1876 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1877 q[i]=gamma_map[ScaleQuantumToMap(q[i])];
1879 q[i]=QuantumRange*gamma_pow(QuantumScale*q[i],1.0/gamma);
1882 q+=GetPixelChannels(image);
1884 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
1886 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1891 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1892 #pragma omp critical (MagickCore_GammaImage)
1894 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
1896 if( IfMagickFalse(proceed) )
1900 image_view=DestroyCacheView(image_view);
1901 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
1902 if (image->gamma != 0.0)
1903 image->gamma*=gamma;
1908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1912 % G r a y s c a l e I m a g e %
1916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1918 % GrayscaleImage() converts the image to grayscale.
1920 % The format of the GrayscaleImage method is:
1922 % MagickBooleanType GrayscaleImage(Image *image,
1923 % const PixelIntensityMethod method ,ExceptionInfo *exception)
1925 % A description of each parameter follows:
1927 % o image: the image.
1929 % o method: the pixel intensity method.
1931 % o exception: return any errors or warnings in this structure.
1935 static inline MagickRealType MagickMax(const MagickRealType x,
1936 const MagickRealType y)
1943 static inline MagickRealType MagickMin(const MagickRealType x,
1944 const MagickRealType y)
1951 MagickExport MagickBooleanType GrayscaleImage(Image *image,
1952 const PixelIntensityMethod method,ExceptionInfo *exception)
1954 #define GrayscaleImageTag "Grayscale/Image"
1968 assert(image != (Image *) NULL);
1969 assert(image->signature == MagickSignature);
1970 if (image->debug != MagickFalse)
1971 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1972 if (image->storage_class == PseudoClass)
1974 if( IfMagickFalse(SyncImage(image,exception)) )
1975 return(MagickFalse);
1976 if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
1977 return(MagickFalse);
1984 image_view=AcquireAuthenticCacheView(image,exception);
1985 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1986 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1987 magick_threads(image,image,image->rows,1)
1989 for (y=0; y < (ssize_t) image->rows; y++)
1997 if( IfMagickFalse(status) )
1999 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2000 if (q == (Quantum *) NULL)
2005 for (x=0; x < (ssize_t) image->columns; x++)
2013 if (GetPixelReadMask(image,q) == 0)
2015 q+=GetPixelChannels(image);
2018 red=(MagickRealType) GetPixelRed(image,q);
2019 green=(MagickRealType) GetPixelGreen(image,q);
2020 blue=(MagickRealType) GetPixelBlue(image,q);
2024 case AveragePixelIntensityMethod:
2026 intensity=(red+green+blue)/3.0;
2029 case BrightnessPixelIntensityMethod:
2031 intensity=MagickMax(MagickMax(red,green),blue);
2034 case LightnessPixelIntensityMethod:
2036 intensity=(MagickMin(MagickMin(red,green),blue)+
2037 MagickMax(MagickMax(red,green),blue))/2.0;
2040 case MSPixelIntensityMethod:
2042 intensity=(MagickRealType) (((double) red*red+green*green+
2046 case Rec601LumaPixelIntensityMethod:
2048 if (image->colorspace == RGBColorspace)
2050 red=EncodePixelGamma(red);
2051 green=EncodePixelGamma(green);
2052 blue=EncodePixelGamma(blue);
2054 intensity=0.298839*red+0.586811*green+0.114350*blue;
2057 case Rec601LuminancePixelIntensityMethod:
2059 if (image->colorspace == sRGBColorspace)
2061 red=DecodePixelGamma(red);
2062 green=DecodePixelGamma(green);
2063 blue=DecodePixelGamma(blue);
2065 intensity=0.298839*red+0.586811*green+0.114350*blue;
2068 case Rec709LumaPixelIntensityMethod:
2071 if (image->colorspace == RGBColorspace)
2073 red=EncodePixelGamma(red);
2074 green=EncodePixelGamma(green);
2075 blue=EncodePixelGamma(blue);
2077 intensity=0.212656*red+0.715158*green+0.072186*blue;
2080 case Rec709LuminancePixelIntensityMethod:
2082 if (image->colorspace == sRGBColorspace)
2084 red=DecodePixelGamma(red);
2085 green=DecodePixelGamma(green);
2086 blue=DecodePixelGamma(blue);
2088 intensity=0.212656*red+0.715158*green+0.072186*blue;
2091 case RMSPixelIntensityMethod:
2093 intensity=(MagickRealType) (sqrt((double) red*red+green*green+
2094 blue*blue)/sqrt(3.0));
2098 SetPixelGray(image,ClampToQuantum(intensity),q);
2099 q+=GetPixelChannels(image);
2101 if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)))
2103 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2108 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2109 #pragma omp critical (MagickCore_GrayscaleImage)
2111 proceed=SetImageProgress(image,GrayscaleImageTag,progress++,
2113 if( IfMagickFalse(proceed) )
2117 image_view=DestroyCacheView(image_view);
2118 image->intensity=method;
2119 image->type=GrayscaleType;
2120 return(SetImageColorspace(image,GRAYColorspace,exception));
2124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2128 % H a l d C l u t I m a g e %
2132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2134 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
2135 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2136 % Create it with the HALD coder. You can apply any color transformation to
2137 % the Hald image and then use this method to apply the transform to the
2140 % The format of the HaldClutImage method is:
2142 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
2143 % ExceptionInfo *exception)
2145 % A description of each parameter follows:
2147 % o image: the image, which is replaced by indexed CLUT values
2149 % o hald_image: the color lookup table image for replacement color values.
2151 % o exception: return any errors or warnings in this structure.
2154 MagickExport MagickBooleanType HaldClutImage(Image *image,
2155 const Image *hald_image,ExceptionInfo *exception)
2157 #define HaldClutImageTag "Clut/Image"
2159 typedef struct _HaldInfo
2191 assert(image != (Image *) NULL);
2192 assert(image->signature == MagickSignature);
2193 if (image->debug != MagickFalse)
2194 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2195 assert(hald_image != (Image *) NULL);
2196 assert(hald_image->signature == MagickSignature);
2197 if( IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
2198 return(MagickFalse);
2199 if (image->alpha_trait != BlendPixelTrait)
2200 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2206 length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2207 (MagickRealType) hald_image->rows);
2208 for (level=2; (level*level*level) < length; level++) ;
2210 cube_size=level*level;
2211 width=(double) hald_image->columns;
2212 GetPixelInfo(hald_image,&zero);
2213 hald_view=AcquireVirtualCacheView(hald_image,exception);
2214 image_view=AcquireAuthenticCacheView(image,exception);
2215 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2216 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2217 magick_threads(image,image,image->rows,1)
2219 for (y=0; y < (ssize_t) image->rows; y++)
2227 if( IfMagickFalse(status) )
2229 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2230 if (q == (Quantum *) NULL)
2235 for (x=0; x < (ssize_t) image->columns; x++)
2250 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2251 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2252 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2253 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2254 point.x-=floor(point.x);
2255 point.y-=floor(point.y);
2256 point.z-=floor(point.z);
2258 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2259 fmod(offset,width),floor(offset/width),&pixel1,exception);
2261 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2262 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2264 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2267 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2268 fmod(offset,width),floor(offset/width),&pixel1,exception);
2269 (void) InterpolatePixelInfo(image,hald_view,image->interpolate,
2270 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2272 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2275 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2277 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2278 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2279 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2280 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2281 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2282 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2283 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2284 (image->colorspace == CMYKColorspace))
2285 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2286 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2287 (image->alpha_trait == BlendPixelTrait))
2288 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2289 q+=GetPixelChannels(image);
2291 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2293 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2298 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2299 #pragma omp critical (MagickCore_HaldClutImage)
2301 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2302 if( IfMagickFalse(proceed) )
2306 hald_view=DestroyCacheView(hald_view);
2307 image_view=DestroyCacheView(image_view);
2312 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2316 % L e v e l I m a g e %
2320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2322 % LevelImage() adjusts the levels of a particular image channel by
2323 % scaling the colors falling between specified white and black points to
2324 % the full available quantum range.
2326 % The parameters provided represent the black, and white points. The black
2327 % point specifies the darkest color in the image. Colors darker than the
2328 % black point are set to zero. White point specifies the lightest color in
2329 % the image. Colors brighter than the white point are set to the maximum
2332 % If a '!' flag is given, map black and white colors to the given levels
2333 % rather than mapping those levels to black and white. See
2334 % LevelizeImage() below.
2336 % Gamma specifies a gamma correction to apply to the image.
2338 % The format of the LevelImage method is:
2340 % MagickBooleanType LevelImage(Image *image,const double black_point,
2341 % const double white_point,const double gamma,ExceptionInfo *exception)
2343 % A description of each parameter follows:
2345 % o image: the image.
2347 % o black_point: The level to map zero (black) to.
2349 % o white_point: The level to map QuantumRange (white) to.
2351 % o exception: return any errors or warnings in this structure.
2355 static inline double LevelPixel(const double black_point,
2356 const double white_point,const double gamma,const double pixel)
2362 scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0;
2363 level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
2365 return(level_pixel);
2368 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2369 const double white_point,const double gamma,ExceptionInfo *exception)
2371 #define LevelImageTag "Level/Image"
2389 Allocate and initialize levels map.
2391 assert(image != (Image *) NULL);
2392 assert(image->signature == MagickSignature);
2393 if (image->debug != MagickFalse)
2394 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2395 if (image->storage_class == PseudoClass)
2396 for (i=0; i < (ssize_t) image->colors; i++)
2401 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2402 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2403 white_point,gamma,image->colormap[i].red));
2404 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2405 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2406 white_point,gamma,image->colormap[i].green));
2407 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2408 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2409 white_point,gamma,image->colormap[i].blue));
2410 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2411 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2412 white_point,gamma,image->colormap[i].alpha));
2419 image_view=AcquireAuthenticCacheView(image,exception);
2420 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2421 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2422 magick_threads(image,image,image->rows,1)
2424 for (y=0; y < (ssize_t) image->rows; y++)
2432 if( IfMagickFalse(status) )
2434 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2435 if (q == (Quantum *) NULL)
2440 for (x=0; x < (ssize_t) image->columns; x++)
2445 if (GetPixelReadMask(image,q) == 0)
2447 q+=GetPixelChannels(image);
2450 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2452 PixelChannel channel=GetPixelChannelChannel(image,i);
2453 PixelTrait traits=GetPixelChannelTraits(image,channel);
2454 if ((traits & UpdatePixelTrait) == 0)
2456 q[i]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2459 q+=GetPixelChannels(image);
2461 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2463 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2468 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2469 #pragma omp critical (MagickCore_LevelImage)
2471 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2472 if( IfMagickFalse(proceed) )
2476 image_view=DestroyCacheView(image_view);
2477 (void) ClampImage(image,exception);
2482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2486 % L e v e l i z e I m a g e %
2490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2492 % LevelizeImage() applies the reversed LevelImage() operation to just
2493 % the specific channels specified. It compresses the full range of color
2494 % values, so that they lie between the given black and white points. Gamma is
2495 % applied before the values are mapped.
2497 % LevelizeImage() can be called with by using a +level command line
2498 % API option, or using a '!' on a -level or LevelImage() geometry string.
2500 % It can be used to de-contrast a greyscale image to the exact levels
2501 % specified. Or by using specific levels for each channel of an image you
2502 % can convert a gray-scale image to any linear color gradient, according to
2505 % The format of the LevelizeImage method is:
2507 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
2508 % const double white_point,const double gamma,ExceptionInfo *exception)
2510 % A description of each parameter follows:
2512 % o image: the image.
2514 % o black_point: The level to map zero (black) to.
2516 % o white_point: The level to map QuantumRange (white) to.
2518 % o gamma: adjust gamma by this factor before mapping values.
2520 % o exception: return any errors or warnings in this structure.
2523 MagickExport MagickBooleanType LevelizeImage(Image *image,
2524 const double black_point,const double white_point,const double gamma,
2525 ExceptionInfo *exception)
2527 #define LevelizeImageTag "Levelize/Image"
2528 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
2529 (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
2547 Allocate and initialize levels map.
2549 assert(image != (Image *) NULL);
2550 assert(image->signature == MagickSignature);
2551 if (image->debug != MagickFalse)
2552 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2553 if (image->storage_class == PseudoClass)
2554 for (i=0; i < (ssize_t) image->colors; i++)
2559 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2560 image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
2561 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2562 image->colormap[i].green=(double) LevelizeValue(
2563 image->colormap[i].green);
2564 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2565 image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
2566 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2567 image->colormap[i].alpha=(double) LevelizeValue(
2568 image->colormap[i].alpha);
2575 image_view=AcquireAuthenticCacheView(image,exception);
2576 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2577 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2578 magick_threads(image,image,image->rows,1)
2580 for (y=0; y < (ssize_t) image->rows; y++)
2588 if( IfMagickFalse(status) )
2590 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2591 if (q == (Quantum *) NULL)
2596 for (x=0; x < (ssize_t) image->columns; x++)
2601 if (GetPixelReadMask(image,q) == 0)
2603 q+=GetPixelChannels(image);
2606 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2608 PixelChannel channel=GetPixelChannelChannel(image,i);
2609 PixelTrait traits=GetPixelChannelTraits(image,channel);
2610 if ((traits & UpdatePixelTrait) == 0)
2612 q[i]=LevelizeValue(q[i]);
2614 q+=GetPixelChannels(image);
2616 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
2618 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2623 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2624 #pragma omp critical (MagickCore_LevelizeImage)
2626 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2627 if( IfMagickFalse(proceed) )
2631 image_view=DestroyCacheView(image_view);
2636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2640 % L e v e l I m a g e C o l o r s %
2644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2646 % LevelImageColors() maps the given color to "black" and "white" values,
2647 % linearly spreading out the colors, and level values on a channel by channel
2648 % bases, as per LevelImage(). The given colors allows you to specify
2649 % different level ranges for each of the color channels separately.
2651 % If the boolean 'invert' is set true the image values will modifyed in the
2652 % reverse direction. That is any existing "black" and "white" colors in the
2653 % image will become the color values given, with all other values compressed
2654 % appropriatally. This effectivally maps a greyscale gradient into the given
2657 % The format of the LevelImageColors method is:
2659 % MagickBooleanType LevelImageColors(Image *image,
2660 % const PixelInfo *black_color,const PixelInfo *white_color,
2661 % const MagickBooleanType invert,ExceptionInfo *exception)
2663 % A description of each parameter follows:
2665 % o image: the image.
2667 % o black_color: The color to map black to/from
2669 % o white_point: The color to map white to/from
2671 % o invert: if true map the colors (levelize), rather than from (level)
2673 % o exception: return any errors or warnings in this structure.
2676 MagickExport MagickBooleanType LevelImageColors(Image *image,
2677 const PixelInfo *black_color,const PixelInfo *white_color,
2678 const MagickBooleanType invert,ExceptionInfo *exception)
2687 Allocate and initialize levels map.
2689 assert(image != (Image *) NULL);
2690 assert(image->signature == MagickSignature);
2691 if (image->debug != MagickFalse)
2692 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2693 if( IfMagickTrue(IsGrayColorspace(image->colorspace)) &&
2694 (IfMagickFalse(IsGrayColorspace(black_color->colorspace)) ||
2695 IfMagickFalse(IsGrayColorspace(white_color->colorspace))))
2696 (void) SetImageColorspace(image,sRGBColorspace,exception);
2698 if( IfMagickFalse(invert) )
2700 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2702 channel_mask=SetImageChannelMask(image,RedChannel);
2703 status&=LevelImage(image,black_color->red,white_color->red,1.0,
2705 (void) SetImageChannelMask(image,channel_mask);
2707 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2709 channel_mask=SetImageChannelMask(image,GreenChannel);
2710 status&=LevelImage(image,black_color->green,white_color->green,1.0,
2712 (void) SetImageChannelMask(image,channel_mask);
2714 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2716 channel_mask=SetImageChannelMask(image,BlueChannel);
2717 status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
2719 (void) SetImageChannelMask(image,channel_mask);
2721 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2722 (image->colorspace == CMYKColorspace))
2724 channel_mask=SetImageChannelMask(image,BlackChannel);
2725 status&=LevelImage(image,black_color->black,white_color->black,1.0,
2727 (void) SetImageChannelMask(image,channel_mask);
2729 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2730 (image->alpha_trait == BlendPixelTrait))
2732 channel_mask=SetImageChannelMask(image,AlphaChannel);
2733 status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2735 (void) SetImageChannelMask(image,channel_mask);
2740 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2742 channel_mask=SetImageChannelMask(image,RedChannel);
2743 status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
2745 (void) SetImageChannelMask(image,channel_mask);
2747 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2749 channel_mask=SetImageChannelMask(image,GreenChannel);
2750 status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
2752 (void) SetImageChannelMask(image,channel_mask);
2754 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2756 channel_mask=SetImageChannelMask(image,BlueChannel);
2757 status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2759 (void) SetImageChannelMask(image,channel_mask);
2761 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2762 (image->colorspace == CMYKColorspace))
2764 channel_mask=SetImageChannelMask(image,BlackChannel);
2765 status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
2767 (void) SetImageChannelMask(image,channel_mask);
2769 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2770 (image->alpha_trait == BlendPixelTrait))
2772 channel_mask=SetImageChannelMask(image,AlphaChannel);
2773 status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2775 (void) SetImageChannelMask(image,channel_mask);
2778 return(status != 0 ? MagickTrue : MagickFalse);
2782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2786 % L i n e a r S t r e t c h I m a g e %
2790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2792 % LinearStretchImage() discards any pixels below the black point and above
2793 % the white point and levels the remaining pixels.
2795 % The format of the LinearStretchImage method is:
2797 % MagickBooleanType LinearStretchImage(Image *image,
2798 % const double black_point,const double white_point,
2799 % ExceptionInfo *exception)
2801 % A description of each parameter follows:
2803 % o image: the image.
2805 % o black_point: the black point.
2807 % o white_point: the white point.
2809 % o exception: return any errors or warnings in this structure.
2812 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2813 const double black_point,const double white_point,ExceptionInfo *exception)
2815 #define LinearStretchImageTag "LinearStretch/Image"
2833 Allocate histogram and linear map.
2835 assert(image != (Image *) NULL);
2836 assert(image->signature == MagickSignature);
2837 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
2838 if (histogram == (double *) NULL)
2839 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2844 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2845 image_view=AcquireVirtualCacheView(image,exception);
2846 for (y=0; y < (ssize_t) image->rows; y++)
2848 register const Quantum
2854 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2855 if (p == (const Quantum *) NULL)
2857 for (x=0; x < (ssize_t) image->columns; x++)
2862 intensity=GetPixelIntensity(image,p);
2863 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2864 p+=GetPixelChannels(image);
2867 image_view=DestroyCacheView(image_view);
2869 Find the histogram boundaries by locating the black and white point levels.
2872 for (black=0; black < (ssize_t) MaxMap; black++)
2874 intensity+=histogram[black];
2875 if (intensity >= black_point)
2879 for (white=(ssize_t) MaxMap; white != 0; white--)
2881 intensity+=histogram[white];
2882 if (intensity >= white_point)
2885 histogram=(double *) RelinquishMagickMemory(histogram);
2886 status=LevelImage(image,(double) black,(double) white,1.0,exception);
2891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2895 % M o d u l a t e I m a g e %
2899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2901 % ModulateImage() lets you control the brightness, saturation, and hue
2902 % of an image. Modulate represents the brightness, saturation, and hue
2903 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
2904 % modulation is lightness, saturation, and hue. For HWB, use blackness,
2905 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
2907 % The format of the ModulateImage method is:
2909 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
2910 % ExceptionInfo *exception)
2912 % A description of each parameter follows:
2914 % o image: the image.
2916 % o modulate: Define the percent change in brightness, saturation, and hue.
2918 % o exception: return any errors or warnings in this structure.
2922 static inline void ModulateHCL(const double percent_hue,
2923 const double percent_chroma,const double percent_luma,double *red,
2924 double *green,double *blue)
2932 Increase or decrease color luma, chroma, or hue.
2934 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2935 hue+=0.5*(0.01*percent_hue-1.0);
2940 chroma*=0.01*percent_chroma;
2941 luma*=0.01*percent_luma;
2942 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2945 static inline void ModulateHCLp(const double percent_hue,
2946 const double percent_chroma,const double percent_luma,double *red,
2947 double *green,double *blue)
2955 Increase or decrease color luma, chroma, or hue.
2957 ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
2958 hue+=0.5*(0.01*percent_hue-1.0);
2963 chroma*=0.01*percent_chroma;
2964 luma*=0.01*percent_luma;
2965 ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
2968 static inline void ModulateHSB(const double percent_hue,
2969 const double percent_saturation,const double percent_brightness,double *red,
2970 double *green,double *blue)
2978 Increase or decrease color brightness, saturation, or hue.
2980 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2981 hue+=0.5*(0.01*percent_hue-1.0);
2986 saturation*=0.01*percent_saturation;
2987 brightness*=0.01*percent_brightness;
2988 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2991 static inline void ModulateHSI(const double percent_hue,
2992 const double percent_saturation,const double percent_intensity,double *red,
2993 double *green,double *blue)
3001 Increase or decrease color intensity, saturation, or hue.
3003 ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3004 hue+=0.5*(0.01*percent_hue-1.0);
3009 saturation*=0.01*percent_saturation;
3010 intensity*=0.01*percent_intensity;
3011 ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3014 static inline void ModulateHSL(const double percent_hue,
3015 const double percent_saturation,const double percent_lightness,double *red,
3016 double *green,double *blue)
3024 Increase or decrease color lightness, saturation, or hue.
3026 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3027 hue+=0.5*(0.01*percent_hue-1.0);
3032 saturation*=0.01*percent_saturation;
3033 lightness*=0.01*percent_lightness;
3034 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3037 static inline void ModulateHSV(const double percent_hue,
3038 const double percent_saturation,const double percent_value,double *red,
3039 double *green,double *blue)
3047 Increase or decrease color value, saturation, or hue.
3049 ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3050 hue+=0.5*(0.01*percent_hue-1.0);
3055 saturation*=0.01*percent_saturation;
3056 value*=0.01*percent_value;
3057 ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3060 static inline void ModulateHWB(const double percent_hue,
3061 const double percent_whiteness,const double percent_blackness,double *red,
3062 double *green,double *blue)
3070 Increase or decrease color blackness, whiteness, or hue.
3072 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3073 hue+=0.5*(0.01*percent_hue-1.0);
3078 blackness*=0.01*percent_blackness;
3079 whiteness*=0.01*percent_whiteness;
3080 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3083 static inline void ModulateLCHab(const double percent_luma,
3084 const double percent_chroma,const double percent_hue,double *red,
3085 double *green,double *blue)
3093 Increase or decrease color luma, chroma, or hue.
3095 ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
3096 luma*=0.01*percent_luma;
3097 chroma*=0.01*percent_chroma;
3098 hue+=0.5*(0.01*percent_hue-1.0);
3103 ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
3106 static inline void ModulateLCHuv(const double percent_luma,
3107 const double percent_chroma,const double percent_hue,double *red,
3108 double *green,double *blue)
3116 Increase or decrease color luma, chroma, or hue.
3118 ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
3119 luma*=0.01*percent_luma;
3120 chroma*=0.01*percent_chroma;
3121 hue+=0.5*(0.01*percent_hue-1.0);
3126 ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
3129 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
3130 ExceptionInfo *exception)
3132 #define ModulateImageTag "Modulate/Image"
3167 Initialize modulate table.
3169 assert(image != (Image *) NULL);
3170 assert(image->signature == MagickSignature);
3171 if (image->debug != MagickFalse)
3172 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3173 if (modulate == (char *) NULL)
3174 return(MagickFalse);
3175 if( IfMagickFalse(IssRGBCompatibleColorspace(image->colorspace)) )
3176 (void) SetImageColorspace(image,sRGBColorspace,exception);
3177 flags=ParseGeometry(modulate,&geometry_info);
3178 percent_brightness=geometry_info.rho;
3179 percent_saturation=geometry_info.sigma;
3180 if ((flags & SigmaValue) == 0)
3181 percent_saturation=100.0;
3182 percent_hue=geometry_info.xi;
3183 if ((flags & XiValue) == 0)
3185 colorspace=UndefinedColorspace;
3186 artifact=GetImageArtifact(image,"modulate:colorspace");
3187 if (artifact != (const char *) NULL)
3188 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3189 MagickFalse,artifact);
3190 if (image->storage_class == PseudoClass)
3191 for (i=0; i < (ssize_t) image->colors; i++)
3199 Modulate image colormap.
3201 red=(double) image->colormap[i].red;
3202 green=(double) image->colormap[i].green;
3203 blue=(double) image->colormap[i].blue;
3208 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3212 case HCLpColorspace:
3214 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3220 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3226 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3233 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3239 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3245 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3250 case LCHabColorspace:
3252 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3256 case LCHuvColorspace:
3258 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3263 image->colormap[i].red=red;
3264 image->colormap[i].green=green;
3265 image->colormap[i].blue=blue;
3272 image_view=AcquireAuthenticCacheView(image,exception);
3273 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3274 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3275 magick_threads(image,image,image->rows,1)
3277 for (y=0; y < (ssize_t) image->rows; y++)
3285 if( IfMagickFalse(status) )
3287 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3288 if (q == (Quantum *) NULL)
3293 for (x=0; x < (ssize_t) image->columns; x++)
3300 red=(double) GetPixelRed(image,q);
3301 green=(double) GetPixelGreen(image,q);
3302 blue=(double) GetPixelBlue(image,q);
3307 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3311 case HCLpColorspace:
3313 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3319 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3326 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3332 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3338 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3342 case LCHabColorspace:
3344 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3349 case LCHuvColorspace:
3351 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3356 SetPixelRed(image,ClampToQuantum(red),q);
3357 SetPixelGreen(image,ClampToQuantum(green),q);
3358 SetPixelBlue(image,ClampToQuantum(blue),q);
3359 q+=GetPixelChannels(image);
3361 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3363 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3368 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3369 #pragma omp critical (MagickCore_ModulateImage)
3371 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3372 if( IfMagickFalse(proceed) )
3376 image_view=DestroyCacheView(image_view);
3381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3385 % N e g a t e I m a g e %
3389 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3391 % NegateImage() negates the colors in the reference image. The grayscale
3392 % option means that only grayscale values within the image are negated.
3394 % The format of the NegateImage method is:
3396 % MagickBooleanType NegateImage(Image *image,
3397 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3399 % A description of each parameter follows:
3401 % o image: the image.
3403 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3405 % o exception: return any errors or warnings in this structure.
3408 MagickExport MagickBooleanType NegateImage(Image *image,
3409 const MagickBooleanType grayscale,ExceptionInfo *exception)
3411 #define NegateImageTag "Negate/Image"
3428 assert(image != (Image *) NULL);
3429 assert(image->signature == MagickSignature);
3430 if (image->debug != MagickFalse)
3431 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3432 if (image->storage_class == PseudoClass)
3433 for (i=0; i < (ssize_t) image->colors; i++)
3438 if( IfMagickTrue(grayscale) )
3439 if ((image->colormap[i].red != image->colormap[i].green) ||
3440 (image->colormap[i].green != image->colormap[i].blue))
3442 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3443 image->colormap[i].red=QuantumRange-image->colormap[i].red;
3444 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3445 image->colormap[i].green=QuantumRange-image->colormap[i].green;
3446 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3447 image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3454 image_view=AcquireAuthenticCacheView(image,exception);
3455 if( IfMagickTrue(grayscale) )
3457 for (y=0; y < (ssize_t) image->rows; y++)
3468 if( IfMagickFalse(status) )
3470 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3472 if (q == (Quantum *) NULL)
3477 for (x=0; x < (ssize_t) image->columns; x++)
3482 if ((GetPixelReadMask(image,q) == 0) ||
3483 IfMagickTrue(IsPixelGray(image,q)))
3485 q+=GetPixelChannels(image);
3488 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3490 PixelChannel channel=GetPixelChannelChannel(image,i);
3491 PixelTrait traits=GetPixelChannelTraits(image,channel);
3492 if ((traits & UpdatePixelTrait) == 0)
3494 q[i]=QuantumRange-q[i];
3496 q+=GetPixelChannels(image);
3498 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3499 if( IfMagickFalse(sync) )
3501 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3506 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3507 #pragma omp critical (MagickCore_NegateImage)
3509 proceed=SetImageProgress(image,NegateImageTag,progress++,
3511 if( IfMagickFalse(proceed) )
3515 image_view=DestroyCacheView(image_view);
3521 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3522 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3523 magick_threads(image,image,image->rows,1)
3525 for (y=0; y < (ssize_t) image->rows; y++)
3533 if( IfMagickFalse(status) )
3535 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3536 if (q == (Quantum *) NULL)
3541 for (x=0; x < (ssize_t) image->columns; x++)
3546 if (GetPixelReadMask(image,q) == 0)
3548 q+=GetPixelChannels(image);
3551 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3553 PixelChannel channel=GetPixelChannelChannel(image,i);
3554 PixelTrait traits=GetPixelChannelTraits(image,channel);
3555 if ((traits & UpdatePixelTrait) == 0)
3557 q[i]=QuantumRange-q[i];
3559 q+=GetPixelChannels(image);
3561 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3563 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3568 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3569 #pragma omp critical (MagickCore_NegateImage)
3571 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3572 if( IfMagickFalse(proceed) )
3576 image_view=DestroyCacheView(image_view);
3581 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3585 % N o r m a l i z e I m a g e %
3589 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3591 % The NormalizeImage() method enhances the contrast of a color image by
3592 % mapping the darkest 2 percent of all pixel to black and the brightest
3593 % 1 percent to white.
3595 % The format of the NormalizeImage method is:
3597 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3599 % A description of each parameter follows:
3601 % o image: the image.
3603 % o exception: return any errors or warnings in this structure.
3606 MagickExport MagickBooleanType NormalizeImage(Image *image,
3607 ExceptionInfo *exception)
3613 black_point=(double) image->columns*image->rows*0.0015;
3614 white_point=(double) image->columns*image->rows*0.9995;
3615 return(ContrastStretchImage(image,black_point,white_point,exception));
3619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3623 % S i g m o i d a l C o n t r a s t I m a g e %
3627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3629 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3630 % sigmoidal contrast algorithm. Increase the contrast of the image using a
3631 % sigmoidal transfer function without saturating highlights or shadows.
3632 % Contrast indicates how much to increase the contrast (0 is none; 3 is
3633 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
3634 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
3635 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
3638 % The format of the SigmoidalContrastImage method is:
3640 % MagickBooleanType SigmoidalContrastImage(Image *image,
3641 % const MagickBooleanType sharpen,const char *levels,
3642 % ExceptionInfo *exception)
3644 % A description of each parameter follows:
3646 % o image: the image.
3648 % o sharpen: Increase or decrease image contrast.
3650 % o contrast: strength of the contrast, the larger the number the more
3651 % 'threshold-like' it becomes.
3653 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3655 % o exception: return any errors or warnings in this structure.
3660 ImageMagick 6 has a version of this function which uses LUTs.
3664 Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
3667 The first version, based on the hyperbolic tangent tanh, when combined with
3668 the scaling step, is an exact arithmetic clone of the the sigmoid function
3669 based on the logistic curve. The equivalence is based on the identity
3671 1/(1+exp(-t)) = (1+tanh(t/2))/2
3673 (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3674 scaled sigmoidal derivation is invariant under affine transformations of
3677 The tanh version is almost certainly more accurate and cheaper. The 0.5
3678 factor in the argument is to clone the legacy ImageMagick behavior. The
3679 reason for making the define depend on atanh even though it only uses tanh
3680 has to do with the construction of the inverse of the scaled sigmoidal.
3682 #if defined(MAGICKCORE_HAVE_ATANH)
3683 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3685 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3688 Scaled sigmoidal function:
3690 ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3691 ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3693 See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
3694 http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
3695 of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
3696 zero. This is fixed below by exiting immediately when contrast is small,
3697 leaving the image (or colormap) unmodified. This appears to be safe because
3698 the series expansion of the logistic sigmoidal function around x=b is
3702 so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3704 #define ScaledSigmoidal(a,b,x) ( \
3705 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3706 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3708 Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
3709 may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
3710 sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
3711 when creating a LUT from in gamut values, hence the branching. In
3712 addition, HDRI may have out of gamut values.
3713 InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
3714 It is only a right inverse. This is unavoidable.
3716 static inline double InverseScaledSigmoidal(const double a,const double b,
3719 const double sig0=Sigmoidal(a,b,0.0);
3720 const double sig1=Sigmoidal(a,b,1.0);
3721 const double argument=(sig1-sig0)*x+sig0;
3722 const double clamped=
3724 #if defined(MAGICKCORE_HAVE_ATANH)
3725 argument < -1+MagickEpsilon
3729 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3731 return(b+(2.0/a)*atanh(clamped));
3733 argument < MagickEpsilon
3737 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3739 return(b-log(1.0/clamped-1.0)/a);
3743 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3744 const MagickBooleanType sharpen,const double contrast,const double midpoint,
3745 ExceptionInfo *exception)
3747 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
3748 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3749 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3750 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3751 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3768 assert(image != (Image *) NULL);
3769 assert(image->signature == MagickSignature);
3770 if (image->debug != MagickFalse)
3771 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3773 Side effect: may clamp values unless contrast<MagickEpsilon, in which
3774 case nothing is done.
3776 if (contrast < MagickEpsilon)
3779 Sigmoidal-contrast enhance colormap.
3781 if (image->storage_class == PseudoClass)
3786 if( IfMagickTrue(sharpen) )
3787 for (i=0; i < (ssize_t) image->colors; i++)
3789 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3790 image->colormap[i].red=(MagickRealType) ScaledSig(
3791 image->colormap[i].red);
3792 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3793 image->colormap[i].green=(MagickRealType) ScaledSig(
3794 image->colormap[i].green);
3795 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3796 image->colormap[i].blue=(MagickRealType) ScaledSig(
3797 image->colormap[i].blue);
3798 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3799 image->colormap[i].alpha=(MagickRealType) ScaledSig(
3800 image->colormap[i].alpha);
3803 for (i=0; i < (ssize_t) image->colors; i++)
3805 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3806 image->colormap[i].red=(MagickRealType) InverseScaledSig(
3807 image->colormap[i].red);
3808 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3809 image->colormap[i].green=(MagickRealType) InverseScaledSig(
3810 image->colormap[i].green);
3811 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3812 image->colormap[i].blue=(MagickRealType) InverseScaledSig(
3813 image->colormap[i].blue);
3814 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3815 image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
3816 image->colormap[i].alpha);
3820 Sigmoidal-contrast enhance image.
3824 image_view=AcquireAuthenticCacheView(image,exception);
3825 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3826 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3827 magick_threads(image,image,image->rows,1)
3829 for (y=0; y < (ssize_t) image->rows; y++)
3837 if( IfMagickFalse(status) )
3839 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3840 if (q == (Quantum *) NULL)
3845 for (x=0; x < (ssize_t) image->columns; x++)
3850 if (GetPixelReadMask(image,q) == 0)
3852 q+=GetPixelChannels(image);
3855 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3857 PixelChannel channel=GetPixelChannelChannel(image,i);
3858 PixelTrait traits=GetPixelChannelTraits(image,channel);
3859 if ((traits & UpdatePixelTrait) == 0)
3861 if( IfMagickTrue(sharpen) )
3862 q[i]=ScaledSig(q[i]);
3864 q[i]=InverseScaledSig(q[i]);
3866 q+=GetPixelChannels(image);
3868 if( IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3870 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3875 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3876 #pragma omp critical (MagickCore_SigmoidalContrastImage)
3878 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3880 if( IfMagickFalse(proceed) )
3884 image_view=DestroyCacheView(image_view);