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-2019 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 % https://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/accelerate-private.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/cache.h"
48 #include "MagickCore/cache-private.h"
49 #include "MagickCore/cache-view.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colorspace.h"
54 #include "MagickCore/colorspace-private.h"
55 #include "MagickCore/composite-private.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/fx.h"
60 #include "MagickCore/gem.h"
61 #include "MagickCore/gem-private.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/histogram.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/monitor.h"
68 #include "MagickCore/monitor-private.h"
69 #include "MagickCore/option.h"
70 #include "MagickCore/pixel.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/quantum.h"
73 #include "MagickCore/quantum-private.h"
74 #include "MagickCore/resample.h"
75 #include "MagickCore/resample-private.h"
76 #include "MagickCore/resource_.h"
77 #include "MagickCore/statistic.h"
78 #include "MagickCore/string_.h"
79 #include "MagickCore/string-private.h"
80 #include "MagickCore/thread-private.h"
81 #include "MagickCore/threshold.h"
82 #include "MagickCore/token.h"
83 #include "MagickCore/xml-tree.h"
84 #include "MagickCore/xml-tree-private.h"
87 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 % A u t o G a m m a I m a g e %
95 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 % AutoGammaImage() extract the 'mean' from the image and adjust the image
98 % to try make set its gamma appropriatally.
100 % The format of the AutoGammaImage method is:
102 % MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception)
104 % A description of each parameter follows:
106 % o image: The image to auto-level
108 % o exception: return any errors or warnings in this structure.
111 MagickExport MagickBooleanType AutoGammaImage(Image *image,
112 ExceptionInfo *exception)
127 if (image->channel_mask == DefaultChannels)
130 Apply gamma correction equally across all given channels.
132 (void) GetImageMean(image,&mean,&sans,exception);
133 gamma=log(mean*QuantumScale)/log_mean;
134 return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception));
137 Auto-gamma each channel separately.
140 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
145 PixelChannel channel = GetPixelChannelChannel(image,i);
146 PixelTrait traits = GetPixelChannelTraits(image,channel);
147 if ((traits & UpdatePixelTrait) == 0)
149 channel_mask=SetImageChannelMask(image,(ChannelType) (1UL << i));
150 status=GetImageMean(image,&mean,&sans,exception);
151 gamma=log(mean*QuantumScale)/log_mean;
152 status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception);
153 (void) SetImageChannelMask(image,channel_mask);
154 if (status == MagickFalse)
157 return(status != 0 ? MagickTrue : MagickFalse);
161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
165 % A u t o L e v e l I m a g e %
169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171 % AutoLevelImage() adjusts the levels of a particular image channel by
172 % scaling the minimum and maximum values to the full quantum range.
174 % The format of the LevelImage method is:
176 % MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception)
178 % A description of each parameter follows:
180 % o image: The image to auto-level
182 % o exception: return any errors or warnings in this structure.
185 MagickExport MagickBooleanType AutoLevelImage(Image *image,
186 ExceptionInfo *exception)
188 return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196 % B r i g h t n e s s C o n t r a s t I m a g e %
200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
202 % BrightnessContrastImage() changes the brightness and/or contrast of an
203 % image. It converts the brightness and contrast parameters into slope and
204 % intercept and calls a polynomical function to apply to the image.
206 % The format of the BrightnessContrastImage method is:
208 % MagickBooleanType BrightnessContrastImage(Image *image,
209 % const double brightness,const double contrast,ExceptionInfo *exception)
211 % A description of each parameter follows:
213 % o image: the image.
215 % o brightness: the brightness percent (-100 .. 100).
217 % o contrast: the contrast percent (-100 .. 100).
219 % o exception: return any errors or warnings in this structure.
222 MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
223 const double brightness,const double contrast,ExceptionInfo *exception)
225 #define BrightnessContastImageTag "BrightnessContast/Image"
237 Compute slope and intercept.
239 assert(image != (Image *) NULL);
240 assert(image->signature == MagickCoreSignature);
241 if (image->debug != MagickFalse)
242 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
244 slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
247 intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
248 coefficients[0]=slope;
249 coefficients[1]=intercept;
250 status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259 % C L A H E I m a g e %
263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265 % CLAHEImage() is a variant of adaptive histogram equalization in which the
266 % contrast amplification is limited, so as to reduce this problem of noise
269 % Adapted from implementation by Karel Zuiderveld, karel@cv.ruu.nl in
270 % "Graphics Gems IV", Academic Press, 1994.
272 % The format of the CLAHEImage method is:
274 % MagickBooleanType CLAHEImage(Image *image,const size_t width,
275 % const size_t height,const size_t number_bins,const double clip_limit,
276 % ExceptionInfo *exception)
278 % A description of each parameter follows:
280 % o image: the image.
282 % o width: the width of the tile divisions to use in horizontal direction.
284 % o height: the height of the tile divisions to use in vertical direction.
286 % o number_bins: number of bins for histogram ("dynamic range").
288 % o clip_limit: contrast limit for localised changes in contrast. A limit
289 % less than 1 results in standard non-contrast limited AHE.
291 % o exception: return any errors or warnings in this structure.
295 typedef struct _RangeInfo
302 static void ClipCLAHEHistogram(const double clip_limit,const size_t number_bins,
305 #define NumberCLAHEGrays (65536)
319 Compute total number of excess pixels.
322 for (i=0; i < (ssize_t) number_bins; i++)
324 excess=(ssize_t) histogram[i]-(ssize_t) clip_limit;
326 cumulative_excess+=excess;
329 Clip histogram and redistribute excess pixels across all bins.
331 step=cumulative_excess/number_bins;
332 excess=(ssize_t) (clip_limit-step);
333 for (i=0; i < (ssize_t) number_bins; i++)
335 if ((double) histogram[i] > clip_limit)
336 histogram[i]=(size_t) clip_limit;
338 if ((ssize_t) histogram[i] > excess)
340 cumulative_excess-=histogram[i]-excess;
341 histogram[i]=(size_t) clip_limit;
345 cumulative_excess-=step;
350 Redistribute remaining excess.
360 previous_excess=cumulative_excess;
362 q=histogram+number_bins;
363 while ((cumulative_excess != 0) && (p < q))
365 step=number_bins/cumulative_excess;
368 for (p=histogram; (p < q) && (cumulative_excess != 0); p+=step)
369 if ((double) *p < clip_limit)
376 } while ((cumulative_excess != 0) && (cumulative_excess < previous_excess));
379 static void GenerateCLAHEHistogram(const RectangleInfo *clahe_info,
380 const RectangleInfo *tile_info,const size_t number_bins,
381 const unsigned short *lut,const unsigned short *pixels,size_t *histogram)
383 register const unsigned short
390 Classify the pixels into a gray histogram.
392 for (i=0; i < (ssize_t) number_bins; i++)
395 for (i=0; i < (ssize_t) tile_info->height; i++)
400 q=p+tile_info->width;
402 histogram[lut[*p++]]++;
403 q+=clahe_info->width;
404 p=q-tile_info->width;
408 static void InterpolateCLAHE(const RectangleInfo *clahe_info,const size_t *Q12,
409 const size_t *Q22,const size_t *Q11,const size_t *Q21,
410 const RectangleInfo *tile,const unsigned short *lut,unsigned short *pixels)
419 Bilinear interpolate four tiles to eliminate boundary artifacts.
421 for (y=(ssize_t) tile->height; y > 0; y--)
426 for (x=(ssize_t) tile->width; x > 0; x--)
428 intensity=lut[*pixels];
429 *pixels++=(unsigned short ) (PerceptibleReciprocal((double) tile->width*
430 tile->height)*(y*(x*Q12[intensity]+(tile->width-x)*Q22[intensity])+
431 (tile->height-y)*(x*Q11[intensity]+(tile->width-x)*Q21[intensity])));
433 pixels+=(clahe_info->width-tile->width);
437 static void GenerateCLAHELut(const RangeInfo *range_info,
438 const size_t number_bins,unsigned short *lut)
447 Scale input image [intensity min,max] to [0,number_bins-1].
449 delta=(unsigned short) ((range_info->max-range_info->min)/number_bins+1);
450 for (i=(ssize_t) range_info->min; i <= (ssize_t) range_info->max; i++)
451 lut[i]=(unsigned short) ((i-range_info->min)/delta);
454 static void MapCLAHEHistogram(const RangeInfo *range_info,
455 const size_t number_bins,const size_t number_pixels,size_t *histogram)
465 Rescale histogram to range [min-intensity .. max-intensity].
467 scale=(double) (range_info->max-range_info->min)/number_pixels;
469 for (i=0; i < (ssize_t) number_bins; i++)
472 histogram[i]=(size_t) (range_info->min+scale*sum);
473 if (histogram[i] > range_info->max)
474 histogram[i]=range_info->max;
478 static MagickBooleanType CLAHE(const RectangleInfo *clahe_info,
479 const RectangleInfo *tile_info,const RangeInfo *range_info,
480 const size_t number_bins,const double clip_limit,unsigned short *pixels)
485 register unsigned short
496 lut[NumberCLAHEGrays];
499 Constrast limited adapted histogram equalization.
501 if (clip_limit == 1.0)
503 tile_cache=AcquireVirtualMemory((size_t) clahe_info->x*clahe_info->y,
504 number_bins*sizeof(*tiles));
505 if (tile_cache == (MemoryInfo *) NULL)
507 tiles=(size_t *) GetVirtualMemoryBlob(tile_cache);
508 limit=(size_t) (clip_limit*(tile_info->width*tile_info->height)/number_bins);
512 Generate greylevel mappings for each tile.
514 GenerateCLAHELut(range_info,number_bins,lut);
516 for (y=0; y < (ssize_t) clahe_info->y; y++)
521 for (x=0; x < (ssize_t) clahe_info->x; x++)
526 histogram=tiles+(number_bins*(y*clahe_info->x+x));
527 GenerateCLAHEHistogram(clahe_info,tile_info,number_bins,lut,p,histogram);
528 ClipCLAHEHistogram((double) limit,number_bins,histogram);
529 MapCLAHEHistogram(range_info,number_bins,tile_info->width*
530 tile_info->height,histogram);
533 p+=clahe_info->width*(tile_info->height-1);
536 Interpolate greylevel mappings to get CLAHE image.
539 for (y=0; y <= (ssize_t) clahe_info->y; y++)
550 tile.height=tile_info->height;
558 tile.height=tile_info->height >> 1;
563 if (y == (ssize_t) clahe_info->y)
568 tile.height=(tile_info->height+1) >> 1;
569 tile.y=clahe_info->y-1;
572 for (x=0; x <= (ssize_t) clahe_info->x; x++)
574 tile.width=tile_info->width;
582 tile.width=tile_info->width >> 1;
587 if (x == (ssize_t) clahe_info->x)
592 tile.width=(tile_info->width+1) >> 1;
593 tile.x=clahe_info->x-1;
596 InterpolateCLAHE(clahe_info,
597 tiles+(number_bins*(tile.y*clahe_info->x+tile.x)), /* Q12 */
598 tiles+(number_bins*(tile.y*clahe_info->x+offset.x)), /* Q22 */
599 tiles+(number_bins*(offset.y*clahe_info->x+tile.x)), /* Q11 */
600 tiles+(number_bins*(offset.y*clahe_info->x+offset.x)), /* Q21 */
604 p+=clahe_info->width*(tile.height-1);
606 tile_cache=RelinquishVirtualMemory(tile_cache);
610 MagickExport MagickBooleanType CLAHEImage(Image *image,const size_t width,
611 const size_t height,const size_t number_bins,const double clip_limit,
612 ExceptionInfo *exception)
614 #define CLAHEImageTag "CLAHE/Image"
648 Configure CLAHE parameters.
650 assert(image != (Image *) NULL);
651 assert(image->signature == MagickCoreSignature);
652 if (image->debug != MagickFalse)
653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
655 range_info.max=NumberCLAHEGrays-1;
656 tile_info.width=width;
657 if (tile_info.width == 0)
658 tile_info.width=image->columns >> 3;
659 tile_info.height=height;
660 if (tile_info.height == 0)
661 tile_info.height=image->rows >> 3;
663 if ((image->columns % tile_info.width) != 0)
664 tile_info.x=(ssize_t) tile_info.width-(image->columns % tile_info.width);
666 if ((image->rows % tile_info.height) != 0)
667 tile_info.y=(ssize_t) tile_info.height-(image->rows % tile_info.height);
668 clahe_info.width=image->columns+tile_info.x;
669 clahe_info.height=image->rows+tile_info.y;
670 clahe_info.x=(ssize_t) clahe_info.width/tile_info.width;
671 clahe_info.y=(ssize_t) clahe_info.height/tile_info.height;
672 pixel_cache=AcquireVirtualMemory(clahe_info.width,clahe_info.height*
674 if (pixel_cache == (MemoryInfo *) NULL)
675 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
677 pixels=(unsigned short *) GetVirtualMemoryBlob(pixel_cache);
678 colorspace=image->colorspace;
679 if (TransformImageColorspace(image,LabColorspace,exception) == MagickFalse)
681 pixel_cache=RelinquishVirtualMemory(pixel_cache);
685 Initialize CLAHE pixels.
687 image_view=AcquireVirtualCacheView(image,exception);
691 for (y=0; y < (ssize_t) clahe_info.height; y++)
693 register const Quantum
699 if (status == MagickFalse)
701 p=GetCacheViewVirtualPixels(image_view,-(tile_info.x >> 1),y-
702 (tile_info.y >> 1),clahe_info.width,1,exception);
703 if (p == (const Quantum *) NULL)
708 for (x=0; x < (ssize_t) clahe_info.width; x++)
710 pixels[n++]=ScaleQuantumToShort(p[0]);
711 p+=GetPixelChannels(image);
713 if (image->progress_monitor != (MagickProgressMonitor) NULL)
718 #if defined(MAGICKCORE_OPENMP_SUPPORT)
722 proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
723 GetPixelChannels(image));
724 if (proceed == MagickFalse)
728 image_view=DestroyCacheView(image_view);
729 status=CLAHE(&clahe_info,&tile_info,&range_info,number_bins == 0 ?
730 (size_t) 128 : MagickMin(number_bins,256),clip_limit,pixels);
731 if (status == MagickFalse)
732 (void) ThrowMagickException(exception,GetMagickModule(),
733 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
735 Push CLAHE pixels to CLAHE image.
737 image_view=AcquireAuthenticCacheView(image,exception);
738 n=clahe_info.width*(tile_info.y >> 1);
739 for (y=0; y < (ssize_t) image->rows; y++)
747 if (status == MagickFalse)
749 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
750 if (q == (Quantum *) NULL)
756 for (x=0; x < (ssize_t) image->columns; x++)
758 q[0]=ScaleShortToQuantum(pixels[n++]);
759 q+=GetPixelChannels(image);
761 n+=(clahe_info.width-image->columns-(tile_info.x >> 1));
762 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
764 if (image->progress_monitor != (MagickProgressMonitor) NULL)
769 #if defined(MAGICKCORE_OPENMP_SUPPORT)
773 proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
774 GetPixelChannels(image));
775 if (proceed == MagickFalse)
779 image_view=DestroyCacheView(image_view);
780 pixel_cache=RelinquishVirtualMemory(pixel_cache);
781 if (TransformImageColorspace(image,colorspace,exception) == MagickFalse)
787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791 % C l u t I m a g e %
795 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
797 % ClutImage() replaces each color value in the given image, by using it as an
798 % index to lookup a replacement color value in a Color Look UP Table in the
799 % form of an image. The values are extracted along a diagonal of the CLUT
800 % image so either a horizontal or vertial gradient image can be used.
802 % Typically this is used to either re-color a gray-scale image according to a
803 % color gradient in the CLUT image, or to perform a freeform histogram
804 % (level) adjustment according to the (typically gray-scale) gradient in the
807 % When the 'channel' mask includes the matte/alpha transparency channel but
808 % one image has no such channel it is assumed that that image is a simple
809 % gray-scale image that will effect the alpha channel values, either for
810 % gray-scale coloring (with transparent or semi-transparent colors), or
811 % a histogram adjustment of existing alpha channel values. If both images
812 % have matte channels, direct and normal indexing is applied, which is rarely
815 % The format of the ClutImage method is:
817 % MagickBooleanType ClutImage(Image *image,Image *clut_image,
818 % const PixelInterpolateMethod method,ExceptionInfo *exception)
820 % A description of each parameter follows:
822 % o image: the image, which is replaced by indexed CLUT values
824 % o clut_image: the color lookup table image for replacement color values.
826 % o method: the pixel interpolation method.
828 % o exception: return any errors or warnings in this structure.
831 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image,
832 const PixelInterpolateMethod method,ExceptionInfo *exception)
834 #define ClutImageTag "Clut/Image"
855 assert(image != (Image *) NULL);
856 assert(image->signature == MagickCoreSignature);
857 if (image->debug != MagickFalse)
858 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
859 assert(clut_image != (Image *) NULL);
860 assert(clut_image->signature == MagickCoreSignature);
861 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
863 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
864 (IsGrayColorspace(clut_image->colorspace) == MagickFalse))
865 (void) SetImageColorspace(image,sRGBColorspace,exception);
866 clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*clut_map));
867 if (clut_map == (PixelInfo *) NULL)
868 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
875 adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
876 clut_view=AcquireVirtualCacheView(clut_image,exception);
877 for (i=0; i <= (ssize_t) MaxMap; i++)
879 GetPixelInfo(clut_image,clut_map+i);
880 status=InterpolatePixelInfo(clut_image,clut_view,method,
881 (double) i*(clut_image->columns-adjust)/MaxMap,(double) i*
882 (clut_image->rows-adjust)/MaxMap,clut_map+i,exception);
883 if (status == MagickFalse)
886 clut_view=DestroyCacheView(clut_view);
887 image_view=AcquireAuthenticCacheView(image,exception);
888 #if defined(MAGICKCORE_OPENMP_SUPPORT)
889 #pragma omp parallel for schedule(static) shared(progress,status) \
890 magick_number_threads(image,image,image->rows,1)
892 for (y=0; y < (ssize_t) image->rows; y++)
903 if (status == MagickFalse)
905 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
906 if (q == (Quantum *) NULL)
911 GetPixelInfo(image,&pixel);
912 for (x=0; x < (ssize_t) image->columns; x++)
917 GetPixelInfoPixel(image,q,&pixel);
918 traits=GetPixelChannelTraits(image,RedPixelChannel);
919 if ((traits & UpdatePixelTrait) != 0)
920 pixel.red=clut_map[ScaleQuantumToMap(ClampToQuantum(
922 traits=GetPixelChannelTraits(image,GreenPixelChannel);
923 if ((traits & UpdatePixelTrait) != 0)
924 pixel.green=clut_map[ScaleQuantumToMap(ClampToQuantum(
925 pixel.green))].green;
926 traits=GetPixelChannelTraits(image,BluePixelChannel);
927 if ((traits & UpdatePixelTrait) != 0)
928 pixel.blue=clut_map[ScaleQuantumToMap(ClampToQuantum(
930 traits=GetPixelChannelTraits(image,BlackPixelChannel);
931 if ((traits & UpdatePixelTrait) != 0)
932 pixel.black=clut_map[ScaleQuantumToMap(ClampToQuantum(
933 pixel.black))].black;
934 traits=GetPixelChannelTraits(image,AlphaPixelChannel);
935 if ((traits & UpdatePixelTrait) != 0)
936 pixel.alpha=clut_map[ScaleQuantumToMap(ClampToQuantum(
937 pixel.alpha))].alpha;
938 SetPixelViaPixelInfo(image,&pixel,q);
939 q+=GetPixelChannels(image);
941 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
943 if (image->progress_monitor != (MagickProgressMonitor) NULL)
948 #if defined(MAGICKCORE_OPENMP_SUPPORT)
952 proceed=SetImageProgress(image,ClutImageTag,progress,image->rows);
953 if (proceed == MagickFalse)
957 image_view=DestroyCacheView(image_view);
958 clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
959 if ((clut_image->alpha_trait != UndefinedPixelTrait) &&
960 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
961 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
970 % C o l o r D e c i s i o n L i s t I m a g e %
974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
976 % ColorDecisionListImage() accepts a lightweight Color Correction Collection
977 % (CCC) file which solely contains one or more color corrections and applies
978 % the correction to the image. Here is a sample CCC file:
980 % <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
981 % <ColorCorrection id="cc03345">
983 % <Slope> 0.9 1.2 0.5 </Slope>
984 % <Offset> 0.4 -0.5 0.6 </Offset>
985 % <Power> 1.0 0.8 1.5 </Power>
988 % <Saturation> 0.85 </Saturation>
991 % </ColorCorrectionCollection>
993 % which includes the slop, offset, and power for each of the RGB channels
994 % as well as the saturation.
996 % The format of the ColorDecisionListImage method is:
998 % MagickBooleanType ColorDecisionListImage(Image *image,
999 % const char *color_correction_collection,ExceptionInfo *exception)
1001 % A description of each parameter follows:
1003 % o image: the image.
1005 % o color_correction_collection: the color correction collection in XML.
1007 % o exception: return any errors or warnings in this structure.
1010 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
1011 const char *color_correction_collection,ExceptionInfo *exception)
1013 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
1015 typedef struct _Correction
1023 typedef struct _ColorCorrection
1038 token[MagickPathExtent];
1069 Allocate and initialize cdl maps.
1071 assert(image != (Image *) NULL);
1072 assert(image->signature == MagickCoreSignature);
1073 if (image->debug != MagickFalse)
1074 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1075 if (color_correction_collection == (const char *) NULL)
1076 return(MagickFalse);
1077 ccc=NewXMLTree((const char *) color_correction_collection,exception);
1078 if (ccc == (XMLTreeInfo *) NULL)
1079 return(MagickFalse);
1080 cc=GetXMLTreeChild(ccc,"ColorCorrection");
1081 if (cc == (XMLTreeInfo *) NULL)
1083 ccc=DestroyXMLTree(ccc);
1084 return(MagickFalse);
1086 color_correction.red.slope=1.0;
1087 color_correction.red.offset=0.0;
1088 color_correction.red.power=1.0;
1089 color_correction.green.slope=1.0;
1090 color_correction.green.offset=0.0;
1091 color_correction.green.power=1.0;
1092 color_correction.blue.slope=1.0;
1093 color_correction.blue.offset=0.0;
1094 color_correction.blue.power=1.0;
1095 color_correction.saturation=0.0;
1096 sop=GetXMLTreeChild(cc,"SOPNode");
1097 if (sop != (XMLTreeInfo *) NULL)
1104 slope=GetXMLTreeChild(sop,"Slope");
1105 if (slope != (XMLTreeInfo *) NULL)
1107 content=GetXMLTreeContent(slope);
1108 p=(const char *) content;
1109 for (i=0; (*p != '\0') && (i < 3); i++)
1111 (void) GetNextToken(p,&p,MagickPathExtent,token);
1113 (void) GetNextToken(p,&p,MagickPathExtent,token);
1118 color_correction.red.slope=StringToDouble(token,(char **) NULL);
1123 color_correction.green.slope=StringToDouble(token,
1129 color_correction.blue.slope=StringToDouble(token,
1136 offset=GetXMLTreeChild(sop,"Offset");
1137 if (offset != (XMLTreeInfo *) NULL)
1139 content=GetXMLTreeContent(offset);
1140 p=(const char *) content;
1141 for (i=0; (*p != '\0') && (i < 3); i++)
1143 (void) GetNextToken(p,&p,MagickPathExtent,token);
1145 (void) GetNextToken(p,&p,MagickPathExtent,token);
1150 color_correction.red.offset=StringToDouble(token,
1156 color_correction.green.offset=StringToDouble(token,
1162 color_correction.blue.offset=StringToDouble(token,
1169 power=GetXMLTreeChild(sop,"Power");
1170 if (power != (XMLTreeInfo *) NULL)
1172 content=GetXMLTreeContent(power);
1173 p=(const char *) content;
1174 for (i=0; (*p != '\0') && (i < 3); i++)
1176 (void) GetNextToken(p,&p,MagickPathExtent,token);
1178 (void) GetNextToken(p,&p,MagickPathExtent,token);
1183 color_correction.red.power=StringToDouble(token,(char **) NULL);
1188 color_correction.green.power=StringToDouble(token,
1194 color_correction.blue.power=StringToDouble(token,
1202 sat=GetXMLTreeChild(cc,"SATNode");
1203 if (sat != (XMLTreeInfo *) NULL)
1208 saturation=GetXMLTreeChild(sat,"Saturation");
1209 if (saturation != (XMLTreeInfo *) NULL)
1211 content=GetXMLTreeContent(saturation);
1212 p=(const char *) content;
1213 (void) GetNextToken(p,&p,MagickPathExtent,token);
1214 color_correction.saturation=StringToDouble(token,(char **) NULL);
1217 ccc=DestroyXMLTree(ccc);
1218 if (image->debug != MagickFalse)
1220 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1221 " Color Correction Collection:");
1222 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1223 " color_correction.red.slope: %g",color_correction.red.slope);
1224 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1225 " color_correction.red.offset: %g",color_correction.red.offset);
1226 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1227 " color_correction.red.power: %g",color_correction.red.power);
1228 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1229 " color_correction.green.slope: %g",color_correction.green.slope);
1230 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1231 " color_correction.green.offset: %g",color_correction.green.offset);
1232 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1233 " color_correction.green.power: %g",color_correction.green.power);
1234 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1235 " color_correction.blue.slope: %g",color_correction.blue.slope);
1236 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1237 " color_correction.blue.offset: %g",color_correction.blue.offset);
1238 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1239 " color_correction.blue.power: %g",color_correction.blue.power);
1240 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1241 " color_correction.saturation: %g",color_correction.saturation);
1243 cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
1244 if (cdl_map == (PixelInfo *) NULL)
1245 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1247 for (i=0; i <= (ssize_t) MaxMap; i++)
1249 cdl_map[i].red=(double) ScaleMapToQuantum((double)
1250 (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
1251 color_correction.red.offset,color_correction.red.power))));
1252 cdl_map[i].green=(double) ScaleMapToQuantum((double)
1253 (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
1254 color_correction.green.offset,color_correction.green.power))));
1255 cdl_map[i].blue=(double) ScaleMapToQuantum((double)
1256 (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
1257 color_correction.blue.offset,color_correction.blue.power))));
1259 if (image->storage_class == PseudoClass)
1260 for (i=0; i < (ssize_t) image->colors; i++)
1263 Apply transfer function to colormap.
1268 luma=0.21267f*image->colormap[i].red+0.71526*image->colormap[i].green+
1269 0.07217f*image->colormap[i].blue;
1270 image->colormap[i].red=luma+color_correction.saturation*cdl_map[
1271 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
1272 image->colormap[i].green=luma+color_correction.saturation*cdl_map[
1273 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
1274 image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
1275 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
1278 Apply transfer function to image.
1282 image_view=AcquireAuthenticCacheView(image,exception);
1283 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1284 #pragma omp parallel for schedule(static) shared(progress,status) \
1285 magick_number_threads(image,image,image->rows,1)
1287 for (y=0; y < (ssize_t) image->rows; y++)
1298 if (status == MagickFalse)
1300 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1301 if (q == (Quantum *) NULL)
1306 for (x=0; x < (ssize_t) image->columns; x++)
1308 luma=0.21267f*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+
1309 0.07217f*GetPixelBlue(image,q);
1310 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
1311 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
1312 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
1313 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
1314 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
1315 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
1316 q+=GetPixelChannels(image);
1318 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1320 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1325 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1329 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
1330 progress,image->rows);
1331 if (proceed == MagickFalse)
1335 image_view=DestroyCacheView(image_view);
1336 cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
1341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345 % C o n t r a s t I m a g e %
1349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1351 % ContrastImage() enhances the intensity differences between the lighter and
1352 % darker elements of the image. Set sharpen to a MagickTrue to increase the
1353 % image contrast otherwise the contrast is reduced.
1355 % The format of the ContrastImage method is:
1357 % MagickBooleanType ContrastImage(Image *image,
1358 % const MagickBooleanType sharpen,ExceptionInfo *exception)
1360 % A description of each parameter follows:
1362 % o image: the image.
1364 % o sharpen: Increase or decrease image contrast.
1366 % o exception: return any errors or warnings in this structure.
1370 static void Contrast(const int sign,double *red,double *green,double *blue)
1378 Enhance contrast: dark color become darker, light color become lighter.
1380 assert(red != (double *) NULL);
1381 assert(green != (double *) NULL);
1382 assert(blue != (double *) NULL);
1386 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
1387 brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
1389 if (brightness > 1.0)
1392 if (brightness < 0.0)
1394 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
1397 MagickExport MagickBooleanType ContrastImage(Image *image,
1398 const MagickBooleanType sharpen,ExceptionInfo *exception)
1400 #define ContrastImageTag "Contrast/Image"
1420 assert(image != (Image *) NULL);
1421 assert(image->signature == MagickCoreSignature);
1422 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1423 if (AccelerateContrastImage(image,sharpen,exception) != MagickFalse)
1426 if (image->debug != MagickFalse)
1427 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1428 sign=sharpen != MagickFalse ? 1 : -1;
1429 if (image->storage_class == PseudoClass)
1432 Contrast enhance colormap.
1434 for (i=0; i < (ssize_t) image->colors; i++)
1441 red=(double) image->colormap[i].red;
1442 green=(double) image->colormap[i].green;
1443 blue=(double) image->colormap[i].blue;
1444 Contrast(sign,&red,&green,&blue);
1445 image->colormap[i].red=(MagickRealType) red;
1446 image->colormap[i].green=(MagickRealType) green;
1447 image->colormap[i].blue=(MagickRealType) blue;
1451 Contrast enhance image.
1455 image_view=AcquireAuthenticCacheView(image,exception);
1456 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1457 #pragma omp parallel for schedule(static) shared(progress,status) \
1458 magick_number_threads(image,image,image->rows,1)
1460 for (y=0; y < (ssize_t) image->rows; y++)
1473 if (status == MagickFalse)
1475 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1476 if (q == (Quantum *) NULL)
1481 for (x=0; x < (ssize_t) image->columns; x++)
1483 red=(double) GetPixelRed(image,q);
1484 green=(double) GetPixelGreen(image,q);
1485 blue=(double) GetPixelBlue(image,q);
1486 Contrast(sign,&red,&green,&blue);
1487 SetPixelRed(image,ClampToQuantum(red),q);
1488 SetPixelGreen(image,ClampToQuantum(green),q);
1489 SetPixelBlue(image,ClampToQuantum(blue),q);
1490 q+=GetPixelChannels(image);
1492 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1494 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1499 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1503 proceed=SetImageProgress(image,ContrastImageTag,progress,image->rows);
1504 if (proceed == MagickFalse)
1508 image_view=DestroyCacheView(image_view);
1513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1517 % C o n t r a s t S t r e t c h I m a g e %
1521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1523 % ContrastStretchImage() is a simple image enhancement technique that attempts
1524 % to improve the contrast in an image by 'stretching' the range of intensity
1525 % values it contains to span a desired range of values. It differs from the
1526 % more sophisticated histogram equalization in that it can only apply a
1527 % linear scaling function to the image pixel values. As a result the
1528 % 'enhancement' is less harsh.
1530 % The format of the ContrastStretchImage method is:
1532 % MagickBooleanType ContrastStretchImage(Image *image,
1533 % const char *levels,ExceptionInfo *exception)
1535 % A description of each parameter follows:
1537 % o image: the image.
1539 % o black_point: the black point.
1541 % o white_point: the white point.
1543 % o levels: Specify the levels where the black and white points have the
1544 % range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
1546 % o exception: return any errors or warnings in this structure.
1549 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
1550 const double black_point,const double white_point,ExceptionInfo *exception)
1552 #define MaxRange(color) ((double) ScaleQuantumToMap((Quantum) (color)))
1553 #define ContrastStretchImageTag "ContrastStretch/Image"
1577 Allocate histogram and stretch map.
1579 assert(image != (Image *) NULL);
1580 assert(image->signature == MagickCoreSignature);
1581 if (image->debug != MagickFalse)
1582 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1583 if (SetImageGray(image,exception) != MagickFalse)
1584 (void) SetImageColorspace(image,GRAYColorspace,exception);
1585 black=(double *) AcquireQuantumMemory(MaxPixelChannels,sizeof(*black));
1586 white=(double *) AcquireQuantumMemory(MaxPixelChannels,sizeof(*white));
1587 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1588 sizeof(*histogram));
1589 stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1590 sizeof(*stretch_map));
1591 if ((black == (double *) NULL) || (white == (double *) NULL) ||
1592 (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1594 if (stretch_map != (double *) NULL)
1595 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1596 if (histogram != (double *) NULL)
1597 histogram=(double *) RelinquishMagickMemory(histogram);
1598 if (white != (double *) NULL)
1599 white=(double *) RelinquishMagickMemory(white);
1600 if (black != (double *) NULL)
1601 black=(double *) RelinquishMagickMemory(black);
1602 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1609 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1610 sizeof(*histogram));
1611 image_view=AcquireVirtualCacheView(image,exception);
1612 for (y=0; y < (ssize_t) image->rows; y++)
1614 register const Quantum
1620 if (status == MagickFalse)
1622 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1623 if (p == (const Quantum *) NULL)
1628 for (x=0; x < (ssize_t) image->columns; x++)
1633 pixel=GetPixelIntensity(image,p);
1634 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1636 if (image->channel_mask != DefaultChannels)
1637 pixel=(double) p[i];
1638 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1639 ClampToQuantum(pixel))+i]++;
1641 p+=GetPixelChannels(image);
1644 image_view=DestroyCacheView(image_view);
1646 Find the histogram boundaries by locating the black/white levels.
1648 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1657 white[i]=MaxRange(QuantumRange);
1659 for (j=0; j <= (ssize_t) MaxMap; j++)
1661 intensity+=histogram[GetPixelChannels(image)*j+i];
1662 if (intensity > black_point)
1665 black[i]=(double) j;
1667 for (j=(ssize_t) MaxMap; j != 0; j--)
1669 intensity+=histogram[GetPixelChannels(image)*j+i];
1670 if (intensity > ((double) image->columns*image->rows-white_point))
1673 white[i]=(double) j;
1675 histogram=(double *) RelinquishMagickMemory(histogram);
1677 Stretch the histogram to create the stretched image mapping.
1679 (void) memset(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1680 sizeof(*stretch_map));
1681 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1686 for (j=0; j <= (ssize_t) MaxMap; j++)
1691 gamma=PerceptibleReciprocal(white[i]-black[i]);
1692 if (j < (ssize_t) black[i])
1693 stretch_map[GetPixelChannels(image)*j+i]=0.0;
1695 if (j > (ssize_t) white[i])
1696 stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange;
1698 if (black[i] != white[i])
1699 stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum(
1700 (double) (MaxMap*gamma*(j-black[i])));
1703 if (image->storage_class == PseudoClass)
1709 Stretch-contrast colormap.
1711 for (j=0; j < (ssize_t) image->colors; j++)
1713 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1715 i=GetPixelChannelOffset(image,RedPixelChannel);
1716 image->colormap[j].red=stretch_map[GetPixelChannels(image)*
1717 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+i];
1719 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1721 i=GetPixelChannelOffset(image,GreenPixelChannel);
1722 image->colormap[j].green=stretch_map[GetPixelChannels(image)*
1723 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+i];
1725 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1727 i=GetPixelChannelOffset(image,BluePixelChannel);
1728 image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
1729 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+i];
1731 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1733 i=GetPixelChannelOffset(image,AlphaPixelChannel);
1734 image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
1735 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+i];
1740 Stretch-contrast image.
1744 image_view=AcquireAuthenticCacheView(image,exception);
1745 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1746 #pragma omp parallel for schedule(static) shared(progress,status) \
1747 magick_number_threads(image,image,image->rows,1)
1749 for (y=0; y < (ssize_t) image->rows; y++)
1757 if (status == MagickFalse)
1759 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1760 if (q == (Quantum *) NULL)
1765 for (x=0; x < (ssize_t) image->columns; x++)
1770 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1772 PixelChannel channel = GetPixelChannelChannel(image,j);
1773 PixelTrait traits = GetPixelChannelTraits(image,channel);
1774 if ((traits & UpdatePixelTrait) == 0)
1776 if (black[j] == white[j])
1778 q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1779 ScaleQuantumToMap(q[j])+j]);
1781 q+=GetPixelChannels(image);
1783 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1785 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1790 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1794 proceed=SetImageProgress(image,ContrastStretchImageTag,progress,
1796 if (proceed == MagickFalse)
1800 image_view=DestroyCacheView(image_view);
1801 stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1802 white=(double *) RelinquishMagickMemory(white);
1803 black=(double *) RelinquishMagickMemory(black);
1808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1812 % E n h a n c e I m a g e %
1816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1818 % EnhanceImage() applies a digital filter that improves the quality of a
1821 % The format of the EnhanceImage method is:
1823 % Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1825 % A description of each parameter follows:
1827 % o image: the image.
1829 % o exception: return any errors or warnings in this structure.
1832 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1834 #define EnhanceImageTag "Enhance/Image"
1835 #define EnhancePixel(weight) \
1836 mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1837 distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1838 distance_squared=(4.0+mean)*distance*distance; \
1839 mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1840 distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1841 distance_squared+=(7.0-mean)*distance*distance; \
1842 mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1843 distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1844 distance_squared+=(5.0-mean)*distance*distance; \
1845 mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1846 distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1847 distance_squared+=(5.0-mean)*distance*distance; \
1848 mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1849 distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1850 distance_squared+=(5.0-mean)*distance*distance; \
1851 if (distance_squared < 0.069) \
1853 aggregate.red+=(weight)*GetPixelRed(image,r); \
1854 aggregate.green+=(weight)*GetPixelGreen(image,r); \
1855 aggregate.blue+=(weight)*GetPixelBlue(image,r); \
1856 aggregate.black+=(weight)*GetPixelBlack(image,r); \
1857 aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \
1858 total_weight+=(weight); \
1860 r+=GetPixelChannels(image);
1879 Initialize enhanced image attributes.
1881 assert(image != (const Image *) NULL);
1882 assert(image->signature == MagickCoreSignature);
1883 if (image->debug != MagickFalse)
1884 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1885 assert(exception != (ExceptionInfo *) NULL);
1886 assert(exception->signature == MagickCoreSignature);
1887 enhance_image=CloneImage(image,0,0,MagickTrue,
1889 if (enhance_image == (Image *) NULL)
1890 return((Image *) NULL);
1891 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1893 enhance_image=DestroyImage(enhance_image);
1894 return((Image *) NULL);
1901 image_view=AcquireVirtualCacheView(image,exception);
1902 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1903 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1904 #pragma omp parallel for schedule(static) shared(progress,status) \
1905 magick_number_threads(image,enhance_image,image->rows,1)
1907 for (y=0; y < (ssize_t) image->rows; y++)
1912 register const Quantum
1924 if (status == MagickFalse)
1926 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1927 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1929 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1934 center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1935 GetPixelInfo(image,&pixel);
1936 for (x=0; x < (ssize_t) image->columns; x++)
1947 register const Quantum
1950 GetPixelInfo(image,&aggregate);
1952 GetPixelInfoPixel(image,p+center,&pixel);
1954 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1955 EnhancePixel(8.0); EnhancePixel(5.0);
1956 r=p+GetPixelChannels(image)*(image->columns+4);
1957 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1958 EnhancePixel(20.0); EnhancePixel(8.0);
1959 r=p+2*GetPixelChannels(image)*(image->columns+4);
1960 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1961 EnhancePixel(40.0); EnhancePixel(10.0);
1962 r=p+3*GetPixelChannels(image)*(image->columns+4);
1963 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1964 EnhancePixel(20.0); EnhancePixel(8.0);
1965 r=p+4*GetPixelChannels(image)*(image->columns+4);
1966 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1967 EnhancePixel(8.0); EnhancePixel(5.0);
1968 if (total_weight > MagickEpsilon)
1970 pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1971 pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1972 pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1973 pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1974 pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
1976 SetPixelViaPixelInfo(enhance_image,&pixel,q);
1977 p+=GetPixelChannels(image);
1978 q+=GetPixelChannels(enhance_image);
1980 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1982 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1987 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1991 proceed=SetImageProgress(image,EnhanceImageTag,progress,image->rows);
1992 if (proceed == MagickFalse)
1996 enhance_view=DestroyCacheView(enhance_view);
1997 image_view=DestroyCacheView(image_view);
1998 if (status == MagickFalse)
1999 enhance_image=DestroyImage(enhance_image);
2000 return(enhance_image);
2004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2008 % E q u a l i z e I m a g e %
2012 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2014 % EqualizeImage() applies a histogram equalization to the image.
2016 % The format of the EqualizeImage method is:
2018 % MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
2020 % A description of each parameter follows:
2022 % o image: the image.
2024 % o exception: return any errors or warnings in this structure.
2027 MagickExport MagickBooleanType EqualizeImage(Image *image,
2028 ExceptionInfo *exception)
2030 #define EqualizeImageTag "Equalize/Image"
2036 black[CompositePixelChannel+1],
2040 white[CompositePixelChannel+1];
2055 Allocate and initialize histogram arrays.
2057 assert(image != (Image *) NULL);
2058 assert(image->signature == MagickCoreSignature);
2059 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2060 if (AccelerateEqualizeImage(image,exception) != MagickFalse)
2063 if (image->debug != MagickFalse)
2064 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2065 equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2066 sizeof(*equalize_map));
2067 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2068 sizeof(*histogram));
2069 map=(double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*sizeof(*map));
2070 if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
2071 (map == (double *) NULL))
2073 if (map != (double *) NULL)
2074 map=(double *) RelinquishMagickMemory(map);
2075 if (histogram != (double *) NULL)
2076 histogram=(double *) RelinquishMagickMemory(histogram);
2077 if (equalize_map != (double *) NULL)
2078 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
2079 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2086 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
2087 sizeof(*histogram));
2088 image_view=AcquireVirtualCacheView(image,exception);
2089 for (y=0; y < (ssize_t) image->rows; y++)
2091 register const Quantum
2097 if (status == MagickFalse)
2099 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2100 if (p == (const Quantum *) NULL)
2105 for (x=0; x < (ssize_t) image->columns; x++)
2107 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2112 intensity=(double) p[i];
2113 if ((image->channel_mask & SyncChannels) != 0)
2114 intensity=GetPixelIntensity(image,p);
2115 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2116 ClampToQuantum(intensity))+i]++;
2118 p+=GetPixelChannels(image);
2121 image_view=DestroyCacheView(image_view);
2123 Integrate the histogram to get the equalization map.
2125 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2134 for (j=0; j <= (ssize_t) MaxMap; j++)
2136 intensity+=histogram[GetPixelChannels(image)*j+i];
2137 map[GetPixelChannels(image)*j+i]=intensity;
2140 (void) memset(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
2141 sizeof(*equalize_map));
2142 (void) memset(black,0,sizeof(*black));
2143 (void) memset(white,0,sizeof(*white));
2144 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2150 white[i]=map[GetPixelChannels(image)*MaxMap+i];
2151 if (black[i] != white[i])
2152 for (j=0; j <= (ssize_t) MaxMap; j++)
2153 equalize_map[GetPixelChannels(image)*j+i]=(double)
2154 ScaleMapToQuantum((double) ((MaxMap*(map[
2155 GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
2157 histogram=(double *) RelinquishMagickMemory(histogram);
2158 map=(double *) RelinquishMagickMemory(map);
2159 if (image->storage_class == PseudoClass)
2167 for (j=0; j < (ssize_t) image->colors; j++)
2169 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2171 PixelChannel channel = GetPixelChannelChannel(image,
2173 if (black[channel] != white[channel])
2174 image->colormap[j].red=equalize_map[GetPixelChannels(image)*
2175 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+
2178 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2180 PixelChannel channel = GetPixelChannelChannel(image,
2182 if (black[channel] != white[channel])
2183 image->colormap[j].green=equalize_map[GetPixelChannels(image)*
2184 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+
2187 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2189 PixelChannel channel = GetPixelChannelChannel(image,
2191 if (black[channel] != white[channel])
2192 image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
2193 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+
2196 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2198 PixelChannel channel = GetPixelChannelChannel(image,
2200 if (black[channel] != white[channel])
2201 image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
2202 ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+
2211 image_view=AcquireAuthenticCacheView(image,exception);
2212 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2213 #pragma omp parallel for schedule(static) shared(progress,status) \
2214 magick_number_threads(image,image,image->rows,1)
2216 for (y=0; y < (ssize_t) image->rows; y++)
2224 if (status == MagickFalse)
2226 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2227 if (q == (Quantum *) NULL)
2232 for (x=0; x < (ssize_t) image->columns; x++)
2237 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2239 PixelChannel channel = GetPixelChannelChannel(image,j);
2240 PixelTrait traits = GetPixelChannelTraits(image,channel);
2241 if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
2243 q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
2244 ScaleQuantumToMap(q[j])+j]);
2246 q+=GetPixelChannels(image);
2248 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2250 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2255 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2259 proceed=SetImageProgress(image,EqualizeImageTag,progress,image->rows);
2260 if (proceed == MagickFalse)
2264 image_view=DestroyCacheView(image_view);
2265 equalize_map=(double *) RelinquishMagickMemory(equalize_map);
2270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2274 % G a m m a I m a g e %
2278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2280 % GammaImage() gamma-corrects a particular image channel. The same
2281 % image viewed on different devices will have perceptual differences in the
2282 % way the image's intensities are represented on the screen. Specify
2283 % individual gamma levels for the red, green, and blue channels, or adjust
2284 % all three with the gamma parameter. Values typically range from 0.8 to 2.3.
2286 % You can also reduce the influence of a particular channel with a gamma
2289 % The format of the GammaImage method is:
2291 % MagickBooleanType GammaImage(Image *image,const double gamma,
2292 % ExceptionInfo *exception)
2294 % A description of each parameter follows:
2296 % o image: the image.
2298 % o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
2300 % o gamma: the image gamma.
2304 static inline double gamma_pow(const double value,const double gamma)
2306 return(value < 0.0 ? value : pow(value,gamma));
2309 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
2310 ExceptionInfo *exception)
2312 #define GammaImageTag "Gamma/Image"
2333 Allocate and initialize gamma maps.
2335 assert(image != (Image *) NULL);
2336 assert(image->signature == MagickCoreSignature);
2337 if (image->debug != MagickFalse)
2338 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2341 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
2342 if (gamma_map == (Quantum *) NULL)
2343 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2345 (void) memset(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
2347 for (i=0; i <= (ssize_t) MaxMap; i++)
2348 gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
2349 MaxMap,PerceptibleReciprocal(gamma))));
2350 if (image->storage_class == PseudoClass)
2351 for (i=0; i < (ssize_t) image->colors; i++)
2354 Gamma-correct colormap.
2356 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2357 image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
2358 ClampToQuantum(image->colormap[i].red))];
2359 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2360 image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
2361 ClampToQuantum(image->colormap[i].green))];
2362 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2363 image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
2364 ClampToQuantum(image->colormap[i].blue))];
2365 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2366 image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
2367 ClampToQuantum(image->colormap[i].alpha))];
2370 Gamma-correct image.
2374 image_view=AcquireAuthenticCacheView(image,exception);
2375 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2376 #pragma omp parallel for schedule(static) shared(progress,status) \
2377 magick_number_threads(image,image,image->rows,1)
2379 for (y=0; y < (ssize_t) image->rows; y++)
2387 if (status == MagickFalse)
2389 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2390 if (q == (Quantum *) NULL)
2395 for (x=0; x < (ssize_t) image->columns; x++)
2400 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2402 PixelChannel channel = GetPixelChannelChannel(image,j);
2403 PixelTrait traits = GetPixelChannelTraits(image,channel);
2404 if ((traits & UpdatePixelTrait) == 0)
2406 q[j]=gamma_map[ScaleQuantumToMap(ClampToQuantum((MagickRealType)
2409 q+=GetPixelChannels(image);
2411 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2413 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2418 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2422 proceed=SetImageProgress(image,GammaImageTag,progress,image->rows);
2423 if (proceed == MagickFalse)
2427 image_view=DestroyCacheView(image_view);
2428 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
2429 if (image->gamma != 0.0)
2430 image->gamma*=gamma;
2435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2439 % G r a y s c a l e I m a g e %
2443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2445 % GrayscaleImage() converts the image to grayscale.
2447 % The format of the GrayscaleImage method is:
2449 % MagickBooleanType GrayscaleImage(Image *image,
2450 % const PixelIntensityMethod method ,ExceptionInfo *exception)
2452 % A description of each parameter follows:
2454 % o image: the image.
2456 % o method: the pixel intensity method.
2458 % o exception: return any errors or warnings in this structure.
2461 MagickExport MagickBooleanType GrayscaleImage(Image *image,
2462 const PixelIntensityMethod method,ExceptionInfo *exception)
2464 #define GrayscaleImageTag "Grayscale/Image"
2478 assert(image != (Image *) NULL);
2479 assert(image->signature == MagickCoreSignature);
2480 if (image->debug != MagickFalse)
2481 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2482 if (image->storage_class == PseudoClass)
2484 if (SyncImage(image,exception) == MagickFalse)
2485 return(MagickFalse);
2486 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2487 return(MagickFalse);
2489 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2490 if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
2492 image->intensity=method;
2493 image->type=GrayscaleType;
2494 if ((method == Rec601LuminancePixelIntensityMethod) ||
2495 (method == Rec709LuminancePixelIntensityMethod))
2496 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2497 return(SetImageColorspace(image,GRAYColorspace,exception));
2505 image_view=AcquireAuthenticCacheView(image,exception);
2506 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2507 #pragma omp parallel for schedule(static) shared(progress,status) \
2508 magick_number_threads(image,image,image->rows,1)
2510 for (y=0; y < (ssize_t) image->rows; y++)
2518 if (status == MagickFalse)
2520 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2521 if (q == (Quantum *) NULL)
2526 for (x=0; x < (ssize_t) image->columns; x++)
2534 red=(MagickRealType) GetPixelRed(image,q);
2535 green=(MagickRealType) GetPixelGreen(image,q);
2536 blue=(MagickRealType) GetPixelBlue(image,q);
2540 case AveragePixelIntensityMethod:
2542 intensity=(red+green+blue)/3.0;
2545 case BrightnessPixelIntensityMethod:
2547 intensity=MagickMax(MagickMax(red,green),blue);
2550 case LightnessPixelIntensityMethod:
2552 intensity=(MagickMin(MagickMin(red,green),blue)+
2553 MagickMax(MagickMax(red,green),blue))/2.0;
2556 case MSPixelIntensityMethod:
2558 intensity=(MagickRealType) (((double) red*red+green*green+
2562 case Rec601LumaPixelIntensityMethod:
2564 if (image->colorspace == RGBColorspace)
2566 red=EncodePixelGamma(red);
2567 green=EncodePixelGamma(green);
2568 blue=EncodePixelGamma(blue);
2570 intensity=0.298839*red+0.586811*green+0.114350*blue;
2573 case Rec601LuminancePixelIntensityMethod:
2575 if (image->colorspace == sRGBColorspace)
2577 red=DecodePixelGamma(red);
2578 green=DecodePixelGamma(green);
2579 blue=DecodePixelGamma(blue);
2581 intensity=0.298839*red+0.586811*green+0.114350*blue;
2584 case Rec709LumaPixelIntensityMethod:
2587 if (image->colorspace == RGBColorspace)
2589 red=EncodePixelGamma(red);
2590 green=EncodePixelGamma(green);
2591 blue=EncodePixelGamma(blue);
2593 intensity=0.212656*red+0.715158*green+0.072186*blue;
2596 case Rec709LuminancePixelIntensityMethod:
2598 if (image->colorspace == sRGBColorspace)
2600 red=DecodePixelGamma(red);
2601 green=DecodePixelGamma(green);
2602 blue=DecodePixelGamma(blue);
2604 intensity=0.212656*red+0.715158*green+0.072186*blue;
2607 case RMSPixelIntensityMethod:
2609 intensity=(MagickRealType) (sqrt((double) red*red+green*green+
2610 blue*blue)/sqrt(3.0));
2614 SetPixelGray(image,ClampToQuantum(intensity),q);
2615 q+=GetPixelChannels(image);
2617 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2619 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2624 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2628 proceed=SetImageProgress(image,GrayscaleImageTag,progress,image->rows);
2629 if (proceed == MagickFalse)
2633 image_view=DestroyCacheView(image_view);
2634 image->intensity=method;
2635 image->type=GrayscaleType;
2636 if ((method == Rec601LuminancePixelIntensityMethod) ||
2637 (method == Rec709LuminancePixelIntensityMethod))
2638 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2639 return(SetImageColorspace(image,GRAYColorspace,exception));
2643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2647 % H a l d C l u t I m a g e %
2651 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2653 % HaldClutImage() applies a Hald color lookup table to the image. A Hald
2654 % color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2655 % Create it with the HALD coder. You can apply any color transformation to
2656 % the Hald image and then use this method to apply the transform to the
2659 % The format of the HaldClutImage method is:
2661 % MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
2662 % ExceptionInfo *exception)
2664 % A description of each parameter follows:
2666 % o image: the image, which is replaced by indexed CLUT values
2668 % o hald_image: the color lookup table image for replacement color values.
2670 % o exception: return any errors or warnings in this structure.
2673 MagickExport MagickBooleanType HaldClutImage(Image *image,
2674 const Image *hald_image,ExceptionInfo *exception)
2676 #define HaldClutImageTag "Clut/Image"
2678 typedef struct _HaldInfo
2710 assert(image != (Image *) NULL);
2711 assert(image->signature == MagickCoreSignature);
2712 if (image->debug != MagickFalse)
2713 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2714 assert(hald_image != (Image *) NULL);
2715 assert(hald_image->signature == MagickCoreSignature);
2716 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2717 return(MagickFalse);
2718 if (image->alpha_trait == UndefinedPixelTrait)
2719 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2725 length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2726 (MagickRealType) hald_image->rows);
2727 for (level=2; (level*level*level) < length; level++) ;
2729 cube_size=level*level;
2730 width=(double) hald_image->columns;
2731 GetPixelInfo(hald_image,&zero);
2732 hald_view=AcquireVirtualCacheView(hald_image,exception);
2733 image_view=AcquireAuthenticCacheView(image,exception);
2734 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2735 #pragma omp parallel for schedule(static) shared(progress,status) \
2736 magick_number_threads(image,image,image->rows,1)
2738 for (y=0; y < (ssize_t) image->rows; y++)
2746 if (status == MagickFalse)
2748 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2749 if (q == (Quantum *) NULL)
2754 for (x=0; x < (ssize_t) image->columns; x++)
2770 point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2771 point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2772 point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2773 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2774 point.x-=floor(point.x);
2775 point.y-=floor(point.y);
2776 point.z-=floor(point.z);
2778 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2779 fmod(offset,width),floor(offset/width),&pixel1,exception);
2780 if (status == MagickFalse)
2783 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2784 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2785 if (status == MagickFalse)
2789 if (hald_image->interpolate == NearestInterpolatePixel)
2790 area=(point.y < 0.5) ? 0 : 1;
2791 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2794 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2795 fmod(offset,width),floor(offset/width),&pixel1,exception);
2796 if (status == MagickFalse)
2798 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2799 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2800 if (status == MagickFalse)
2803 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2807 if (hald_image->interpolate==NearestInterpolatePixel)
2808 area=(point.z<0.5)?0:1;
2809 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2811 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2812 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2813 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2814 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2815 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2816 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2817 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2818 (image->colorspace == CMYKColorspace))
2819 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2820 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2821 (image->alpha_trait != UndefinedPixelTrait))
2822 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2823 q+=GetPixelChannels(image);
2825 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2827 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2832 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2836 proceed=SetImageProgress(image,HaldClutImageTag,progress,image->rows);
2837 if (proceed == MagickFalse)
2841 hald_view=DestroyCacheView(hald_view);
2842 image_view=DestroyCacheView(image_view);
2847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2851 % L e v e l I m a g e %
2855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2857 % LevelImage() adjusts the levels of a particular image channel by
2858 % scaling the colors falling between specified white and black points to
2859 % the full available quantum range.
2861 % The parameters provided represent the black, and white points. The black
2862 % point specifies the darkest color in the image. Colors darker than the
2863 % black point are set to zero. White point specifies the lightest color in
2864 % the image. Colors brighter than the white point are set to the maximum
2867 % If a '!' flag is given, map black and white colors to the given levels
2868 % rather than mapping those levels to black and white. See
2869 % LevelizeImage() below.
2871 % Gamma specifies a gamma correction to apply to the image.
2873 % The format of the LevelImage method is:
2875 % MagickBooleanType LevelImage(Image *image,const double black_point,
2876 % const double white_point,const double gamma,ExceptionInfo *exception)
2878 % A description of each parameter follows:
2880 % o image: the image.
2882 % o black_point: The level to map zero (black) to.
2884 % o white_point: The level to map QuantumRange (white) to.
2886 % o exception: return any errors or warnings in this structure.
2890 static inline double LevelPixel(const double black_point,
2891 const double white_point,const double gamma,const double pixel)
2897 scale=PerceptibleReciprocal(white_point-black_point);
2898 level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
2899 PerceptibleReciprocal(gamma));
2900 return(level_pixel);
2903 MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2904 const double white_point,const double gamma,ExceptionInfo *exception)
2906 #define LevelImageTag "Level/Image"
2924 Allocate and initialize levels map.
2926 assert(image != (Image *) NULL);
2927 assert(image->signature == MagickCoreSignature);
2928 if (image->debug != MagickFalse)
2929 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2930 if (image->storage_class == PseudoClass)
2931 for (i=0; i < (ssize_t) image->colors; i++)
2936 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2937 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2938 white_point,gamma,image->colormap[i].red));
2939 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2940 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2941 white_point,gamma,image->colormap[i].green));
2942 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2943 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2944 white_point,gamma,image->colormap[i].blue));
2945 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2946 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2947 white_point,gamma,image->colormap[i].alpha));
2954 image_view=AcquireAuthenticCacheView(image,exception);
2955 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2956 #pragma omp parallel for schedule(static) shared(progress,status) \
2957 magick_number_threads(image,image,image->rows,1)
2959 for (y=0; y < (ssize_t) image->rows; y++)
2967 if (status == MagickFalse)
2969 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2970 if (q == (Quantum *) NULL)
2975 for (x=0; x < (ssize_t) image->columns; x++)
2980 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2982 PixelChannel channel = GetPixelChannelChannel(image,j);
2983 PixelTrait traits = GetPixelChannelTraits(image,channel);
2984 if ((traits & UpdatePixelTrait) == 0)
2986 q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2989 q+=GetPixelChannels(image);
2991 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2993 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2998 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3002 proceed=SetImageProgress(image,LevelImageTag,progress,image->rows);
3003 if (proceed == MagickFalse)
3007 image_view=DestroyCacheView(image_view);
3008 (void) ClampImage(image,exception);
3013 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3017 % L e v e l i z e I m a g e %
3021 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3023 % LevelizeImage() applies the reversed LevelImage() operation to just
3024 % the specific channels specified. It compresses the full range of color
3025 % values, so that they lie between the given black and white points. Gamma is
3026 % applied before the values are mapped.
3028 % LevelizeImage() can be called with by using a +level command line
3029 % API option, or using a '!' on a -level or LevelImage() geometry string.
3031 % It can be used to de-contrast a greyscale image to the exact levels
3032 % specified. Or by using specific levels for each channel of an image you
3033 % can convert a gray-scale image to any linear color gradient, according to
3036 % The format of the LevelizeImage method is:
3038 % MagickBooleanType LevelizeImage(Image *image,const double black_point,
3039 % const double white_point,const double gamma,ExceptionInfo *exception)
3041 % A description of each parameter follows:
3043 % o image: the image.
3045 % o black_point: The level to map zero (black) to.
3047 % o white_point: The level to map QuantumRange (white) to.
3049 % o gamma: adjust gamma by this factor before mapping values.
3051 % o exception: return any errors or warnings in this structure.
3054 MagickExport MagickBooleanType LevelizeImage(Image *image,
3055 const double black_point,const double white_point,const double gamma,
3056 ExceptionInfo *exception)
3058 #define LevelizeImageTag "Levelize/Image"
3059 #define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
3060 (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
3078 Allocate and initialize levels map.
3080 assert(image != (Image *) NULL);
3081 assert(image->signature == MagickCoreSignature);
3082 if (image->debug != MagickFalse)
3083 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3084 if (image->storage_class == PseudoClass)
3085 for (i=0; i < (ssize_t) image->colors; i++)
3090 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3091 image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
3092 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3093 image->colormap[i].green=(double) LevelizeValue(
3094 image->colormap[i].green);
3095 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3096 image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
3097 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3098 image->colormap[i].alpha=(double) LevelizeValue(
3099 image->colormap[i].alpha);
3106 image_view=AcquireAuthenticCacheView(image,exception);
3107 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3108 #pragma omp parallel for schedule(static) shared(progress,status) \
3109 magick_number_threads(image,image,image->rows,1)
3111 for (y=0; y < (ssize_t) image->rows; y++)
3119 if (status == MagickFalse)
3121 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3122 if (q == (Quantum *) NULL)
3127 for (x=0; x < (ssize_t) image->columns; x++)
3132 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3134 PixelChannel channel = GetPixelChannelChannel(image,j);
3135 PixelTrait traits = GetPixelChannelTraits(image,channel);
3136 if ((traits & UpdatePixelTrait) == 0)
3138 q[j]=LevelizeValue(q[j]);
3140 q+=GetPixelChannels(image);
3142 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3144 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3149 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3153 proceed=SetImageProgress(image,LevelizeImageTag,progress,image->rows);
3154 if (proceed == MagickFalse)
3158 image_view=DestroyCacheView(image_view);
3163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3167 % L e v e l I m a g e C o l o r s %
3171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3173 % LevelImageColors() maps the given color to "black" and "white" values,
3174 % linearly spreading out the colors, and level values on a channel by channel
3175 % bases, as per LevelImage(). The given colors allows you to specify
3176 % different level ranges for each of the color channels separately.
3178 % If the boolean 'invert' is set true the image values will modifyed in the
3179 % reverse direction. That is any existing "black" and "white" colors in the
3180 % image will become the color values given, with all other values compressed
3181 % appropriatally. This effectivally maps a greyscale gradient into the given
3184 % The format of the LevelImageColors method is:
3186 % MagickBooleanType LevelImageColors(Image *image,
3187 % const PixelInfo *black_color,const PixelInfo *white_color,
3188 % const MagickBooleanType invert,ExceptionInfo *exception)
3190 % A description of each parameter follows:
3192 % o image: the image.
3194 % o black_color: The color to map black to/from
3196 % o white_point: The color to map white to/from
3198 % o invert: if true map the colors (levelize), rather than from (level)
3200 % o exception: return any errors or warnings in this structure.
3203 MagickExport MagickBooleanType LevelImageColors(Image *image,
3204 const PixelInfo *black_color,const PixelInfo *white_color,
3205 const MagickBooleanType invert,ExceptionInfo *exception)
3214 Allocate and initialize levels map.
3216 assert(image != (Image *) NULL);
3217 assert(image->signature == MagickCoreSignature);
3218 if (image->debug != MagickFalse)
3219 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3220 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
3221 ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
3222 (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
3223 (void) SetImageColorspace(image,sRGBColorspace,exception);
3225 if (invert == MagickFalse)
3227 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3229 channel_mask=SetImageChannelMask(image,RedChannel);
3230 status&=LevelImage(image,black_color->red,white_color->red,1.0,
3232 (void) SetImageChannelMask(image,channel_mask);
3234 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3236 channel_mask=SetImageChannelMask(image,GreenChannel);
3237 status&=LevelImage(image,black_color->green,white_color->green,1.0,
3239 (void) SetImageChannelMask(image,channel_mask);
3241 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3243 channel_mask=SetImageChannelMask(image,BlueChannel);
3244 status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
3246 (void) SetImageChannelMask(image,channel_mask);
3248 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3249 (image->colorspace == CMYKColorspace))
3251 channel_mask=SetImageChannelMask(image,BlackChannel);
3252 status&=LevelImage(image,black_color->black,white_color->black,1.0,
3254 (void) SetImageChannelMask(image,channel_mask);
3256 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3257 (image->alpha_trait != UndefinedPixelTrait))
3259 channel_mask=SetImageChannelMask(image,AlphaChannel);
3260 status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
3262 (void) SetImageChannelMask(image,channel_mask);
3267 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3269 channel_mask=SetImageChannelMask(image,RedChannel);
3270 status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
3272 (void) SetImageChannelMask(image,channel_mask);
3274 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3276 channel_mask=SetImageChannelMask(image,GreenChannel);
3277 status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
3279 (void) SetImageChannelMask(image,channel_mask);
3281 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3283 channel_mask=SetImageChannelMask(image,BlueChannel);
3284 status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
3286 (void) SetImageChannelMask(image,channel_mask);
3288 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3289 (image->colorspace == CMYKColorspace))
3291 channel_mask=SetImageChannelMask(image,BlackChannel);
3292 status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
3294 (void) SetImageChannelMask(image,channel_mask);
3296 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3297 (image->alpha_trait != UndefinedPixelTrait))
3299 channel_mask=SetImageChannelMask(image,AlphaChannel);
3300 status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
3302 (void) SetImageChannelMask(image,channel_mask);
3305 return(status != 0 ? MagickTrue : MagickFalse);
3309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3313 % L i n e a r S t r e t c h I m a g e %
3317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3319 % LinearStretchImage() discards any pixels below the black point and above
3320 % the white point and levels the remaining pixels.
3322 % The format of the LinearStretchImage method is:
3324 % MagickBooleanType LinearStretchImage(Image *image,
3325 % const double black_point,const double white_point,
3326 % ExceptionInfo *exception)
3328 % A description of each parameter follows:
3330 % o image: the image.
3332 % o black_point: the black point.
3334 % o white_point: the white point.
3336 % o exception: return any errors or warnings in this structure.
3339 MagickExport MagickBooleanType LinearStretchImage(Image *image,
3340 const double black_point,const double white_point,ExceptionInfo *exception)
3342 #define LinearStretchImageTag "LinearStretch/Image"
3360 Allocate histogram and linear map.
3362 assert(image != (Image *) NULL);
3363 assert(image->signature == MagickCoreSignature);
3364 histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
3365 if (histogram == (double *) NULL)
3366 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3371 (void) memset(histogram,0,(MaxMap+1)*sizeof(*histogram));
3372 image_view=AcquireVirtualCacheView(image,exception);
3373 for (y=0; y < (ssize_t) image->rows; y++)
3375 register const Quantum
3381 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3382 if (p == (const Quantum *) NULL)
3384 for (x=0; x < (ssize_t) image->columns; x++)
3386 intensity=GetPixelIntensity(image,p);
3387 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
3388 p+=GetPixelChannels(image);
3391 image_view=DestroyCacheView(image_view);
3393 Find the histogram boundaries by locating the black and white point levels.
3396 for (black=0; black < (ssize_t) MaxMap; black++)
3398 intensity+=histogram[black];
3399 if (intensity >= black_point)
3403 for (white=(ssize_t) MaxMap; white != 0; white--)
3405 intensity+=histogram[white];
3406 if (intensity >= white_point)
3409 histogram=(double *) RelinquishMagickMemory(histogram);
3410 status=LevelImage(image,(double) ScaleMapToQuantum((MagickRealType) black),
3411 (double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
3417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3421 % M o d u l a t e I m a g e %
3425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3427 % ModulateImage() lets you control the brightness, saturation, and hue
3428 % of an image. Modulate represents the brightness, saturation, and hue
3429 % as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
3430 % modulation is lightness, saturation, and hue. For HWB, use blackness,
3431 % whiteness, and hue. And for HCL, use chrome, luma, and hue.
3433 % The format of the ModulateImage method is:
3435 % MagickBooleanType ModulateImage(Image *image,const char *modulate,
3436 % ExceptionInfo *exception)
3438 % A description of each parameter follows:
3440 % o image: the image.
3442 % o modulate: Define the percent change in brightness, saturation, and hue.
3444 % o exception: return any errors or warnings in this structure.
3448 static inline void ModulateHCL(const double percent_hue,
3449 const double percent_chroma,const double percent_luma,double *red,
3450 double *green,double *blue)
3458 Increase or decrease color luma, chroma, or hue.
3460 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
3461 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3462 chroma*=0.01*percent_chroma;
3463 luma*=0.01*percent_luma;
3464 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
3467 static inline void ModulateHCLp(const double percent_hue,
3468 const double percent_chroma,const double percent_luma,double *red,
3469 double *green,double *blue)
3477 Increase or decrease color luma, chroma, or hue.
3479 ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
3480 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3481 chroma*=0.01*percent_chroma;
3482 luma*=0.01*percent_luma;
3483 ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
3486 static inline void ModulateHSB(const double percent_hue,
3487 const double percent_saturation,const double percent_brightness,double *red,
3488 double *green,double *blue)
3496 Increase or decrease color brightness, saturation, or hue.
3498 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
3499 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3500 saturation*=0.01*percent_saturation;
3501 brightness*=0.01*percent_brightness;
3502 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3505 static inline void ModulateHSI(const double percent_hue,
3506 const double percent_saturation,const double percent_intensity,double *red,
3507 double *green,double *blue)
3515 Increase or decrease color intensity, saturation, or hue.
3517 ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3518 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3519 saturation*=0.01*percent_saturation;
3520 intensity*=0.01*percent_intensity;
3521 ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3524 static inline void ModulateHSL(const double percent_hue,
3525 const double percent_saturation,const double percent_lightness,double *red,
3526 double *green,double *blue)
3534 Increase or decrease color lightness, saturation, or hue.
3536 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3537 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3538 saturation*=0.01*percent_saturation;
3539 lightness*=0.01*percent_lightness;
3540 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3543 static inline void ModulateHSV(const double percent_hue,
3544 const double percent_saturation,const double percent_value,double *red,
3545 double *green,double *blue)
3553 Increase or decrease color value, saturation, or hue.
3555 ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3556 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3557 saturation*=0.01*percent_saturation;
3558 value*=0.01*percent_value;
3559 ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3562 static inline void ModulateHWB(const double percent_hue,
3563 const double percent_whiteness,const double percent_blackness,double *red,
3564 double *green,double *blue)
3572 Increase or decrease color blackness, whiteness, or hue.
3574 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3575 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3576 blackness*=0.01*percent_blackness;
3577 whiteness*=0.01*percent_whiteness;
3578 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3581 static inline void ModulateLCHab(const double percent_luma,
3582 const double percent_chroma,const double percent_hue,double *red,
3583 double *green,double *blue)
3591 Increase or decrease color luma, chroma, or hue.
3593 ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
3594 luma*=0.01*percent_luma;
3595 chroma*=0.01*percent_chroma;
3596 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3597 ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
3600 static inline void ModulateLCHuv(const double percent_luma,
3601 const double percent_chroma,const double percent_hue,double *red,
3602 double *green,double *blue)
3610 Increase or decrease color luma, chroma, or hue.
3612 ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
3613 luma*=0.01*percent_luma;
3614 chroma*=0.01*percent_chroma;
3615 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3616 ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
3619 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
3620 ExceptionInfo *exception)
3622 #define ModulateImageTag "Modulate/Image"
3657 Initialize modulate table.
3659 assert(image != (Image *) NULL);
3660 assert(image->signature == MagickCoreSignature);
3661 if (image->debug != MagickFalse)
3662 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3663 if (modulate == (char *) NULL)
3664 return(MagickFalse);
3665 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
3666 (void) SetImageColorspace(image,sRGBColorspace,exception);
3667 flags=ParseGeometry(modulate,&geometry_info);
3668 percent_brightness=geometry_info.rho;
3669 percent_saturation=geometry_info.sigma;
3670 if ((flags & SigmaValue) == 0)
3671 percent_saturation=100.0;
3672 percent_hue=geometry_info.xi;
3673 if ((flags & XiValue) == 0)
3675 colorspace=UndefinedColorspace;
3676 artifact=GetImageArtifact(image,"modulate:colorspace");
3677 if (artifact != (const char *) NULL)
3678 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3679 MagickFalse,artifact);
3680 if (image->storage_class == PseudoClass)
3681 for (i=0; i < (ssize_t) image->colors; i++)
3689 Modulate image colormap.
3691 red=(double) image->colormap[i].red;
3692 green=(double) image->colormap[i].green;
3693 blue=(double) image->colormap[i].blue;
3698 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3702 case HCLpColorspace:
3704 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3710 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3716 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3723 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3729 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3735 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3740 case LCHabColorspace:
3742 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3746 case LCHuvColorspace:
3748 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3753 image->colormap[i].red=red;
3754 image->colormap[i].green=green;
3755 image->colormap[i].blue=blue;
3760 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3761 if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3762 percent_saturation,colorspace,exception) != MagickFalse)
3767 image_view=AcquireAuthenticCacheView(image,exception);
3768 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3769 #pragma omp parallel for schedule(static) shared(progress,status) \
3770 magick_number_threads(image,image,image->rows,1)
3772 for (y=0; y < (ssize_t) image->rows; y++)
3780 if (status == MagickFalse)
3782 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3783 if (q == (Quantum *) NULL)
3788 for (x=0; x < (ssize_t) image->columns; x++)
3795 red=(double) GetPixelRed(image,q);
3796 green=(double) GetPixelGreen(image,q);
3797 blue=(double) GetPixelBlue(image,q);
3802 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3806 case HCLpColorspace:
3808 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3814 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3821 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3827 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3833 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3837 case LCHabColorspace:
3839 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3844 case LCHuvColorspace:
3846 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3851 SetPixelRed(image,ClampToQuantum(red),q);
3852 SetPixelGreen(image,ClampToQuantum(green),q);
3853 SetPixelBlue(image,ClampToQuantum(blue),q);
3854 q+=GetPixelChannels(image);
3856 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3858 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3863 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3867 proceed=SetImageProgress(image,ModulateImageTag,progress,image->rows);
3868 if (proceed == MagickFalse)
3872 image_view=DestroyCacheView(image_view);
3877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3881 % N e g a t e I m a g e %
3885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3887 % NegateImage() negates the colors in the reference image. The grayscale
3888 % option means that only grayscale values within the image are negated.
3890 % The format of the NegateImage method is:
3892 % MagickBooleanType NegateImage(Image *image,
3893 % const MagickBooleanType grayscale,ExceptionInfo *exception)
3895 % A description of each parameter follows:
3897 % o image: the image.
3899 % o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3901 % o exception: return any errors or warnings in this structure.
3904 MagickExport MagickBooleanType NegateImage(Image *image,
3905 const MagickBooleanType grayscale,ExceptionInfo *exception)
3907 #define NegateImageTag "Negate/Image"
3924 assert(image != (Image *) NULL);
3925 assert(image->signature == MagickCoreSignature);
3926 if (image->debug != MagickFalse)
3927 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3928 if (image->storage_class == PseudoClass)
3929 for (i=0; i < (ssize_t) image->colors; i++)
3934 if( grayscale != MagickFalse )
3935 if ((image->colormap[i].red != image->colormap[i].green) ||
3936 (image->colormap[i].green != image->colormap[i].blue))
3938 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3939 image->colormap[i].red=QuantumRange-image->colormap[i].red;
3940 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3941 image->colormap[i].green=QuantumRange-image->colormap[i].green;
3942 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3943 image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3950 image_view=AcquireAuthenticCacheView(image,exception);
3951 if( grayscale != MagickFalse )
3953 for (y=0; y < (ssize_t) image->rows; y++)
3964 if (status == MagickFalse)
3966 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3968 if (q == (Quantum *) NULL)
3973 for (x=0; x < (ssize_t) image->columns; x++)
3978 if (IsPixelGray(image,q) != MagickFalse)
3980 q+=GetPixelChannels(image);
3983 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3985 PixelChannel channel = GetPixelChannelChannel(image,j);
3986 PixelTrait traits = GetPixelChannelTraits(image,channel);
3987 if ((traits & UpdatePixelTrait) == 0)
3989 q[j]=QuantumRange-q[j];
3991 q+=GetPixelChannels(image);
3993 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3994 if (sync == MagickFalse)
3996 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4001 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4005 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4006 if (proceed == MagickFalse)
4010 image_view=DestroyCacheView(image_view);
4016 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4017 #pragma omp parallel for schedule(static) shared(progress,status) \
4018 magick_number_threads(image,image,image->rows,1)
4020 for (y=0; y < (ssize_t) image->rows; y++)
4028 if (status == MagickFalse)
4030 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4031 if (q == (Quantum *) NULL)
4036 for (x=0; x < (ssize_t) image->columns; x++)
4041 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4043 PixelChannel channel = GetPixelChannelChannel(image,j);
4044 PixelTrait traits = GetPixelChannelTraits(image,channel);
4045 if ((traits & UpdatePixelTrait) == 0)
4047 q[j]=QuantumRange-q[j];
4049 q+=GetPixelChannels(image);
4051 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4053 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4058 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4062 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4063 if (proceed == MagickFalse)
4067 image_view=DestroyCacheView(image_view);
4072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4076 % N o r m a l i z e I m a g e %
4080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4082 % The NormalizeImage() method enhances the contrast of a color image by
4083 % mapping the darkest 2 percent of all pixel to black and the brightest
4084 % 1 percent to white.
4086 % The format of the NormalizeImage method is:
4088 % MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
4090 % A description of each parameter follows:
4092 % o image: the image.
4094 % o exception: return any errors or warnings in this structure.
4097 MagickExport MagickBooleanType NormalizeImage(Image *image,
4098 ExceptionInfo *exception)
4104 black_point=(double) image->columns*image->rows*0.0015;
4105 white_point=(double) image->columns*image->rows*0.9995;
4106 return(ContrastStretchImage(image,black_point,white_point,exception));
4110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4114 % S i g m o i d a l C o n t r a s t I m a g e %
4118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4120 % SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
4121 % sigmoidal contrast algorithm. Increase the contrast of the image using a
4122 % sigmoidal transfer function without saturating highlights or shadows.
4123 % Contrast indicates how much to increase the contrast (0 is none; 3 is
4124 % typical; 20 is pushing it); mid-point indicates where midtones fall in the
4125 % resultant image (0 is white; 50% is middle-gray; 100% is black). Set
4126 % sharpen to MagickTrue to increase the image contrast otherwise the contrast
4129 % The format of the SigmoidalContrastImage method is:
4131 % MagickBooleanType SigmoidalContrastImage(Image *image,
4132 % const MagickBooleanType sharpen,const char *levels,
4133 % ExceptionInfo *exception)
4135 % A description of each parameter follows:
4137 % o image: the image.
4139 % o sharpen: Increase or decrease image contrast.
4141 % o contrast: strength of the contrast, the larger the number the more
4142 % 'threshold-like' it becomes.
4144 % o midpoint: midpoint of the function as a color value 0 to QuantumRange.
4146 % o exception: return any errors or warnings in this structure.
4151 ImageMagick 6 has a version of this function which uses LUTs.
4155 Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
4158 The first version, based on the hyperbolic tangent tanh, when combined with
4159 the scaling step, is an exact arithmetic clone of the the sigmoid function
4160 based on the logistic curve. The equivalence is based on the identity
4162 1/(1+exp(-t)) = (1+tanh(t/2))/2
4164 (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
4165 scaled sigmoidal derivation is invariant under affine transformations of
4168 The tanh version is almost certainly more accurate and cheaper. The 0.5
4169 factor in the argument is to clone the legacy ImageMagick behavior. The
4170 reason for making the define depend on atanh even though it only uses tanh
4171 has to do with the construction of the inverse of the scaled sigmoidal.
4173 #if defined(MAGICKCORE_HAVE_ATANH)
4174 #define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
4176 #define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
4179 Scaled sigmoidal function:
4181 ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
4182 ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
4184 See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
4185 http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf. The limit
4186 of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
4187 zero. This is fixed below by exiting immediately when contrast is small,
4188 leaving the image (or colormap) unmodified. This appears to be safe because
4189 the series expansion of the logistic sigmoidal function around x=b is
4193 so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
4195 #define ScaledSigmoidal(a,b,x) ( \
4196 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
4197 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
4199 Inverse of ScaledSigmoidal, used for +sigmoidal-contrast. Because b
4200 may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
4201 sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
4202 when creating a LUT from in gamut values, hence the branching. In
4203 addition, HDRI may have out of gamut values.
4204 InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
4205 It is only a right inverse. This is unavoidable.
4207 static inline double InverseScaledSigmoidal(const double a,const double b,
4210 const double sig0=Sigmoidal(a,b,0.0);
4211 const double sig1=Sigmoidal(a,b,1.0);
4212 const double argument=(sig1-sig0)*x+sig0;
4213 const double clamped=
4215 #if defined(MAGICKCORE_HAVE_ATANH)
4216 argument < -1+MagickEpsilon
4220 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4222 return(b+(2.0/a)*atanh(clamped));
4224 argument < MagickEpsilon
4228 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4230 return(b-log(1.0/clamped-1.0)/a);
4234 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
4235 const MagickBooleanType sharpen,const double contrast,const double midpoint,
4236 ExceptionInfo *exception)
4238 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
4239 #define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
4240 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
4241 #define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
4242 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
4259 assert(image != (Image *) NULL);
4260 assert(image->signature == MagickCoreSignature);
4261 if (image->debug != MagickFalse)
4262 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4264 Side effect: may clamp values unless contrast<MagickEpsilon, in which
4265 case nothing is done.
4267 if (contrast < MagickEpsilon)
4270 Sigmoidal-contrast enhance colormap.
4272 if (image->storage_class == PseudoClass)
4277 if( sharpen != MagickFalse )
4278 for (i=0; i < (ssize_t) image->colors; i++)
4280 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4281 image->colormap[i].red=(MagickRealType) ScaledSig(
4282 image->colormap[i].red);
4283 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4284 image->colormap[i].green=(MagickRealType) ScaledSig(
4285 image->colormap[i].green);
4286 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4287 image->colormap[i].blue=(MagickRealType) ScaledSig(
4288 image->colormap[i].blue);
4289 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4290 image->colormap[i].alpha=(MagickRealType) ScaledSig(
4291 image->colormap[i].alpha);
4294 for (i=0; i < (ssize_t) image->colors; i++)
4296 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4297 image->colormap[i].red=(MagickRealType) InverseScaledSig(
4298 image->colormap[i].red);
4299 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4300 image->colormap[i].green=(MagickRealType) InverseScaledSig(
4301 image->colormap[i].green);
4302 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4303 image->colormap[i].blue=(MagickRealType) InverseScaledSig(
4304 image->colormap[i].blue);
4305 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4306 image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
4307 image->colormap[i].alpha);
4311 Sigmoidal-contrast enhance image.
4315 image_view=AcquireAuthenticCacheView(image,exception);
4316 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4317 #pragma omp parallel for schedule(static) shared(progress,status) \
4318 magick_number_threads(image,image,image->rows,1)
4320 for (y=0; y < (ssize_t) image->rows; y++)
4328 if (status == MagickFalse)
4330 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4331 if (q == (Quantum *) NULL)
4336 for (x=0; x < (ssize_t) image->columns; x++)
4341 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4343 PixelChannel channel = GetPixelChannelChannel(image,i);
4344 PixelTrait traits = GetPixelChannelTraits(image,channel);
4345 if ((traits & UpdatePixelTrait) == 0)
4347 if( sharpen != MagickFalse )
4348 q[i]=ScaledSig(q[i]);
4350 q[i]=InverseScaledSig(q[i]);
4352 q+=GetPixelChannels(image);
4354 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4356 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4361 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4365 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress,
4367 if (proceed == MagickFalse)
4371 image_view=DestroyCacheView(image_view);