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/property.h"
73#include "MagickCore/quantum.h"
74#include "MagickCore/quantum-private.h"
75#include "MagickCore/resample.h"
76#include "MagickCore/resample-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/statistic.h"
79#include "MagickCore/string_.h"
80#include "MagickCore/string-private.h"
81#include "MagickCore/thread-private.h"
82#include "MagickCore/threshold.h"
83#include "MagickCore/token.h"
84#include "MagickCore/xml-tree.h"
85#include "MagickCore/xml-tree-private.h"
112MagickExport MagickBooleanType AutoGammaImage(Image *image,
113 ExceptionInfo *exception)
128 if (image->channel_mask == AllChannels)
133 (void) GetImageMean(image,&mean,&sans,exception);
134 gamma=log(mean*QuantumScale)/log_mean;
135 return(LevelImage(image,0.0,(
double) QuantumRange,gamma,exception));
141 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
146 PixelChannel channel = GetPixelChannelChannel(image,i);
147 PixelTrait traits = GetPixelChannelTraits(image,channel);
148 if ((traits & UpdatePixelTrait) == 0)
150 channel_mask=SetImageChannelMask(image,(ChannelType) (1UL << i));
151 status=GetImageMean(image,&mean,&sans,exception);
152 gamma=log(mean*QuantumScale)/log_mean;
153 status&=(MagickStatusType) LevelImage(image,0.0,(
double) QuantumRange,gamma,
155 (void) SetImageChannelMask(image,channel_mask);
156 if (status == MagickFalse)
159 return(status != 0 ? MagickTrue : MagickFalse);
187MagickExport MagickBooleanType AutoLevelImage(Image *image,
188 ExceptionInfo *exception)
190 return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
224MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
225 const double brightness,
const double contrast,ExceptionInfo *exception)
227#define BrightnessContrastImageTag "BrightnessContrast/Image"
240 assert(image != (Image *) NULL);
241 assert(image->signature == MagickCoreSignature);
242 if (IsEventLogging() != MagickFalse)
243 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
244 slope=100.0*MagickSafeReciprocal(100.0-contrast);
246 slope=0.01*contrast+1.0;
247 intercept=(0.01*brightness-0.5)*slope+0.5;
248 coefficients[0]=slope;
249 coefficients[1]=intercept;
250 status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
302static void ClipCLAHEHistogram(
const double clip_limit,
const size_t number_bins,
305#define NumberCLAHEGrays (65536)
317 if (number_bins == 0)
320 for (i=0; i < (ssize_t) number_bins; i++)
322 excess=(ssize_t) histogram[i]-(ssize_t) clip_limit;
324 cumulative_excess+=excess;
329 step=cumulative_excess/(ssize_t) number_bins;
330 excess=(ssize_t) (clip_limit-step);
331 for (i=0; i < (ssize_t) number_bins; i++)
333 if ((
double) histogram[i] > clip_limit)
334 histogram[i]=(size_t) clip_limit;
336 if ((ssize_t) histogram[i] > excess)
338 cumulative_excess-=(ssize_t) histogram[i]-excess;
339 histogram[i]=(size_t) clip_limit;
343 cumulative_excess-=step;
344 histogram[i]+=(size_t) step;
358 previous_excess=cumulative_excess;
360 q=histogram+number_bins;
361 while ((cumulative_excess != 0) && (p < q))
363 step=(ssize_t) number_bins/cumulative_excess;
366 for (p=histogram; (p < q) && (cumulative_excess != 0); p+=(ptrdiff_t) step)
367 if ((
double) *p < clip_limit)
374 }
while ((cumulative_excess != 0) && (cumulative_excess < previous_excess));
377static void GenerateCLAHEHistogram(
const RectangleInfo *clahe_info,
378 const RectangleInfo *tile_info,
const size_t number_bins,
379 const unsigned short *lut,
const unsigned short *pixels,
size_t *histogram)
390 for (i=0; i < (ssize_t) number_bins; i++)
393 for (i=0; i < (ssize_t) tile_info->height; i++)
398 q=p+tile_info->width;
400 histogram[lut[*p++]]++;
401 q+=(ptrdiff_t) clahe_info->width;
402 p=q-tile_info->width;
406static void InterpolateCLAHE(
const RectangleInfo *clahe_info,
const size_t *Q12,
407 const size_t *Q22,
const size_t *Q11,
const size_t *Q21,
408 const RectangleInfo *tile,
const unsigned short *lut,
unsigned short *pixels)
419 for (y=(ssize_t) tile->height; y > 0; y--)
424 for (x=(ssize_t) tile->width; x > 0; x--)
426 intensity=lut[*pixels];
427 *pixels++=(
unsigned short) (MagickSafeReciprocal((
double) tile->width*
428 tile->height)*(y*((
double) x*Q12[intensity]+((
double) tile->width-x)*
429 Q22[intensity])+((
double) tile->height-y)*((
double) x*Q11[intensity]+
430 ((
double) tile->width-x)*Q21[intensity])));
432 pixels+=(clahe_info->width-tile->width);
436static void GenerateCLAHELut(
const RangeInfo *range_info,
437 const size_t number_bins,
unsigned short *lut)
448 delta=(
unsigned short) ((range_info->max-range_info->min)/number_bins+1);
449 for (i=(ssize_t) range_info->min; i <= (ssize_t) range_info->max; i++)
450 lut[i]=(
unsigned short) ((i-range_info->min)/delta);
453static void MapCLAHEHistogram(
const RangeInfo *range_info,
454 const size_t number_bins,
const size_t number_pixels,
size_t *histogram)
466 scale=(double) (range_info->max-range_info->min)/number_pixels;
468 for (i=0; i < (ssize_t) number_bins; i++)
471 histogram[i]=(size_t) (range_info->min+scale*sum);
472 if (histogram[i] > range_info->max)
473 histogram[i]=range_info->max;
477static MagickBooleanType CLAHE(
const RectangleInfo *clahe_info,
478 const RectangleInfo *tile_info,
const RangeInfo *range_info,
479 const size_t number_bins,
const double clip_limit,
unsigned short *pixels)
500 if (clip_limit == 1.0)
502 tile_cache=AcquireVirtualMemory((
size_t) clahe_info->x*number_bins,
503 (
size_t) clahe_info->y*
sizeof(*tiles));
504 if (tile_cache == (MemoryInfo *) NULL)
506 lut=(
unsigned short *) AcquireQuantumMemory(NumberCLAHEGrays,
sizeof(*lut));
507 if (lut == (
unsigned short *) NULL)
509 tile_cache=RelinquishVirtualMemory(tile_cache);
512 tiles=(
size_t *) GetVirtualMemoryBlob(tile_cache);
513 limit=(size_t) (clip_limit*(tile_info->width*tile_info->height)/number_bins);
519 GenerateCLAHELut(range_info,number_bins,lut);
521 for (y=0; y < (ssize_t) clahe_info->y; y++)
526 for (x=0; x < (ssize_t) clahe_info->x; x++)
531 histogram=tiles+((ssize_t) number_bins*(y*clahe_info->x+x));
532 GenerateCLAHEHistogram(clahe_info,tile_info,number_bins,lut,p,histogram);
533 ClipCLAHEHistogram((
double) limit,number_bins,histogram);
534 MapCLAHEHistogram(range_info,number_bins,tile_info->width*
535 tile_info->height,histogram);
536 p+=(ptrdiff_t) tile_info->width;
538 p+=(ptrdiff_t) clahe_info->width*(tile_info->height-1);
544 for (y=0; y <= (ssize_t) clahe_info->y; y++)
555 tile.height=tile_info->height;
563 tile.height=tile_info->height >> 1;
568 if (y == (ssize_t) clahe_info->y)
573 tile.height=(tile_info->height+1) >> 1;
574 tile.y=clahe_info->y-1;
577 for (x=0; x <= (ssize_t) clahe_info->x; x++)
579 tile.width=tile_info->width;
587 tile.width=tile_info->width >> 1;
592 if (x == (ssize_t) clahe_info->x)
597 tile.width=(tile_info->width+1) >> 1;
598 tile.x=clahe_info->x-1;
601 InterpolateCLAHE(clahe_info,
602 tiles+((ssize_t) number_bins*(tile.y*clahe_info->x+tile.x)),
603 tiles+((ssize_t) number_bins*(tile.y*clahe_info->x+offset.x)),
604 tiles+((ssize_t) number_bins*(offset.y*clahe_info->x+tile.x)),
605 tiles+((ssize_t) number_bins*(offset.y*clahe_info->x+offset.x)),
607 p+=(ptrdiff_t) tile.width;
609 p+=(ptrdiff_t) clahe_info->width*(tile.height-1);
611 lut=(
unsigned short *) RelinquishMagickMemory(lut);
612 tile_cache=RelinquishVirtualMemory(tile_cache);
616MagickExport MagickBooleanType CLAHEImage(Image *image,
const size_t width,
617 const size_t height,
const size_t number_bins,
const double clip_limit,
618 ExceptionInfo *exception)
620#define CLAHEImageTag "CLAHE/Image"
656 assert(image != (Image *) NULL);
657 assert(image->signature == MagickCoreSignature);
658 if (IsEventLogging() != MagickFalse)
659 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
661 range_info.max=NumberCLAHEGrays-1;
662 tile_info.width=width;
663 if (tile_info.width == 0)
664 tile_info.width=image->columns >> 3;
665 tile_info.height=height;
666 if (tile_info.height == 0)
667 tile_info.height=image->rows >> 3;
669 if ((image->columns % tile_info.width) != 0)
670 tile_info.x=(ssize_t) (tile_info.width-(image->columns % tile_info.width));
672 if ((image->rows % tile_info.height) != 0)
673 tile_info.y=(ssize_t) (tile_info.height-(image->rows % tile_info.height));
674 clahe_info.width=(size_t) ((ssize_t) image->columns+tile_info.x);
675 clahe_info.height=(size_t) ((ssize_t) image->rows+tile_info.y);
676 clahe_info.x=(ssize_t) (clahe_info.width/tile_info.width);
677 clahe_info.y=(ssize_t) (clahe_info.height/tile_info.height);
678 pixel_cache=AcquireVirtualMemory(clahe_info.width,clahe_info.height*
680 if (pixel_cache == (MemoryInfo *) NULL)
681 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
683 pixels=(
unsigned short *) GetVirtualMemoryBlob(pixel_cache);
684 colorspace=image->colorspace;
685 if (TransformImageColorspace(image,LabColorspace,exception) == MagickFalse)
687 pixel_cache=RelinquishVirtualMemory(pixel_cache);
693 image_view=AcquireVirtualCacheView(image,exception);
697 for (y=0; y < (ssize_t) clahe_info.height; y++)
705 if (status == MagickFalse)
707 p=GetCacheViewVirtualPixels(image_view,-(tile_info.x >> 1),y-
708 (tile_info.y >> 1),clahe_info.width,1,exception);
709 if (p == (
const Quantum *) NULL)
714 for (x=0; x < (ssize_t) clahe_info.width; x++)
716 pixels[n++]=ScaleQuantumToShort(p[0]);
717 p+=(ptrdiff_t) GetPixelChannels(image);
719 if (image->progress_monitor != (MagickProgressMonitor) NULL)
725 proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
726 GetPixelChannels(image));
727 if (proceed == MagickFalse)
731 image_view=DestroyCacheView(image_view);
732 status=CLAHE(&clahe_info,&tile_info,&range_info,number_bins == 0 ?
733 (
size_t) 128 : MagickMin(number_bins,256),clip_limit,pixels);
734 if (status == MagickFalse)
735 (void) ThrowMagickException(exception,GetMagickModule(),
736 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
740 image_view=AcquireAuthenticCacheView(image,exception);
741 n=clahe_info.width*(size_t) (tile_info.y/2);
742 for (y=0; y < (ssize_t) image->rows; y++)
750 if (status == MagickFalse)
752 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
753 if (q == (Quantum *) NULL)
758 n+=(size_t) (tile_info.x/2);
759 for (x=0; x < (ssize_t) image->columns; x++)
761 q[0]=ScaleShortToQuantum(pixels[n++]);
762 q+=(ptrdiff_t) GetPixelChannels(image);
764 n+=(size_t) ((ssize_t) clahe_info.width-(ssize_t) image->columns-
766 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
768 if (image->progress_monitor != (MagickProgressMonitor) NULL)
774 proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
775 GetPixelChannels(image));
776 if (proceed == MagickFalse)
780 image_view=DestroyCacheView(image_view);
781 pixel_cache=RelinquishVirtualMemory(pixel_cache);
782 if (TransformImageColorspace(image,colorspace,exception) == MagickFalse)
832MagickExport MagickBooleanType ClutImage(Image *image,
const Image *clut_image,
833 const PixelInterpolateMethod method,ExceptionInfo *exception)
835#define ClutImageTag "Clut/Image"
855 assert(image != (Image *) NULL);
856 assert(image->signature == MagickCoreSignature);
857 assert(clut_image != (Image *) NULL);
858 assert(clut_image->signature == MagickCoreSignature);
859 if (IsEventLogging() != MagickFalse)
860 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
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) (method == 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,(
double) i*
881 ((
double) clut_image->columns-adjust)/MaxMap,(
double) i*
882 ((
double) 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+=(ptrdiff_t) 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);
1010MagickExport 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];
1071 assert(image != (Image *) NULL);
1072 assert(image->signature == MagickCoreSignature);
1073 if (IsEventLogging() != 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++)
1268 luma=0.21267*image->colormap[i].red+0.71526*image->colormap[i].green+
1269 0.07217*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;
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.21267*(double) GetPixelRed(image,q)+0.71526*(double)
1309 GetPixelGreen(image,q)+0.07217*(double) 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+=(ptrdiff_t) 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);
1370static inline void Contrast(
const int sign,
double *red,
double *green,
1381 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
1382 brightness+=0.5*sign*(0.5*(sin((
double) (MagickPI*(brightness-0.5)))+1.0)-
1384 if (brightness > 1.0)
1387 if (brightness < 0.0)
1389 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
1392MagickExport MagickBooleanType ContrastImage(Image *image,
1393 const MagickBooleanType sharpen,ExceptionInfo *exception)
1395#define ContrastImageTag "Contrast/Image"
1415 assert(image != (Image *) NULL);
1416 assert(image->signature == MagickCoreSignature);
1417#if defined(MAGICKCORE_OPENCL_SUPPORT)
1418 if (AccelerateContrastImage(image,sharpen,exception) != MagickFalse)
1421 if (IsEventLogging() != MagickFalse)
1422 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1423 sign=sharpen != MagickFalse ? 1 : -1;
1424 if (image->storage_class == PseudoClass)
1429 for (i=0; i < (ssize_t) image->colors; i++)
1436 red=(double) image->colormap[i].red;
1437 green=(double) image->colormap[i].green;
1438 blue=(double) image->colormap[i].blue;
1439 Contrast(sign,&red,&green,&blue);
1440 image->colormap[i].red=(MagickRealType) red;
1441 image->colormap[i].green=(MagickRealType) green;
1442 image->colormap[i].blue=(MagickRealType) blue;
1450 image_view=AcquireAuthenticCacheView(image,exception);
1451#if defined(MAGICKCORE_OPENMP_SUPPORT)
1452 #pragma omp parallel for schedule(static) shared(progress,status) \
1453 magick_number_threads(image,image,image->rows,1)
1455 for (y=0; y < (ssize_t) image->rows; y++)
1468 if (status == MagickFalse)
1470 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1471 if (q == (Quantum *) NULL)
1476 for (x=0; x < (ssize_t) image->columns; x++)
1478 red=(double) GetPixelRed(image,q);
1479 green=(double) GetPixelGreen(image,q);
1480 blue=(double) GetPixelBlue(image,q);
1481 Contrast(sign,&red,&green,&blue);
1482 SetPixelRed(image,ClampToQuantum(red),q);
1483 SetPixelGreen(image,ClampToQuantum(green),q);
1484 SetPixelBlue(image,ClampToQuantum(blue),q);
1485 q+=(ptrdiff_t) GetPixelChannels(image);
1487 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1489 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1494#if defined(MAGICKCORE_OPENMP_SUPPORT)
1498 proceed=SetImageProgress(image,ContrastImageTag,progress,image->rows);
1499 if (proceed == MagickFalse)
1503 image_view=DestroyCacheView(image_view);
1544MagickExport MagickBooleanType ContrastStretchImage(Image *image,
1545 const double black_point,
const double white_point,ExceptionInfo *exception)
1547#define ContrastStretchImageTag "ContrastStretch/Image"
1553 property[MagickPathExtent];
1581 assert(image != (Image *) NULL);
1582 assert(image->signature == MagickCoreSignature);
1583 if (IsEventLogging() != MagickFalse)
1584 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1585 type=IdentifyImageType(image,exception);
1586 if (IsGrayImageType(type) != MagickFalse)
1587 (void) SetImageColorspace(image,GRAYColorspace,exception);
1588 black=(Quantum *) AcquireQuantumMemory(MaxPixelChannels,
sizeof(*black));
1589 white=(Quantum *) AcquireQuantumMemory(MaxPixelChannels,
sizeof(*white));
1590 stretch_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1591 sizeof(*stretch_map));
1592 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1593 sizeof(*histogram));
1594 if ((black == (Quantum *) NULL) || (white == (Quantum *) NULL) ||
1595 (stretch_map == (Quantum *) NULL) || (histogram == (
double *) NULL))
1597 if (histogram != (
double *) NULL)
1598 histogram=(
double *) RelinquishMagickMemory(histogram);
1599 if (stretch_map != (Quantum *) NULL)
1600 stretch_map=(Quantum *) RelinquishMagickMemory(stretch_map);
1601 if (white != (Quantum *) NULL)
1602 white=(Quantum *) RelinquishMagickMemory(white);
1603 if (black != (Quantum *) NULL)
1604 black=(Quantum *) RelinquishMagickMemory(black);
1605 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
1612 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1613 sizeof(*histogram));
1614 image_view=AcquireVirtualCacheView(image,exception);
1615 for (y=0; y < (ssize_t) image->rows; y++)
1623 if (status == MagickFalse)
1625 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1626 if (p == (
const Quantum *) NULL)
1631 for (x=0; x < (ssize_t) image->columns; x++)
1636 pixel=GetPixelIntensity(image,p);
1637 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1639 if (image->channel_mask != AllChannels)
1640 pixel=(double) p[i];
1641 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1642 ClampToQuantum(pixel))+(size_t) i]++;
1644 p+=(ptrdiff_t) GetPixelChannels(image);
1647 image_view=DestroyCacheView(image_view);
1651 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1659 black[i]=(Quantum) 0;
1660 white[i]=(Quantum) ScaleQuantumToMap(QuantumRange);
1662 for (j=0; j <= (ssize_t) MaxMap; j++)
1664 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
1665 if (intensity > black_point)
1668 black[i]=(Quantum) j;
1670 for (j=(ssize_t) MaxMap; j != 0; j--)
1672 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
1673 if (intensity > ((
double) image->columns*image->rows-white_point))
1676 white[i]=(Quantum) j;
1678 histogram=(
double *) RelinquishMagickMemory(histogram);
1682 (void) memset(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1683 sizeof(*stretch_map));
1684 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1689 for (j=0; j <= (ssize_t) MaxMap; j++)
1694 gamma=MagickSafeReciprocal(white[i]-black[i]);
1695 if (j < (ssize_t) black[i])
1696 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=(Quantum) 0;
1698 if (j > (ssize_t) white[i])
1699 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=QuantumRange;
1701 if (black[i] != white[i])
1702 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=
1703 ScaleMapToQuantum((
double) (MaxMap*gamma*(j-(
double) black[i])));
1706 if (image->storage_class == PseudoClass)
1714 for (j=0; j < (ssize_t) image->colors; j++)
1716 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1718 i=GetPixelChannelOffset(image,RedPixelChannel);
1719 image->colormap[j].red=(MagickRealType) stretch_map[
1720 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1721 image->colormap[j].red))+(size_t) i];
1723 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1725 i=GetPixelChannelOffset(image,GreenPixelChannel);
1726 image->colormap[j].green=(MagickRealType) stretch_map[
1727 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1728 image->colormap[j].green))+(size_t) i];
1730 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1732 i=GetPixelChannelOffset(image,BluePixelChannel);
1733 image->colormap[j].blue=(MagickRealType) stretch_map[
1734 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1735 image->colormap[j].blue))+(size_t) i];
1737 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1739 i=GetPixelChannelOffset(image,AlphaPixelChannel);
1740 image->colormap[j].alpha=(MagickRealType) stretch_map[
1741 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1742 image->colormap[j].alpha))+(size_t) i];
1751 image_view=AcquireAuthenticCacheView(image,exception);
1752#if defined(MAGICKCORE_OPENMP_SUPPORT)
1753 #pragma omp parallel for schedule(static) shared(progress,status) \
1754 magick_number_threads(image,image,image->rows,1)
1756 for (y=0; y < (ssize_t) image->rows; y++)
1764 if (status == MagickFalse)
1766 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1767 if (q == (Quantum *) NULL)
1772 for (x=0; x < (ssize_t) image->columns; x++)
1777 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1779 PixelChannel channel = GetPixelChannelChannel(image,j);
1780 PixelTrait traits = GetPixelChannelTraits(image,channel);
1781 if ((traits & UpdatePixelTrait) == 0)
1783 if (black[j] == white[j])
1785 q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1786 ScaleQuantumToMap(q[j])+(
size_t) j]);
1788 q+=(ptrdiff_t) GetPixelChannels(image);
1790 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1792 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1797#if defined(MAGICKCORE_OPENMP_SUPPORT)
1801 proceed=SetImageProgress(image,ContrastStretchImageTag,progress,
1803 if (proceed == MagickFalse)
1807 image_view=DestroyCacheView(image_view);
1808 (void) FormatLocaleString(property,MagickPathExtent,
"%gx%g%%",100.0*
1809 QuantumScale*GetPixelIntensity(image,black),100.0*QuantumScale*
1810 GetPixelIntensity(image,white));
1811 (void) SetImageProperty(image,
"histogram:contrast-stretch",property,
1813 white=(Quantum *) RelinquishMagickMemory(white);
1814 black=(Quantum *) RelinquishMagickMemory(black);
1815 stretch_map=(Quantum *) RelinquishMagickMemory(stretch_map);
1844MagickExport Image *EnhanceImage(
const Image *image,ExceptionInfo *exception)
1846#define EnhanceImageTag "Enhance/Image"
1847#define EnhancePixel(weight) \
1848 mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1849 distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1850 distance_squared=(4.0+mean)*distance*distance; \
1851 mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1852 distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1853 distance_squared+=(7.0-mean)*distance*distance; \
1854 mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1855 distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1856 distance_squared+=(5.0-mean)*distance*distance; \
1857 mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1858 distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1859 distance_squared+=(5.0-mean)*distance*distance; \
1860 mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1861 distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1862 distance_squared+=(5.0-mean)*distance*distance; \
1863 if (distance_squared < 0.069) \
1865 aggregate.red+=(weight)*(double) GetPixelRed(image,r); \
1866 aggregate.green+=(weight)*(double) GetPixelGreen(image,r); \
1867 aggregate.blue+=(weight)*(double) GetPixelBlue(image,r); \
1868 aggregate.black+=(weight)*(double) GetPixelBlack(image,r); \
1869 aggregate.alpha+=(weight)*(double) GetPixelAlpha(image,r); \
1870 total_weight+=(weight); \
1872 r+=(ptrdiff_t) GetPixelChannels(image);
1893 assert(image != (
const Image *) NULL);
1894 assert(image->signature == MagickCoreSignature);
1895 if (IsEventLogging() != MagickFalse)
1896 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1897 assert(exception != (ExceptionInfo *) NULL);
1898 assert(exception->signature == MagickCoreSignature);
1899 enhance_image=CloneImage(image,0,0,MagickTrue,
1901 if (enhance_image == (Image *) NULL)
1902 return((Image *) NULL);
1903 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1905 enhance_image=DestroyImage(enhance_image);
1906 return((Image *) NULL);
1913 image_view=AcquireVirtualCacheView(image,exception);
1914 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1915#if defined(MAGICKCORE_OPENMP_SUPPORT)
1916 #pragma omp parallel for schedule(static) shared(progress,status) \
1917 magick_number_threads(image,enhance_image,image->rows,1)
1919 for (y=0; y < (ssize_t) image->rows; y++)
1936 if (status == MagickFalse)
1938 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1939 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1941 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
1946 center=(ssize_t) GetPixelChannels(image)*(2*((ssize_t) image->columns+4)+2);
1947 GetPixelInfo(image,&pixel);
1948 for (x=0; x < (ssize_t) image->columns; x++)
1962 GetPixelInfo(image,&aggregate);
1964 GetPixelInfoPixel(image,p+center,&pixel);
1966 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1967 EnhancePixel(8.0); EnhancePixel(5.0);
1968 r=p+GetPixelChannels(image)*(image->columns+4);
1969 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1970 EnhancePixel(20.0); EnhancePixel(8.0);
1971 r=p+2*GetPixelChannels(image)*(image->columns+4);
1972 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1973 EnhancePixel(40.0); EnhancePixel(10.0);
1974 r=p+3*GetPixelChannels(image)*(image->columns+4);
1975 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1976 EnhancePixel(20.0); EnhancePixel(8.0);
1977 r=p+4*GetPixelChannels(image)*(image->columns+4);
1978 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1979 EnhancePixel(8.0); EnhancePixel(5.0);
1980 if (total_weight > MagickEpsilon)
1982 pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1983 pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1984 pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1985 pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1986 pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
1988 SetPixelViaPixelInfo(enhance_image,&pixel,q);
1989 p+=(ptrdiff_t) GetPixelChannels(image);
1990 q+=(ptrdiff_t) GetPixelChannels(enhance_image);
1992 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1994 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1999#if defined(MAGICKCORE_OPENMP_SUPPORT)
2003 proceed=SetImageProgress(image,EnhanceImageTag,progress,image->rows);
2004 if (proceed == MagickFalse)
2008 enhance_view=DestroyCacheView(enhance_view);
2009 image_view=DestroyCacheView(image_view);
2010 if (status == MagickFalse)
2011 enhance_image=DestroyImage(enhance_image);
2012 return(enhance_image);
2039MagickExport MagickBooleanType EqualizeImage(Image *image,
2040 ExceptionInfo *exception)
2042#define EqualizeImageTag "Equalize/Image"
2048 black[2*CompositePixelChannel+1],
2052 white[2*CompositePixelChannel+1];
2069 assert(image != (Image *) NULL);
2070 assert(image->signature == MagickCoreSignature);
2071#if defined(MAGICKCORE_OPENCL_SUPPORT)
2072 if (AccelerateEqualizeImage(image,exception) != MagickFalse)
2075 if (IsEventLogging() != MagickFalse)
2076 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2077 equalize_map=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2078 sizeof(*equalize_map));
2079 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2080 sizeof(*histogram));
2081 map=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
sizeof(*map));
2082 if ((equalize_map == (
double *) NULL) || (histogram == (
double *) NULL) ||
2083 (map == (
double *) NULL))
2085 if (map != (
double *) NULL)
2086 map=(
double *) RelinquishMagickMemory(map);
2087 if (histogram != (
double *) NULL)
2088 histogram=(
double *) RelinquishMagickMemory(histogram);
2089 if (equalize_map != (
double *) NULL)
2090 equalize_map=(
double *) RelinquishMagickMemory(equalize_map);
2091 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2098 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
2099 sizeof(*histogram));
2100 image_view=AcquireVirtualCacheView(image,exception);
2101 for (y=0; y < (ssize_t) image->rows; y++)
2109 if (status == MagickFalse)
2111 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2112 if (p == (
const Quantum *) NULL)
2117 for (x=0; x < (ssize_t) image->columns; x++)
2119 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2124 intensity=(double) p[i];
2125 if ((image->channel_mask & SyncChannels) != 0)
2126 intensity=GetPixelIntensity(image,p);
2127 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2128 ClampToQuantum(intensity))+(size_t) i]++;
2130 p+=(ptrdiff_t) GetPixelChannels(image);
2133 image_view=DestroyCacheView(image_view);
2137 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2146 for (j=0; j <= (ssize_t) MaxMap; j++)
2148 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
2149 map[(ssize_t) GetPixelChannels(image)*j+i]=intensity;
2152 (void) memset(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
2153 sizeof(*equalize_map));
2154 (void) memset(black,0,
sizeof(*black));
2155 (void) memset(white,0,
sizeof(*white));
2156 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2162 white[i]=map[GetPixelChannels(image)*MaxMap+(size_t) i];
2163 if (black[i] != white[i])
2164 for (j=0; j <= (ssize_t) MaxMap; j++)
2165 equalize_map[GetPixelChannels(image)*(size_t) j+(
size_t) i]=(double)
2166 ScaleMapToQuantum((
double) ((MaxMap*(map[GetPixelChannels(image)*
2167 (
size_t) j+(
size_t) i]-black[i]))/(white[i]-black[i])));
2169 histogram=(
double *) RelinquishMagickMemory(histogram);
2170 map=(
double *) RelinquishMagickMemory(map);
2171 if (image->storage_class == PseudoClass)
2179 for (j=0; j < (ssize_t) image->colors; j++)
2181 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2183 PixelChannel channel = GetPixelChannelChannel(image,
2185 if (black[channel] != white[channel])
2186 image->colormap[j].red=equalize_map[(ssize_t)
2187 GetPixelChannels(image)*ScaleQuantumToMap(
2188 ClampToQuantum(image->colormap[j].red))+channel];
2190 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2192 PixelChannel channel = GetPixelChannelChannel(image,
2194 if (black[channel] != white[channel])
2195 image->colormap[j].green=equalize_map[(ssize_t)
2196 GetPixelChannels(image)*ScaleQuantumToMap(
2197 ClampToQuantum(image->colormap[j].green))+channel];
2199 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2201 PixelChannel channel = GetPixelChannelChannel(image,
2203 if (black[channel] != white[channel])
2204 image->colormap[j].blue=equalize_map[(ssize_t)
2205 GetPixelChannels(image)*ScaleQuantumToMap(
2206 ClampToQuantum(image->colormap[j].blue))+channel];
2208 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2210 PixelChannel channel = GetPixelChannelChannel(image,
2212 if (black[channel] != white[channel])
2213 image->colormap[j].alpha=equalize_map[(ssize_t)
2214 GetPixelChannels(image)*ScaleQuantumToMap(
2215 ClampToQuantum(image->colormap[j].alpha))+channel];
2223 image_view=AcquireAuthenticCacheView(image,exception);
2224#if defined(MAGICKCORE_OPENMP_SUPPORT)
2225 #pragma omp parallel for schedule(static) shared(progress,status) \
2226 magick_number_threads(image,image,image->rows,1)
2228 for (y=0; y < (ssize_t) image->rows; y++)
2236 if (status == MagickFalse)
2238 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2239 if (q == (Quantum *) NULL)
2244 for (x=0; x < (ssize_t) image->columns; x++)
2249 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2251 PixelChannel channel = GetPixelChannelChannel(image,j);
2252 PixelTrait traits = GetPixelChannelTraits(image,channel);
2253 if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
2255 q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
2256 ScaleQuantumToMap(q[j])+(
size_t) j]);
2258 q+=(ptrdiff_t) GetPixelChannels(image);
2260 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2262 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2267#if defined(MAGICKCORE_OPENMP_SUPPORT)
2271 proceed=SetImageProgress(image,EqualizeImageTag,progress,image->rows);
2272 if (proceed == MagickFalse)
2276 image_view=DestroyCacheView(image_view);
2277 equalize_map=(
double *) RelinquishMagickMemory(equalize_map);
2316static inline double gamma_pow(
const double value,
const double gamma)
2318 return(value < 0.0 ? value : pow(value,gamma));
2321MagickExport MagickBooleanType GammaImage(Image *image,
const double gamma,
2322 ExceptionInfo *exception)
2324#define GammaImageTag "Gamma/Image"
2347 assert(image != (Image *) NULL);
2348 assert(image->signature == MagickCoreSignature);
2349 if (IsEventLogging() != MagickFalse)
2350 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2353 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*gamma_map));
2354 if (gamma_map == (Quantum *) NULL)
2355 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2357 (void) memset(gamma_map,0,(MaxMap+1)*
sizeof(*gamma_map));
2359 for (i=0; i <= (ssize_t) MaxMap; i++)
2360 gamma_map[i]=ScaleMapToQuantum((
double) (MaxMap*pow((
double) i/
2361 MaxMap,MagickSafeReciprocal(gamma))));
2362 if (image->storage_class == PseudoClass)
2363 for (i=0; i < (ssize_t) image->colors; i++)
2368 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2369 image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
2370 ClampToQuantum(image->colormap[i].red))];
2371 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2372 image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
2373 ClampToQuantum(image->colormap[i].green))];
2374 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2375 image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
2376 ClampToQuantum(image->colormap[i].blue))];
2377 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2378 image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
2379 ClampToQuantum(image->colormap[i].alpha))];
2386 image_view=AcquireAuthenticCacheView(image,exception);
2387#if defined(MAGICKCORE_OPENMP_SUPPORT)
2388 #pragma omp parallel for schedule(static) shared(progress,status) \
2389 magick_number_threads(image,image,image->rows,1)
2391 for (y=0; y < (ssize_t) image->rows; y++)
2399 if (status == MagickFalse)
2401 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2402 if (q == (Quantum *) NULL)
2407 for (x=0; x < (ssize_t) image->columns; x++)
2412 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2414 PixelChannel channel = GetPixelChannelChannel(image,j);
2415 PixelTrait traits = GetPixelChannelTraits(image,channel);
2416 if ((traits & UpdatePixelTrait) == 0)
2418 q[j]=gamma_map[ScaleQuantumToMap(ClampToQuantum((MagickRealType)
2421 q+=(ptrdiff_t) GetPixelChannels(image);
2423 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2425 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2430#if defined(MAGICKCORE_OPENMP_SUPPORT)
2434 proceed=SetImageProgress(image,GammaImageTag,progress,image->rows);
2435 if (proceed == MagickFalse)
2439 image_view=DestroyCacheView(image_view);
2440 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
2441 if (image->gamma != 0.0)
2442 image->gamma*=gamma;
2473MagickExport MagickBooleanType GrayscaleImage(Image *image,
2474 const PixelIntensityMethod method,ExceptionInfo *exception)
2476#define GrayscaleImageTag "Grayscale/Image"
2490 assert(image != (Image *) NULL);
2491 assert(image->signature == MagickCoreSignature);
2492 if (IsEventLogging() != MagickFalse)
2493 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2494 if (image->storage_class == PseudoClass)
2496 if (SyncImage(image,exception) == MagickFalse)
2497 return(MagickFalse);
2498 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2499 return(MagickFalse);
2501#if defined(MAGICKCORE_OPENCL_SUPPORT)
2502 if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
2504 image->intensity=method;
2505 image->type=GrayscaleType;
2506 if ((method == Rec601LuminancePixelIntensityMethod) ||
2507 (method == Rec709LuminancePixelIntensityMethod))
2508 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2509 return(SetImageColorspace(image,GRAYColorspace,exception));
2517 image_view=AcquireAuthenticCacheView(image,exception);
2518#if defined(MAGICKCORE_OPENMP_SUPPORT)
2519 #pragma omp parallel for schedule(static) shared(progress,status) \
2520 magick_number_threads(image,image,image->rows,1)
2522 for (y=0; y < (ssize_t) image->rows; y++)
2530 if (status == MagickFalse)
2532 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2533 if (q == (Quantum *) NULL)
2538 for (x=0; x < (ssize_t) image->columns; x++)
2546 red=(MagickRealType) GetPixelRed(image,q);
2547 green=(MagickRealType) GetPixelGreen(image,q);
2548 blue=(MagickRealType) GetPixelBlue(image,q);
2552 case AveragePixelIntensityMethod:
2554 intensity=(red+green+blue)/3.0;
2557 case BrightnessPixelIntensityMethod:
2559 intensity=MagickMax(MagickMax(red,green),blue);
2562 case LightnessPixelIntensityMethod:
2564 intensity=(MagickMin(MagickMin(red,green),blue)+
2565 MagickMax(MagickMax(red,green),blue))/2.0;
2568 case MSPixelIntensityMethod:
2570 intensity=(MagickRealType) (((
double) red*red+green*green+
2574 case Rec601LumaPixelIntensityMethod:
2576 if (image->colorspace == RGBColorspace)
2578 red=EncodePixelGamma(red);
2579 green=EncodePixelGamma(green);
2580 blue=EncodePixelGamma(blue);
2582 intensity=0.298839*red+0.586811*green+0.114350*blue;
2585 case Rec601LuminancePixelIntensityMethod:
2587 if (image->colorspace == sRGBColorspace)
2589 red=DecodePixelGamma(red);
2590 green=DecodePixelGamma(green);
2591 blue=DecodePixelGamma(blue);
2593 intensity=0.298839*red+0.586811*green+0.114350*blue;
2596 case Rec709LumaPixelIntensityMethod:
2599 if (image->colorspace == RGBColorspace)
2601 red=EncodePixelGamma(red);
2602 green=EncodePixelGamma(green);
2603 blue=EncodePixelGamma(blue);
2605 intensity=0.212656*red+0.715158*green+0.072186*blue;
2608 case Rec709LuminancePixelIntensityMethod:
2610 if (image->colorspace == sRGBColorspace)
2612 red=DecodePixelGamma(red);
2613 green=DecodePixelGamma(green);
2614 blue=DecodePixelGamma(blue);
2616 intensity=0.212656*red+0.715158*green+0.072186*blue;
2619 case RMSPixelIntensityMethod:
2621 intensity=(MagickRealType) (sqrt((
double) red*red+green*green+
2622 blue*blue)/sqrt(3.0));
2626 SetPixelGray(image,ClampToQuantum(intensity),q);
2627 q+=(ptrdiff_t) GetPixelChannels(image);
2629 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2631 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2636#if defined(MAGICKCORE_OPENMP_SUPPORT)
2640 proceed=SetImageProgress(image,GrayscaleImageTag,progress,image->rows);
2641 if (proceed == MagickFalse)
2645 image_view=DestroyCacheView(image_view);
2646 image->intensity=method;
2647 image->type=GrayscaleType;
2648 if ((method == Rec601LuminancePixelIntensityMethod) ||
2649 (method == Rec709LuminancePixelIntensityMethod))
2650 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2651 return(SetImageColorspace(image,GRAYColorspace,exception));
2685MagickExport MagickBooleanType HaldClutImage(Image *image,
2686 const Image *hald_image,ExceptionInfo *exception)
2688#define HaldClutImageTag "Clut/Image"
2690 typedef struct _HaldInfo
2722 assert(image != (Image *) NULL);
2723 assert(image->signature == MagickCoreSignature);
2724 if (IsEventLogging() != MagickFalse)
2725 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2726 assert(hald_image != (Image *) NULL);
2727 assert(hald_image->signature == MagickCoreSignature);
2728 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2729 return(MagickFalse);
2730 if ((image->alpha_trait & BlendPixelTrait) == 0)
2731 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2732 if (image->colorspace != hald_image->colorspace)
2733 (void) SetImageColorspace(image,hald_image->colorspace,exception);
2739 length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2740 (MagickRealType) hald_image->rows);
2741 for (level=2; (level*level*level) < length; level++) ;
2743 cube_size=level*level;
2744 width=(double) hald_image->columns;
2745 GetPixelInfo(hald_image,&zero);
2746 hald_view=AcquireVirtualCacheView(hald_image,exception);
2747 image_view=AcquireAuthenticCacheView(image,exception);
2748#if defined(MAGICKCORE_OPENMP_SUPPORT)
2749 #pragma omp parallel for schedule(static) shared(progress,status) \
2750 magick_number_threads(image,image,image->rows,1)
2752 for (y=0; y < (ssize_t) image->rows; y++)
2760 if (status == MagickFalse)
2762 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2763 if (q == (Quantum *) NULL)
2768 for (x=0; x < (ssize_t) image->columns; x++)
2775 point = { 0, 0, 0 };
2784 point.x=QuantumScale*(level-1.0)*(
double) GetPixelRed(image,q);
2785 point.y=QuantumScale*(level-1.0)*(
double) GetPixelGreen(image,q);
2786 point.z=QuantumScale*(level-1.0)*(
double) GetPixelBlue(image,q);
2787 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2788 point.x-=floor(point.x);
2789 point.y-=floor(point.y);
2790 point.z-=floor(point.z);
2791 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2792 fmod(offset,width),floor(offset/width),&pixel1,exception);
2793 if (status == MagickFalse)
2795 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2796 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2797 if (status == MagickFalse)
2800 if (hald_image->interpolate == NearestInterpolatePixel)
2801 area=(point.y < 0.5) ? 0.0 : 1.0;
2802 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2805 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2806 fmod(offset,width),floor(offset/width),&pixel1,exception);
2807 if (status == MagickFalse)
2809 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2810 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2811 if (status == MagickFalse)
2813 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2816 if (hald_image->interpolate == NearestInterpolatePixel)
2817 area=(point.z < 0.5)? 0.0 : 1.0;
2818 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2820 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2821 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2822 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2823 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2824 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2825 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2826 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2827 (image->colorspace == CMYKColorspace))
2828 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2829 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2830 (image->alpha_trait != UndefinedPixelTrait))
2831 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2832 q+=(ptrdiff_t) GetPixelChannels(image);
2834 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2836 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2841#if defined(MAGICKCORE_OPENMP_SUPPORT)
2845 proceed=SetImageProgress(image,HaldClutImageTag,progress,image->rows);
2846 if (proceed == MagickFalse)
2850 hald_view=DestroyCacheView(hald_view);
2851 image_view=DestroyCacheView(image_view);
2899static inline double LevelPixel(
const double black_point,
2900 const double white_point,
const double gamma,
const double pixel)
2906 scale=MagickSafeReciprocal(white_point-black_point);
2907 level_pixel=(double) QuantumRange*gamma_pow(scale*((
double) pixel-(
double)
2908 black_point),MagickSafeReciprocal(gamma));
2909 return(level_pixel);
2912MagickExport MagickBooleanType LevelImage(Image *image,
const double black_point,
2913 const double white_point,
const double gamma,ExceptionInfo *exception)
2915#define LevelImageTag "Level/Image"
2933 assert(image != (Image *) NULL);
2934 assert(image->signature == MagickCoreSignature);
2935 if (IsEventLogging() != MagickFalse)
2936 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2937 if (image->storage_class == PseudoClass)
2938 for (i=0; i < (ssize_t) image->colors; i++)
2943 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2944 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2945 white_point,gamma,image->colormap[i].red));
2946 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2947 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2948 white_point,gamma,image->colormap[i].green));
2949 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2950 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2951 white_point,gamma,image->colormap[i].blue));
2952 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2953 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2954 white_point,gamma,image->colormap[i].alpha));
2961 image_view=AcquireAuthenticCacheView(image,exception);
2962#if defined(MAGICKCORE_OPENMP_SUPPORT)
2963 #pragma omp parallel for schedule(static) shared(progress,status) \
2964 magick_number_threads(image,image,image->rows,1)
2966 for (y=0; y < (ssize_t) image->rows; y++)
2974 if (status == MagickFalse)
2976 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2977 if (q == (Quantum *) NULL)
2982 for (x=0; x < (ssize_t) image->columns; x++)
2987 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2989 PixelChannel channel = GetPixelChannelChannel(image,j);
2990 PixelTrait traits = GetPixelChannelTraits(image,channel);
2991 if ((traits & UpdatePixelTrait) == 0)
2993 q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2996 q+=(ptrdiff_t) GetPixelChannels(image);
2998 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3000 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3005#if defined(MAGICKCORE_OPENMP_SUPPORT)
3009 proceed=SetImageProgress(image,LevelImageTag,progress,image->rows);
3010 if (proceed == MagickFalse)
3014 image_view=DestroyCacheView(image_view);
3015 (void) ClampImage(image,exception);
3061MagickExport MagickBooleanType LevelizeImage(Image *image,
3062 const double black_point,
const double white_point,
const double gamma,
3063 ExceptionInfo *exception)
3065#define LevelizeImageTag "Levelize/Image"
3066#define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
3067 (QuantumScale*((double) x)),gamma))*(white_point-black_point)+black_point)
3087 assert(image != (Image *) NULL);
3088 assert(image->signature == MagickCoreSignature);
3089 if (IsEventLogging() != MagickFalse)
3090 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3091 if (image->storage_class == PseudoClass)
3092 for (i=0; i < (ssize_t) image->colors; i++)
3097 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3098 image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
3099 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3100 image->colormap[i].green=(double) LevelizeValue(
3101 image->colormap[i].green);
3102 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3103 image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
3104 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3105 image->colormap[i].alpha=(double) LevelizeValue(
3106 image->colormap[i].alpha);
3113 image_view=AcquireAuthenticCacheView(image,exception);
3114#if defined(MAGICKCORE_OPENMP_SUPPORT)
3115 #pragma omp parallel for schedule(static) shared(progress,status) \
3116 magick_number_threads(image,image,image->rows,1)
3118 for (y=0; y < (ssize_t) image->rows; y++)
3126 if (status == MagickFalse)
3128 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3129 if (q == (Quantum *) NULL)
3134 for (x=0; x < (ssize_t) image->columns; x++)
3139 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3141 PixelChannel channel = GetPixelChannelChannel(image,j);
3142 PixelTrait traits = GetPixelChannelTraits(image,channel);
3143 if ((traits & UpdatePixelTrait) == 0)
3145 q[j]=LevelizeValue(q[j]);
3147 q+=(ptrdiff_t) GetPixelChannels(image);
3149 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3151 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3156#if defined(MAGICKCORE_OPENMP_SUPPORT)
3160 proceed=SetImageProgress(image,LevelizeImageTag,progress,image->rows);
3161 if (proceed == MagickFalse)
3165 image_view=DestroyCacheView(image_view);
3210MagickExport MagickBooleanType LevelImageColors(Image *image,
3211 const PixelInfo *black_color,
const PixelInfo *white_color,
3212 const MagickBooleanType invert,ExceptionInfo *exception)
3223 assert(image != (Image *) NULL);
3224 assert(image->signature == MagickCoreSignature);
3225 if (IsEventLogging() != MagickFalse)
3226 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3227 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
3228 ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
3229 (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
3230 (void) SetImageColorspace(image,sRGBColorspace,exception);
3232 if (invert == MagickFalse)
3234 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3236 channel_mask=SetImageChannelMask(image,RedChannel);
3237 status&=(MagickStatusType) LevelImage(image,black_color->red,
3238 white_color->red,1.0,exception);
3239 (void) SetImageChannelMask(image,channel_mask);
3241 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3243 channel_mask=SetImageChannelMask(image,GreenChannel);
3244 status&=(MagickStatusType) LevelImage(image,black_color->green,
3245 white_color->green,1.0,exception);
3246 (void) SetImageChannelMask(image,channel_mask);
3248 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3250 channel_mask=SetImageChannelMask(image,BlueChannel);
3251 status&=(MagickStatusType) LevelImage(image,black_color->blue,
3252 white_color->blue,1.0,exception);
3253 (void) SetImageChannelMask(image,channel_mask);
3255 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3256 (image->colorspace == CMYKColorspace))
3258 channel_mask=SetImageChannelMask(image,BlackChannel);
3259 status&=(MagickStatusType) LevelImage(image,black_color->black,
3260 white_color->black,1.0,exception);
3261 (void) SetImageChannelMask(image,channel_mask);
3263 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3264 (image->alpha_trait != UndefinedPixelTrait))
3266 channel_mask=SetImageChannelMask(image,AlphaChannel);
3267 status&=(MagickStatusType) LevelImage(image,black_color->alpha,
3268 white_color->alpha,1.0,exception);
3269 (void) SetImageChannelMask(image,channel_mask);
3274 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3276 channel_mask=SetImageChannelMask(image,RedChannel);
3277 status&=(MagickStatusType) LevelizeImage(image,black_color->red,
3278 white_color->red,1.0,exception);
3279 (void) SetImageChannelMask(image,channel_mask);
3281 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3283 channel_mask=SetImageChannelMask(image,GreenChannel);
3284 status&=(MagickStatusType) LevelizeImage(image,black_color->green,
3285 white_color->green,1.0,exception);
3286 (void) SetImageChannelMask(image,channel_mask);
3288 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3290 channel_mask=SetImageChannelMask(image,BlueChannel);
3291 status&=(MagickStatusType) LevelizeImage(image,black_color->blue,
3292 white_color->blue,1.0,exception);
3293 (void) SetImageChannelMask(image,channel_mask);
3295 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3296 (image->colorspace == CMYKColorspace))
3298 channel_mask=SetImageChannelMask(image,BlackChannel);
3299 status&=(MagickStatusType) LevelizeImage(image,black_color->black,
3300 white_color->black,1.0,exception);
3301 (void) SetImageChannelMask(image,channel_mask);
3303 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3304 (image->alpha_trait != UndefinedPixelTrait))
3306 channel_mask=SetImageChannelMask(image,AlphaChannel);
3307 status&=(MagickStatusType) LevelizeImage(image,black_color->alpha,
3308 white_color->alpha,1.0,exception);
3309 (void) SetImageChannelMask(image,channel_mask);
3312 return(status != 0 ? MagickTrue : MagickFalse);
3346MagickExport MagickBooleanType LinearStretchImage(Image *image,
3347 const double black_point,
const double white_point,ExceptionInfo *exception)
3349#define LinearStretchImageTag "LinearStretch/Image"
3355 property[MagickPathExtent];
3372 assert(image != (Image *) NULL);
3373 assert(image->signature == MagickCoreSignature);
3374 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*histogram));
3375 if (histogram == (
double *) NULL)
3376 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3381 (void) memset(histogram,0,(MaxMap+1)*
sizeof(*histogram));
3382 image_view=AcquireVirtualCacheView(image,exception);
3383 for (y=0; y < (ssize_t) image->rows; y++)
3391 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3392 if (p == (
const Quantum *) NULL)
3394 for (x=0; x < (ssize_t) image->columns; x++)
3396 intensity=GetPixelIntensity(image,p);
3397 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
3398 p+=(ptrdiff_t) GetPixelChannels(image);
3401 image_view=DestroyCacheView(image_view);
3406 for (black=0; black < (ssize_t) MaxMap; black++)
3408 intensity+=histogram[black];
3409 if (intensity >= black_point)
3413 for (white=(ssize_t) MaxMap; white != 0; white--)
3415 intensity+=histogram[white];
3416 if (intensity >= white_point)
3419 histogram=(
double *) RelinquishMagickMemory(histogram);
3420 status=LevelImage(image,(
double) ScaleMapToQuantum((MagickRealType) black),
3421 (
double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
3422 (void) FormatLocaleString(property,MagickPathExtent,
"%gx%g%%",100.0*black/
3423 MaxMap,100.0*white/MaxMap);
3424 (void) SetImageProperty(image,
"histogram:linear-stretch",property,exception);
3460static inline void ModulateHCL(
const double percent_hue,
3461 const double percent_chroma,
const double percent_luma,
double *red,
3462 double *green,
double *blue)
3472 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
3473 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3474 chroma*=0.01*percent_chroma;
3475 luma*=0.01*percent_luma;
3476 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
3479static inline void ModulateHCLp(
const double percent_hue,
3480 const double percent_chroma,
const double percent_luma,
double *red,
3481 double *green,
double *blue)
3491 ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
3492 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3493 chroma*=0.01*percent_chroma;
3494 luma*=0.01*percent_luma;
3495 ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
3498static inline void ModulateHSB(
const double percent_hue,
3499 const double percent_saturation,
const double percent_brightness,
double *red,
3500 double *green,
double *blue)
3510 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
3511 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3512 saturation*=0.01*percent_saturation;
3513 brightness*=0.01*percent_brightness;
3514 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3517static inline void ModulateHSI(
const double percent_hue,
3518 const double percent_saturation,
const double percent_intensity,
double *red,
3519 double *green,
double *blue)
3529 ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3530 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3531 saturation*=0.01*percent_saturation;
3532 intensity*=0.01*percent_intensity;
3533 ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3536static inline void ModulateHSL(
const double percent_hue,
3537 const double percent_saturation,
const double percent_lightness,
double *red,
3538 double *green,
double *blue)
3548 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3549 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3550 saturation*=0.01*percent_saturation;
3551 lightness*=0.01*percent_lightness;
3552 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3555static inline void ModulateHSV(
const double percent_hue,
3556 const double percent_saturation,
const double percent_value,
double *red,
3557 double *green,
double *blue)
3567 ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3568 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3569 saturation*=0.01*percent_saturation;
3570 value*=0.01*percent_value;
3571 ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3574static inline void ModulateHWB(
const double percent_hue,
3575 const double percent_whiteness,
const double percent_blackness,
double *red,
3576 double *green,
double *blue)
3586 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3587 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3588 blackness*=0.01*percent_blackness;
3589 whiteness*=0.01*percent_whiteness;
3590 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3593static inline void ModulateLCHab(
const double percent_luma,
3594 const double percent_chroma,
const double percent_hue,
3595 const IlluminantType illuminant,
double *red,
double *green,
double *blue)
3605 ConvertRGBToLCHab(*red,*green,*blue,illuminant,&luma,&chroma,&hue);
3606 luma*=0.01*percent_luma;
3607 chroma*=0.01*percent_chroma;
3608 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3609 ConvertLCHabToRGB(luma,chroma,hue,illuminant,red,green,blue);
3612static inline void ModulateLCHuv(
const double percent_luma,
3613 const double percent_chroma,
const double percent_hue,
3614 const IlluminantType illuminant,
double *red,
double *green,
double *blue)
3624 ConvertRGBToLCHuv(*red,*green,*blue,illuminant,&luma,&chroma,&hue);
3625 luma*=0.01*percent_luma;
3626 chroma*=0.01*percent_chroma;
3627 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3628 ConvertLCHuvToRGB(luma,chroma,hue,illuminant,red,green,blue);
3631MagickExport MagickBooleanType ModulateImage(Image *image,
const char *modulate,
3632 ExceptionInfo *exception)
3634#define ModulateImageTag "Modulate/Image"
3640 colorspace = UndefinedColorspace;
3646 percent_brightness = 100.0,
3647 percent_hue = 100.0,
3648 percent_saturation = 100.0;
3654 illuminant = D65Illuminant;
3674 assert(image != (Image *) NULL);
3675 assert(image->signature == MagickCoreSignature);
3676 if (IsEventLogging() != MagickFalse)
3677 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3678 if (modulate == (
char *) NULL)
3679 return(MagickFalse);
3680 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
3681 (void) SetImageColorspace(image,sRGBColorspace,exception);
3682 flags=ParseGeometry(modulate,&geometry_info);
3683 if ((flags & RhoValue) != 0)
3684 percent_brightness=geometry_info.rho;
3685 if ((flags & SigmaValue) != 0)
3686 percent_saturation=geometry_info.sigma;
3687 if ((flags & XiValue) != 0)
3688 percent_hue=geometry_info.xi;
3689 artifact=GetImageArtifact(image,
"modulate:colorspace");
3690 if (artifact != (
const char *) NULL)
3691 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3692 MagickFalse,artifact);
3693 artifact=GetImageArtifact(image,
"color:illuminant");
3694 if (artifact != (
const char *) NULL)
3699 illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
3701 if (illuminant_type < 0)
3703 illuminant=UndefinedIlluminant;
3704 colorspace=UndefinedColorspace;
3707 illuminant=(IlluminantType) illuminant_type;
3709 if (image->storage_class == PseudoClass)
3710 for (i=0; i < (ssize_t) image->colors; i++)
3720 red=(double) image->colormap[i].red;
3721 green=(double) image->colormap[i].green;
3722 blue=(double) image->colormap[i].blue;
3727 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3731 case HCLpColorspace:
3733 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3739 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3745 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3752 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3758 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3764 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3769 case LCHabColorspace:
3771 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3772 illuminant,&red,&green,&blue);
3775 case LCHuvColorspace:
3777 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3778 illuminant,&red,&green,&blue);
3782 image->colormap[i].red=red;
3783 image->colormap[i].green=green;
3784 image->colormap[i].blue=blue;
3789#if defined(MAGICKCORE_OPENCL_SUPPORT)
3790 if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3791 percent_saturation,colorspace,exception) != MagickFalse)
3796 image_view=AcquireAuthenticCacheView(image,exception);
3797#if defined(MAGICKCORE_OPENMP_SUPPORT)
3798 #pragma omp parallel for schedule(static) shared(progress,status) \
3799 magick_number_threads(image,image,image->rows,1)
3801 for (y=0; y < (ssize_t) image->rows; y++)
3809 if (status == MagickFalse)
3811 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3812 if (q == (Quantum *) NULL)
3817 for (x=0; x < (ssize_t) image->columns; x++)
3824 red=(double) GetPixelRed(image,q);
3825 green=(double) GetPixelGreen(image,q);
3826 blue=(double) GetPixelBlue(image,q);
3831 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3835 case HCLpColorspace:
3837 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3843 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3849 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3856 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3862 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3868 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3873 case LCHabColorspace:
3875 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3876 illuminant,&red,&green,&blue);
3879 case LCHuvColorspace:
3881 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3882 illuminant,&red,&green,&blue);
3886 SetPixelRed(image,ClampToQuantum(red),q);
3887 SetPixelGreen(image,ClampToQuantum(green),q);
3888 SetPixelBlue(image,ClampToQuantum(blue),q);
3889 q+=(ptrdiff_t) GetPixelChannels(image);
3891 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3893 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3898#if defined(MAGICKCORE_OPENMP_SUPPORT)
3902 proceed=SetImageProgress(image,ModulateImageTag,progress,image->rows);
3903 if (proceed == MagickFalse)
3907 image_view=DestroyCacheView(image_view);
3939MagickExport MagickBooleanType NegateImage(Image *image,
3940 const MagickBooleanType grayscale,ExceptionInfo *exception)
3942#define NegateImageTag "Negate/Image"
3959 assert(image != (Image *) NULL);
3960 assert(image->signature == MagickCoreSignature);
3961 if (IsEventLogging() != MagickFalse)
3962 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3963 if (image->storage_class == PseudoClass)
3964 for (i=0; i < (ssize_t) image->colors; i++)
3969 if (grayscale != MagickFalse)
3970 if ((image->colormap[i].red != image->colormap[i].green) ||
3971 (image->colormap[i].green != image->colormap[i].blue))
3973 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3974 image->colormap[i].red=(double) QuantumRange-image->colormap[i].red;
3975 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3976 image->colormap[i].green=(double) QuantumRange-image->colormap[i].green;
3977 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3978 image->colormap[i].blue=(double) QuantumRange-image->colormap[i].blue;
3985 image_view=AcquireAuthenticCacheView(image,exception);
3986 if( grayscale != MagickFalse )
3988 for (y=0; y < (ssize_t) image->rows; y++)
3999 if (status == MagickFalse)
4001 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4003 if (q == (Quantum *) NULL)
4008 for (x=0; x < (ssize_t) image->columns; x++)
4013 if (IsPixelGray(image,q) == MagickFalse)
4015 q+=(ptrdiff_t) GetPixelChannels(image);
4018 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4020 PixelChannel channel = GetPixelChannelChannel(image,j);
4021 PixelTrait traits = GetPixelChannelTraits(image,channel);
4022 if ((traits & UpdatePixelTrait) == 0)
4024 q[j]=QuantumRange-q[j];
4026 q+=(ptrdiff_t) GetPixelChannels(image);
4028 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4029 if (sync == MagickFalse)
4031 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4037 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4038 if (proceed == MagickFalse)
4042 image_view=DestroyCacheView(image_view);
4048#if defined(MAGICKCORE_OPENMP_SUPPORT)
4049 #pragma omp parallel for schedule(static) shared(progress,status) \
4050 magick_number_threads(image,image,image->rows,1)
4052 for (y=0; y < (ssize_t) image->rows; y++)
4060 if (status == MagickFalse)
4062 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4063 if (q == (Quantum *) NULL)
4068 for (x=0; x < (ssize_t) image->columns; x++)
4073 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4075 PixelChannel channel = GetPixelChannelChannel(image,j);
4076 PixelTrait traits = GetPixelChannelTraits(image,channel);
4077 if ((traits & UpdatePixelTrait) == 0)
4079 q[j]=QuantumRange-q[j];
4081 q+=(ptrdiff_t) GetPixelChannels(image);
4083 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4085 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4090#if defined(MAGICKCORE_OPENMP_SUPPORT)
4094 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4095 if (proceed == MagickFalse)
4099 image_view=DestroyCacheView(image_view);
4129MagickExport MagickBooleanType NormalizeImage(Image *image,
4130 ExceptionInfo *exception)
4136 black_point=0.02*image->columns*image->rows;
4137 white_point=0.99*image->columns*image->rows;
4138 return(ContrastStretchImage(image,black_point,white_point,exception));
4205#if defined(MAGICKCORE_HAVE_ATANH)
4206#define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
4208#define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
4227#define ScaledSigmoidal(a,b,x) ( \
4228 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
4229 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
4239static inline double InverseScaledSigmoidal(
const double a,
const double b,
4242 const double sig0=Sigmoidal(a,b,0.0);
4243 const double sig1=Sigmoidal(a,b,1.0);
4244 const double argument=(sig1-sig0)*x+sig0;
4245 const double clamped=
4247#if defined(MAGICKCORE_HAVE_ATANH)
4248 argument < -1+MagickEpsilon
4252 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4254 return(b+(2.0/a)*atanh(clamped));
4256 argument < MagickEpsilon
4260 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4262 return(b-log(1.0/clamped-1.0)/a);
4266MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
4267 const MagickBooleanType sharpen,
const double contrast,
const double midpoint,
4268 ExceptionInfo *exception)
4270#define SigmoidalContrastImageTag "SigmoidalContrast/Image"
4271#define ScaledSig(x) (ClampToQuantum((double) QuantumRange* \
4272 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*((double) x))) )
4273#define InverseScaledSig(x) (ClampToQuantum((double) QuantumRange* \
4274 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale* \
4292 assert(image != (Image *) NULL);
4293 assert(image->signature == MagickCoreSignature);
4294 if (IsEventLogging() != MagickFalse)
4295 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4300 if (contrast < MagickEpsilon)
4305 if (image->storage_class == PseudoClass)
4310 if( sharpen != MagickFalse )
4311 for (i=0; i < (ssize_t) image->colors; i++)
4313 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4314 image->colormap[i].red=(MagickRealType) ScaledSig(
4315 image->colormap[i].red);
4316 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4317 image->colormap[i].green=(MagickRealType) ScaledSig(
4318 image->colormap[i].green);
4319 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4320 image->colormap[i].blue=(MagickRealType) ScaledSig(
4321 image->colormap[i].blue);
4322 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4323 image->colormap[i].alpha=(MagickRealType) ScaledSig(
4324 image->colormap[i].alpha);
4327 for (i=0; i < (ssize_t) image->colors; i++)
4329 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4330 image->colormap[i].red=(MagickRealType) InverseScaledSig(
4331 image->colormap[i].red);
4332 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4333 image->colormap[i].green=(MagickRealType) InverseScaledSig(
4334 image->colormap[i].green);
4335 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4336 image->colormap[i].blue=(MagickRealType) InverseScaledSig(
4337 image->colormap[i].blue);
4338 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4339 image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
4340 image->colormap[i].alpha);
4348 image_view=AcquireAuthenticCacheView(image,exception);
4349#if defined(MAGICKCORE_OPENMP_SUPPORT)
4350 #pragma omp parallel for schedule(static) shared(progress,status) \
4351 magick_number_threads(image,image,image->rows,1)
4353 for (y=0; y < (ssize_t) image->rows; y++)
4361 if (status == MagickFalse)
4363 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4364 if (q == (Quantum *) NULL)
4369 for (x=0; x < (ssize_t) image->columns; x++)
4374 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4376 PixelChannel channel = GetPixelChannelChannel(image,i);
4377 PixelTrait traits = GetPixelChannelTraits(image,channel);
4378 if ((traits & UpdatePixelTrait) == 0)
4380 if( sharpen != MagickFalse )
4381 q[i]=ScaledSig(q[i]);
4383 q[i]=InverseScaledSig(q[i]);
4385 q+=(ptrdiff_t) GetPixelChannels(image);
4387 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4389 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4394#if defined(MAGICKCORE_OPENMP_SUPPORT)
4398 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress,
4400 if (proceed == MagickFalse)
4404 image_view=DestroyCacheView(image_view);
4434MagickExport MagickBooleanType WhiteBalanceImage(Image *image,
4435 ExceptionInfo *exception)
4437#define WhiteBalanceImageTag "WhiteBalance/Image"
4461 assert(image != (Image *) NULL);
4462 assert(image->signature == MagickCoreSignature);
4463 if (IsEventLogging() != MagickFalse)
4464 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4465 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4466 return(MagickFalse);
4467 status=TransformImageColorspace(image,LabColorspace,exception);
4470 image_view=AcquireAuthenticCacheView(image,exception);
4471 for (y=0; y < (ssize_t) image->rows; y++)
4479 if (status == MagickFalse)
4481 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4482 if (p == (Quantum *) NULL)
4487 for (x=0; x < (ssize_t) image->columns; x++)
4489 a_mean+=QuantumScale*(double) GetPixela(image,p)-0.5;
4490 b_mean+=QuantumScale*(double) GetPixelb(image,p)-0.5;
4491 p+=(ptrdiff_t) GetPixelChannels(image);
4494 a_mean/=((double) image->columns*image->rows);
4495 b_mean/=((double) image->columns*image->rows);
4497#if defined(MAGICKCORE_OPENMP_SUPPORT)
4498 #pragma omp parallel for schedule(static) shared(progress,status) \
4499 magick_number_threads(image,image,image->rows,1)
4501 for (y=0; y < (ssize_t) image->rows; y++)
4509 if (status == MagickFalse)
4511 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4512 if (q == (Quantum *) NULL)
4517 for (x=0; x < (ssize_t) image->columns; x++)
4526 a=(double) GetPixela(image,q)-1.1*(double) GetPixelL(image,q)*a_mean;
4527 b=(double) GetPixelb(image,q)-1.1*(double) GetPixelL(image,q)*b_mean;
4528 SetPixela(image,ClampToQuantum(a),q);
4529 SetPixelb(image,ClampToQuantum(b),q);
4530 q+=(ptrdiff_t) GetPixelChannels(image);
4532 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4534 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4539#if defined(MAGICKCORE_OPENMP_SUPPORT)
4543 proceed=SetImageProgress(image,WhiteBalanceImageTag,progress,image->rows);
4544 if (proceed == MagickFalse)
4548 image_view=DestroyCacheView(image_view);
4549 artifact=GetImageArtifact(image,
"white-balance:vibrance");
4550 if (artifact != (
const char *) NULL)
4567 flags=ParseGeometry(artifact,&geometry_info);
4568 if ((flags & RhoValue) != 0)
4569 black_point=geometry_info.rho;
4570 if ((flags & PercentValue) != 0)
4571 black_point*=((double) QuantumRange/100.0);
4572 channel_mask=SetImageChannelMask(image,(ChannelType) (aChannel |
4574 status&=(MagickStatusType) LevelImage(image,black_point,(
double)
4575 QuantumRange-black_point,1.0,exception);
4576 (void) SetImageChannelMask(image,channel_mask);
4578 status&=(MagickStatusType) TransformImageColorspace(image,sRGBColorspace,
4580 return(status != 0 ? MagickTrue : MagickFalse);