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,
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,
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*PerceptibleReciprocal(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,
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) (PerceptibleReciprocal((
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,
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));
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,
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*
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));
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);
1079 return(MagickFalse);
1080 cc=GetXMLTreeChild(ccc,
"ColorCorrection");
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");
1104 slope=GetXMLTreeChild(sop,
"Slope");
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");
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");
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");
1208 saturation=GetXMLTreeChild(sat,
"Saturation");
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));
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,
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 MaxRange(color) ((double) ScaleQuantumToMap((Quantum) (color)))
1548#define ContrastStretchImageTag "ContrastStretch/Image"
1554 property[MagickPathExtent];
1582 assert(image != (
Image *) NULL);
1583 assert(image->signature == MagickCoreSignature);
1584 if (IsEventLogging() != MagickFalse)
1585 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1586 type=IdentifyImageType(image,exception);
1587 if (IsGrayImageType(type) != MagickFalse)
1588 (void) SetImageColorspace(image,GRAYColorspace,exception);
1589 black=(Quantum *) AcquireQuantumMemory(MaxPixelChannels,
sizeof(*black));
1590 white=(Quantum *) AcquireQuantumMemory(MaxPixelChannels,
sizeof(*white));
1591 stretch_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1592 sizeof(*stretch_map));
1593 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1594 sizeof(*histogram));
1595 if ((black == (Quantum *) NULL) || (white == (Quantum *) NULL) ||
1596 (stretch_map == (Quantum *) NULL) || (histogram == (
double *) NULL))
1598 if (histogram != (
double *) NULL)
1599 histogram=(
double *) RelinquishMagickMemory(histogram);
1600 if (stretch_map != (Quantum *) NULL)
1601 stretch_map=(Quantum *) RelinquishMagickMemory(stretch_map);
1602 if (white != (Quantum *) NULL)
1603 white=(Quantum *) RelinquishMagickMemory(white);
1604 if (black != (Quantum *) NULL)
1605 black=(Quantum *) RelinquishMagickMemory(black);
1606 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
1613 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1614 sizeof(*histogram));
1615 image_view=AcquireVirtualCacheView(image,exception);
1616 for (y=0; y < (ssize_t) image->rows; y++)
1624 if (status == MagickFalse)
1626 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1627 if (p == (
const Quantum *) NULL)
1632 for (x=0; x < (ssize_t) image->columns; x++)
1637 pixel=GetPixelIntensity(image,p);
1638 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1640 if (image->channel_mask != AllChannels)
1641 pixel=(double) p[i];
1642 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1643 ClampToQuantum(pixel))+(size_t) i]++;
1645 p+=(ptrdiff_t) GetPixelChannels(image);
1648 image_view=DestroyCacheView(image_view);
1652 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1661 white[i]=MaxRange(QuantumRange);
1663 for (j=0; j <= (ssize_t) MaxMap; j++)
1665 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
1666 if (intensity > black_point)
1669 black[i]=(Quantum) j;
1671 for (j=(ssize_t) MaxMap; j != 0; j--)
1673 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
1674 if (intensity > ((
double) image->columns*image->rows-white_point))
1677 white[i]=(Quantum) j;
1679 histogram=(
double *) RelinquishMagickMemory(histogram);
1683 (void) memset(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1684 sizeof(*stretch_map));
1685 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1690 for (j=0; j <= (ssize_t) MaxMap; j++)
1695 gamma=PerceptibleReciprocal(white[i]-black[i]);
1696 if (j < (ssize_t) black[i])
1697 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=0.0;
1699 if (j > (ssize_t) white[i])
1700 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=QuantumRange;
1702 if (black[i] != white[i])
1703 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=
1704 ScaleMapToQuantum((
double) (MaxMap*gamma*(j-(
double) black[i])));
1707 if (image->storage_class == PseudoClass)
1715 for (j=0; j < (ssize_t) image->colors; j++)
1717 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1719 i=GetPixelChannelOffset(image,RedPixelChannel);
1720 image->colormap[j].red=(MagickRealType) stretch_map[
1721 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1722 image->colormap[j].red))+(size_t) i];
1724 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1726 i=GetPixelChannelOffset(image,GreenPixelChannel);
1727 image->colormap[j].green=(MagickRealType) stretch_map[
1728 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1729 image->colormap[j].green))+(size_t) i];
1731 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1733 i=GetPixelChannelOffset(image,BluePixelChannel);
1734 image->colormap[j].blue=(MagickRealType) stretch_map[
1735 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1736 image->colormap[j].blue))+(size_t) i];
1738 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1740 i=GetPixelChannelOffset(image,AlphaPixelChannel);
1741 image->colormap[j].alpha=(MagickRealType) stretch_map[
1742 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1743 image->colormap[j].alpha))+(size_t) i];
1752 image_view=AcquireAuthenticCacheView(image,exception);
1753#if defined(MAGICKCORE_OPENMP_SUPPORT)
1754 #pragma omp parallel for schedule(static) shared(progress,status) \
1755 magick_number_threads(image,image,image->rows,1)
1757 for (y=0; y < (ssize_t) image->rows; y++)
1765 if (status == MagickFalse)
1767 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1768 if (q == (Quantum *) NULL)
1773 for (x=0; x < (ssize_t) image->columns; x++)
1778 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1780 PixelChannel channel = GetPixelChannelChannel(image,j);
1781 PixelTrait traits = GetPixelChannelTraits(image,channel);
1782 if ((traits & UpdatePixelTrait) == 0)
1784 if (black[j] == white[j])
1786 q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1787 ScaleQuantumToMap(q[j])+(
size_t) j]);
1789 q+=(ptrdiff_t) GetPixelChannels(image);
1791 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1793 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1798#if defined(MAGICKCORE_OPENMP_SUPPORT)
1802 proceed=SetImageProgress(image,ContrastStretchImageTag,progress,
1804 if (proceed == MagickFalse)
1808 image_view=DestroyCacheView(image_view);
1809 (void) FormatLocaleString(property,MagickPathExtent,
"%gx%g%%",100.0*
1810 QuantumScale*GetPixelIntensity(image,black),100.0*QuantumScale*
1811 GetPixelIntensity(image,white));
1812 (void) SetImageProperty(image,
"histogram:contrast-stretch",property,
1814 white=(Quantum *) RelinquishMagickMemory(white);
1815 black=(Quantum *) RelinquishMagickMemory(black);
1816 stretch_map=(Quantum *) RelinquishMagickMemory(stretch_map);
1847#define EnhanceImageTag "Enhance/Image"
1848#define EnhancePixel(weight) \
1849 mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1850 distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1851 distance_squared=(4.0+mean)*distance*distance; \
1852 mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1853 distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1854 distance_squared+=(7.0-mean)*distance*distance; \
1855 mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1856 distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1857 distance_squared+=(5.0-mean)*distance*distance; \
1858 mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1859 distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1860 distance_squared+=(5.0-mean)*distance*distance; \
1861 mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1862 distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1863 distance_squared+=(5.0-mean)*distance*distance; \
1864 if (distance_squared < 0.069) \
1866 aggregate.red+=(weight)*(double) GetPixelRed(image,r); \
1867 aggregate.green+=(weight)*(double) GetPixelGreen(image,r); \
1868 aggregate.blue+=(weight)*(double) GetPixelBlue(image,r); \
1869 aggregate.black+=(weight)*(double) GetPixelBlack(image,r); \
1870 aggregate.alpha+=(weight)*(double) GetPixelAlpha(image,r); \
1871 total_weight+=(weight); \
1873 r+=(ptrdiff_t) GetPixelChannels(image);
1894 assert(image != (
const Image *) NULL);
1895 assert(image->signature == MagickCoreSignature);
1896 if (IsEventLogging() != MagickFalse)
1897 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1899 assert(exception->signature == MagickCoreSignature);
1900 enhance_image=CloneImage(image,0,0,MagickTrue,
1902 if (enhance_image == (
Image *) NULL)
1903 return((
Image *) NULL);
1904 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1906 enhance_image=DestroyImage(enhance_image);
1907 return((
Image *) NULL);
1914 image_view=AcquireVirtualCacheView(image,exception);
1915 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1916#if defined(MAGICKCORE_OPENMP_SUPPORT)
1917 #pragma omp parallel for schedule(static) shared(progress,status) \
1918 magick_number_threads(image,enhance_image,image->rows,1)
1920 for (y=0; y < (ssize_t) image->rows; y++)
1937 if (status == MagickFalse)
1939 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1940 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1942 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
1947 center=(ssize_t) GetPixelChannels(image)*(2*((ssize_t) image->columns+4)+2);
1948 GetPixelInfo(image,&pixel);
1949 for (x=0; x < (ssize_t) image->columns; x++)
1963 GetPixelInfo(image,&aggregate);
1965 GetPixelInfoPixel(image,p+center,&pixel);
1967 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1968 EnhancePixel(8.0); EnhancePixel(5.0);
1969 r=p+GetPixelChannels(image)*(image->columns+4);
1970 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1971 EnhancePixel(20.0); EnhancePixel(8.0);
1972 r=p+2*GetPixelChannels(image)*(image->columns+4);
1973 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1974 EnhancePixel(40.0); EnhancePixel(10.0);
1975 r=p+3*GetPixelChannels(image)*(image->columns+4);
1976 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1977 EnhancePixel(20.0); EnhancePixel(8.0);
1978 r=p+4*GetPixelChannels(image)*(image->columns+4);
1979 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1980 EnhancePixel(8.0); EnhancePixel(5.0);
1981 if (total_weight > MagickEpsilon)
1983 pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1984 pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1985 pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1986 pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1987 pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
1989 SetPixelViaPixelInfo(enhance_image,&pixel,q);
1990 p+=(ptrdiff_t) GetPixelChannels(image);
1991 q+=(ptrdiff_t) GetPixelChannels(enhance_image);
1993 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1995 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2000#if defined(MAGICKCORE_OPENMP_SUPPORT)
2004 proceed=SetImageProgress(image,EnhanceImageTag,progress,image->rows);
2005 if (proceed == MagickFalse)
2009 enhance_view=DestroyCacheView(enhance_view);
2010 image_view=DestroyCacheView(image_view);
2011 if (status == MagickFalse)
2012 enhance_image=DestroyImage(enhance_image);
2013 return(enhance_image);
2040MagickExport MagickBooleanType EqualizeImage(
Image *image,
2043#define EqualizeImageTag "Equalize/Image"
2049 black[2*CompositePixelChannel+1],
2053 white[2*CompositePixelChannel+1];
2070 assert(image != (
Image *) NULL);
2071 assert(image->signature == MagickCoreSignature);
2072#if defined(MAGICKCORE_OPENCL_SUPPORT)
2073 if (AccelerateEqualizeImage(image,exception) != MagickFalse)
2076 if (IsEventLogging() != MagickFalse)
2077 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2078 equalize_map=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2079 sizeof(*equalize_map));
2080 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2081 sizeof(*histogram));
2082 map=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
sizeof(*map));
2083 if ((equalize_map == (
double *) NULL) || (histogram == (
double *) NULL) ||
2084 (map == (
double *) NULL))
2086 if (map != (
double *) NULL)
2087 map=(
double *) RelinquishMagickMemory(map);
2088 if (histogram != (
double *) NULL)
2089 histogram=(
double *) RelinquishMagickMemory(histogram);
2090 if (equalize_map != (
double *) NULL)
2091 equalize_map=(
double *) RelinquishMagickMemory(equalize_map);
2092 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2099 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
2100 sizeof(*histogram));
2101 image_view=AcquireVirtualCacheView(image,exception);
2102 for (y=0; y < (ssize_t) image->rows; y++)
2110 if (status == MagickFalse)
2112 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2113 if (p == (
const Quantum *) NULL)
2118 for (x=0; x < (ssize_t) image->columns; x++)
2120 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2125 intensity=(double) p[i];
2126 if ((image->channel_mask & SyncChannels) != 0)
2127 intensity=GetPixelIntensity(image,p);
2128 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2129 ClampToQuantum(intensity))+(size_t) i]++;
2131 p+=(ptrdiff_t) GetPixelChannels(image);
2134 image_view=DestroyCacheView(image_view);
2138 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2147 for (j=0; j <= (ssize_t) MaxMap; j++)
2149 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
2150 map[(ssize_t) GetPixelChannels(image)*j+i]=intensity;
2153 (void) memset(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
2154 sizeof(*equalize_map));
2155 (void) memset(black,0,
sizeof(*black));
2156 (void) memset(white,0,
sizeof(*white));
2157 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2163 white[i]=map[GetPixelChannels(image)*MaxMap+(size_t) i];
2164 if (black[i] != white[i])
2165 for (j=0; j <= (ssize_t) MaxMap; j++)
2166 equalize_map[GetPixelChannels(image)*(size_t) j+(
size_t) i]=(double)
2167 ScaleMapToQuantum((
double) ((MaxMap*(map[GetPixelChannels(image)*
2168 (
size_t) j+(
size_t) i]-black[i]))/(white[i]-black[i])));
2170 histogram=(
double *) RelinquishMagickMemory(histogram);
2171 map=(
double *) RelinquishMagickMemory(map);
2172 if (image->storage_class == PseudoClass)
2180 for (j=0; j < (ssize_t) image->colors; j++)
2182 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2184 PixelChannel channel = GetPixelChannelChannel(image,
2186 if (black[channel] != white[channel])
2187 image->colormap[j].red=equalize_map[(ssize_t)
2188 GetPixelChannels(image)*ScaleQuantumToMap(
2189 ClampToQuantum(image->colormap[j].red))+channel];
2191 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2193 PixelChannel channel = GetPixelChannelChannel(image,
2195 if (black[channel] != white[channel])
2196 image->colormap[j].green=equalize_map[(ssize_t)
2197 GetPixelChannels(image)*ScaleQuantumToMap(
2198 ClampToQuantum(image->colormap[j].green))+channel];
2200 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2202 PixelChannel channel = GetPixelChannelChannel(image,
2204 if (black[channel] != white[channel])
2205 image->colormap[j].blue=equalize_map[(ssize_t)
2206 GetPixelChannels(image)*ScaleQuantumToMap(
2207 ClampToQuantum(image->colormap[j].blue))+channel];
2209 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2211 PixelChannel channel = GetPixelChannelChannel(image,
2213 if (black[channel] != white[channel])
2214 image->colormap[j].alpha=equalize_map[(ssize_t)
2215 GetPixelChannels(image)*ScaleQuantumToMap(
2216 ClampToQuantum(image->colormap[j].alpha))+channel];
2224 image_view=AcquireAuthenticCacheView(image,exception);
2225#if defined(MAGICKCORE_OPENMP_SUPPORT)
2226 #pragma omp parallel for schedule(static) shared(progress,status) \
2227 magick_number_threads(image,image,image->rows,1)
2229 for (y=0; y < (ssize_t) image->rows; y++)
2237 if (status == MagickFalse)
2239 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2240 if (q == (Quantum *) NULL)
2245 for (x=0; x < (ssize_t) image->columns; x++)
2250 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2252 PixelChannel channel = GetPixelChannelChannel(image,j);
2253 PixelTrait traits = GetPixelChannelTraits(image,channel);
2254 if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
2256 q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
2257 ScaleQuantumToMap(q[j])+(
size_t) j]);
2259 q+=(ptrdiff_t) GetPixelChannels(image);
2261 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2263 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2268#if defined(MAGICKCORE_OPENMP_SUPPORT)
2272 proceed=SetImageProgress(image,EqualizeImageTag,progress,image->rows);
2273 if (proceed == MagickFalse)
2277 image_view=DestroyCacheView(image_view);
2278 equalize_map=(
double *) RelinquishMagickMemory(equalize_map);
2317static inline double gamma_pow(
const double value,
const double gamma)
2319 return(value < 0.0 ? value : pow(value,gamma));
2322MagickExport MagickBooleanType GammaImage(
Image *image,
const double gamma,
2325#define GammaImageTag "Gamma/Image"
2348 assert(image != (
Image *) NULL);
2349 assert(image->signature == MagickCoreSignature);
2350 if (IsEventLogging() != MagickFalse)
2351 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2354 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*gamma_map));
2355 if (gamma_map == (Quantum *) NULL)
2356 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2358 (void) memset(gamma_map,0,(MaxMap+1)*
sizeof(*gamma_map));
2360 for (i=0; i <= (ssize_t) MaxMap; i++)
2361 gamma_map[i]=ScaleMapToQuantum((
double) (MaxMap*pow((
double) i/
2362 MaxMap,PerceptibleReciprocal(gamma))));
2363 if (image->storage_class == PseudoClass)
2364 for (i=0; i < (ssize_t) image->colors; i++)
2369 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2370 image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
2371 ClampToQuantum(image->colormap[i].red))];
2372 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2373 image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
2374 ClampToQuantum(image->colormap[i].green))];
2375 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2376 image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
2377 ClampToQuantum(image->colormap[i].blue))];
2378 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2379 image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
2380 ClampToQuantum(image->colormap[i].alpha))];
2387 image_view=AcquireAuthenticCacheView(image,exception);
2388#if defined(MAGICKCORE_OPENMP_SUPPORT)
2389 #pragma omp parallel for schedule(static) shared(progress,status) \
2390 magick_number_threads(image,image,image->rows,1)
2392 for (y=0; y < (ssize_t) image->rows; y++)
2400 if (status == MagickFalse)
2402 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2403 if (q == (Quantum *) NULL)
2408 for (x=0; x < (ssize_t) image->columns; x++)
2413 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2415 PixelChannel channel = GetPixelChannelChannel(image,j);
2416 PixelTrait traits = GetPixelChannelTraits(image,channel);
2417 if ((traits & UpdatePixelTrait) == 0)
2419 q[j]=gamma_map[ScaleQuantumToMap(ClampToQuantum((MagickRealType)
2422 q+=(ptrdiff_t) GetPixelChannels(image);
2424 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2426 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2431#if defined(MAGICKCORE_OPENMP_SUPPORT)
2435 proceed=SetImageProgress(image,GammaImageTag,progress,image->rows);
2436 if (proceed == MagickFalse)
2440 image_view=DestroyCacheView(image_view);
2441 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
2442 if (image->gamma != 0.0)
2443 image->gamma*=gamma;
2474MagickExport MagickBooleanType GrayscaleImage(
Image *image,
2477#define GrayscaleImageTag "Grayscale/Image"
2491 assert(image != (
Image *) NULL);
2492 assert(image->signature == MagickCoreSignature);
2493 if (IsEventLogging() != MagickFalse)
2494 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2495 if (image->storage_class == PseudoClass)
2497 if (SyncImage(image,exception) == MagickFalse)
2498 return(MagickFalse);
2499 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2500 return(MagickFalse);
2502#if defined(MAGICKCORE_OPENCL_SUPPORT)
2503 if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
2505 image->intensity=method;
2506 image->type=GrayscaleType;
2507 if ((method == Rec601LuminancePixelIntensityMethod) ||
2508 (method == Rec709LuminancePixelIntensityMethod))
2509 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2510 return(SetImageColorspace(image,GRAYColorspace,exception));
2518 image_view=AcquireAuthenticCacheView(image,exception);
2519#if defined(MAGICKCORE_OPENMP_SUPPORT)
2520 #pragma omp parallel for schedule(static) shared(progress,status) \
2521 magick_number_threads(image,image,image->rows,1)
2523 for (y=0; y < (ssize_t) image->rows; y++)
2531 if (status == MagickFalse)
2533 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2534 if (q == (Quantum *) NULL)
2539 for (x=0; x < (ssize_t) image->columns; x++)
2547 red=(MagickRealType) GetPixelRed(image,q);
2548 green=(MagickRealType) GetPixelGreen(image,q);
2549 blue=(MagickRealType) GetPixelBlue(image,q);
2553 case AveragePixelIntensityMethod:
2555 intensity=(red+green+blue)/3.0;
2558 case BrightnessPixelIntensityMethod:
2560 intensity=MagickMax(MagickMax(red,green),blue);
2563 case LightnessPixelIntensityMethod:
2565 intensity=(MagickMin(MagickMin(red,green),blue)+
2566 MagickMax(MagickMax(red,green),blue))/2.0;
2569 case MSPixelIntensityMethod:
2571 intensity=(MagickRealType) (((
double) red*red+green*green+
2575 case Rec601LumaPixelIntensityMethod:
2577 if (image->colorspace == RGBColorspace)
2579 red=EncodePixelGamma(red);
2580 green=EncodePixelGamma(green);
2581 blue=EncodePixelGamma(blue);
2583 intensity=0.298839*red+0.586811*green+0.114350*blue;
2586 case Rec601LuminancePixelIntensityMethod:
2588 if (image->colorspace == sRGBColorspace)
2590 red=DecodePixelGamma(red);
2591 green=DecodePixelGamma(green);
2592 blue=DecodePixelGamma(blue);
2594 intensity=0.298839*red+0.586811*green+0.114350*blue;
2597 case Rec709LumaPixelIntensityMethod:
2600 if (image->colorspace == RGBColorspace)
2602 red=EncodePixelGamma(red);
2603 green=EncodePixelGamma(green);
2604 blue=EncodePixelGamma(blue);
2606 intensity=0.212656*red+0.715158*green+0.072186*blue;
2609 case Rec709LuminancePixelIntensityMethod:
2611 if (image->colorspace == sRGBColorspace)
2613 red=DecodePixelGamma(red);
2614 green=DecodePixelGamma(green);
2615 blue=DecodePixelGamma(blue);
2617 intensity=0.212656*red+0.715158*green+0.072186*blue;
2620 case RMSPixelIntensityMethod:
2622 intensity=(MagickRealType) (sqrt((
double) red*red+green*green+
2623 blue*blue)/sqrt(3.0));
2627 SetPixelGray(image,ClampToQuantum(intensity),q);
2628 q+=(ptrdiff_t) GetPixelChannels(image);
2630 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2632 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2637#if defined(MAGICKCORE_OPENMP_SUPPORT)
2641 proceed=SetImageProgress(image,GrayscaleImageTag,progress,image->rows);
2642 if (proceed == MagickFalse)
2646 image_view=DestroyCacheView(image_view);
2647 image->intensity=method;
2648 image->type=GrayscaleType;
2649 if ((method == Rec601LuminancePixelIntensityMethod) ||
2650 (method == Rec709LuminancePixelIntensityMethod))
2651 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2652 return(SetImageColorspace(image,GRAYColorspace,exception));
2686MagickExport MagickBooleanType HaldClutImage(
Image *image,
2689#define HaldClutImageTag "Clut/Image"
2691 typedef struct _HaldInfo
2723 assert(image != (
Image *) NULL);
2724 assert(image->signature == MagickCoreSignature);
2725 if (IsEventLogging() != MagickFalse)
2726 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2727 assert(hald_image != (
Image *) NULL);
2728 assert(hald_image->signature == MagickCoreSignature);
2729 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2730 return(MagickFalse);
2731 if ((image->alpha_trait & BlendPixelTrait) == 0)
2732 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2733 if (image->colorspace != hald_image->colorspace)
2734 (void) SetImageColorspace(image,hald_image->colorspace,exception);
2740 length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2741 (MagickRealType) hald_image->rows);
2742 for (level=2; (level*level*level) < length; level++) ;
2744 cube_size=level*level;
2745 width=(double) hald_image->columns;
2746 GetPixelInfo(hald_image,&zero);
2747 hald_view=AcquireVirtualCacheView(hald_image,exception);
2748 image_view=AcquireAuthenticCacheView(image,exception);
2749#if defined(MAGICKCORE_OPENMP_SUPPORT)
2750 #pragma omp parallel for schedule(static) shared(progress,status) \
2751 magick_number_threads(image,image,image->rows,1)
2753 for (y=0; y < (ssize_t) image->rows; y++)
2761 if (status == MagickFalse)
2763 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2764 if (q == (Quantum *) NULL)
2769 for (x=0; x < (ssize_t) image->columns; x++)
2776 point = { 0, 0, 0 };
2785 point.x=QuantumScale*(level-1.0)*(
double) GetPixelRed(image,q);
2786 point.y=QuantumScale*(level-1.0)*(
double) GetPixelGreen(image,q);
2787 point.z=QuantumScale*(level-1.0)*(
double) GetPixelBlue(image,q);
2788 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2789 point.x-=floor(point.x);
2790 point.y-=floor(point.y);
2791 point.z-=floor(point.z);
2792 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2793 fmod(offset,width),floor(offset/width),&pixel1,exception);
2794 if (status == MagickFalse)
2796 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2797 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2798 if (status == MagickFalse)
2801 if (hald_image->interpolate == NearestInterpolatePixel)
2802 area=(point.y < 0.5) ? 0.0 : 1.0;
2803 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2806 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2807 fmod(offset,width),floor(offset/width),&pixel1,exception);
2808 if (status == MagickFalse)
2810 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2811 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2812 if (status == MagickFalse)
2814 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2817 if (hald_image->interpolate == NearestInterpolatePixel)
2818 area=(point.z < 0.5)? 0.0 : 1.0;
2819 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2821 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2822 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2823 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2824 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2825 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2826 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2827 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2828 (image->colorspace == CMYKColorspace))
2829 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2830 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2831 (image->alpha_trait != UndefinedPixelTrait))
2832 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2833 q+=(ptrdiff_t) GetPixelChannels(image);
2835 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2837 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2842#if defined(MAGICKCORE_OPENMP_SUPPORT)
2846 proceed=SetImageProgress(image,HaldClutImageTag,progress,image->rows);
2847 if (proceed == MagickFalse)
2851 hald_view=DestroyCacheView(hald_view);
2852 image_view=DestroyCacheView(image_view);
2900static inline double LevelPixel(
const double black_point,
2901 const double white_point,
const double gamma,
const double pixel)
2907 scale=PerceptibleReciprocal(white_point-black_point);
2908 level_pixel=(double) QuantumRange*gamma_pow(scale*((
double) pixel-(
double)
2909 black_point),PerceptibleReciprocal(gamma));
2910 return(level_pixel);
2913MagickExport MagickBooleanType LevelImage(
Image *image,
const double black_point,
2914 const double white_point,
const double gamma,
ExceptionInfo *exception)
2916#define LevelImageTag "Level/Image"
2934 assert(image != (
Image *) NULL);
2935 assert(image->signature == MagickCoreSignature);
2936 if (IsEventLogging() != MagickFalse)
2937 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2938 if (image->storage_class == PseudoClass)
2939 for (i=0; i < (ssize_t) image->colors; i++)
2944 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2945 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2946 white_point,gamma,image->colormap[i].red));
2947 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2948 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2949 white_point,gamma,image->colormap[i].green));
2950 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2951 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2952 white_point,gamma,image->colormap[i].blue));
2953 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2954 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2955 white_point,gamma,image->colormap[i].alpha));
2962 image_view=AcquireAuthenticCacheView(image,exception);
2963#if defined(MAGICKCORE_OPENMP_SUPPORT)
2964 #pragma omp parallel for schedule(static) shared(progress,status) \
2965 magick_number_threads(image,image,image->rows,1)
2967 for (y=0; y < (ssize_t) image->rows; y++)
2975 if (status == MagickFalse)
2977 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2978 if (q == (Quantum *) NULL)
2983 for (x=0; x < (ssize_t) image->columns; x++)
2988 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2990 PixelChannel channel = GetPixelChannelChannel(image,j);
2991 PixelTrait traits = GetPixelChannelTraits(image,channel);
2992 if ((traits & UpdatePixelTrait) == 0)
2994 q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2997 q+=(ptrdiff_t) GetPixelChannels(image);
2999 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3001 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3006#if defined(MAGICKCORE_OPENMP_SUPPORT)
3010 proceed=SetImageProgress(image,LevelImageTag,progress,image->rows);
3011 if (proceed == MagickFalse)
3015 image_view=DestroyCacheView(image_view);
3016 (void) ClampImage(image,exception);
3062MagickExport MagickBooleanType LevelizeImage(
Image *image,
3063 const double black_point,
const double white_point,
const double gamma,
3066#define LevelizeImageTag "Levelize/Image"
3067#define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
3068 (QuantumScale*((double) x)),gamma))*(white_point-black_point)+black_point)
3088 assert(image != (
Image *) NULL);
3089 assert(image->signature == MagickCoreSignature);
3090 if (IsEventLogging() != MagickFalse)
3091 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3092 if (image->storage_class == PseudoClass)
3093 for (i=0; i < (ssize_t) image->colors; i++)
3098 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3099 image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
3100 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3101 image->colormap[i].green=(double) LevelizeValue(
3102 image->colormap[i].green);
3103 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3104 image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
3105 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3106 image->colormap[i].alpha=(double) LevelizeValue(
3107 image->colormap[i].alpha);
3114 image_view=AcquireAuthenticCacheView(image,exception);
3115#if defined(MAGICKCORE_OPENMP_SUPPORT)
3116 #pragma omp parallel for schedule(static) shared(progress,status) \
3117 magick_number_threads(image,image,image->rows,1)
3119 for (y=0; y < (ssize_t) image->rows; y++)
3127 if (status == MagickFalse)
3129 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3130 if (q == (Quantum *) NULL)
3135 for (x=0; x < (ssize_t) image->columns; x++)
3140 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3142 PixelChannel channel = GetPixelChannelChannel(image,j);
3143 PixelTrait traits = GetPixelChannelTraits(image,channel);
3144 if ((traits & UpdatePixelTrait) == 0)
3146 q[j]=LevelizeValue(q[j]);
3148 q+=(ptrdiff_t) GetPixelChannels(image);
3150 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3152 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3157#if defined(MAGICKCORE_OPENMP_SUPPORT)
3161 proceed=SetImageProgress(image,LevelizeImageTag,progress,image->rows);
3162 if (proceed == MagickFalse)
3166 image_view=DestroyCacheView(image_view);
3211MagickExport MagickBooleanType LevelImageColors(
Image *image,
3224 assert(image != (
Image *) NULL);
3225 assert(image->signature == MagickCoreSignature);
3226 if (IsEventLogging() != MagickFalse)
3227 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3228 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
3229 ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
3230 (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
3231 (void) SetImageColorspace(image,sRGBColorspace,exception);
3233 if (invert == MagickFalse)
3235 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3237 channel_mask=SetImageChannelMask(image,RedChannel);
3238 status&=(MagickStatusType) LevelImage(image,black_color->red,
3239 white_color->red,1.0,exception);
3240 (void) SetImageChannelMask(image,channel_mask);
3242 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3244 channel_mask=SetImageChannelMask(image,GreenChannel);
3245 status&=(MagickStatusType) LevelImage(image,black_color->green,
3246 white_color->green,1.0,exception);
3247 (void) SetImageChannelMask(image,channel_mask);
3249 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3251 channel_mask=SetImageChannelMask(image,BlueChannel);
3252 status&=(MagickStatusType) LevelImage(image,black_color->blue,
3253 white_color->blue,1.0,exception);
3254 (void) SetImageChannelMask(image,channel_mask);
3256 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3257 (image->colorspace == CMYKColorspace))
3259 channel_mask=SetImageChannelMask(image,BlackChannel);
3260 status&=(MagickStatusType) LevelImage(image,black_color->black,
3261 white_color->black,1.0,exception);
3262 (void) SetImageChannelMask(image,channel_mask);
3264 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3265 (image->alpha_trait != UndefinedPixelTrait))
3267 channel_mask=SetImageChannelMask(image,AlphaChannel);
3268 status&=(MagickStatusType) LevelImage(image,black_color->alpha,
3269 white_color->alpha,1.0,exception);
3270 (void) SetImageChannelMask(image,channel_mask);
3275 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3277 channel_mask=SetImageChannelMask(image,RedChannel);
3278 status&=(MagickStatusType) LevelizeImage(image,black_color->red,
3279 white_color->red,1.0,exception);
3280 (void) SetImageChannelMask(image,channel_mask);
3282 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3284 channel_mask=SetImageChannelMask(image,GreenChannel);
3285 status&=(MagickStatusType) LevelizeImage(image,black_color->green,
3286 white_color->green,1.0,exception);
3287 (void) SetImageChannelMask(image,channel_mask);
3289 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3291 channel_mask=SetImageChannelMask(image,BlueChannel);
3292 status&=(MagickStatusType) LevelizeImage(image,black_color->blue,
3293 white_color->blue,1.0,exception);
3294 (void) SetImageChannelMask(image,channel_mask);
3296 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3297 (image->colorspace == CMYKColorspace))
3299 channel_mask=SetImageChannelMask(image,BlackChannel);
3300 status&=(MagickStatusType) LevelizeImage(image,black_color->black,
3301 white_color->black,1.0,exception);
3302 (void) SetImageChannelMask(image,channel_mask);
3304 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3305 (image->alpha_trait != UndefinedPixelTrait))
3307 channel_mask=SetImageChannelMask(image,AlphaChannel);
3308 status&=(MagickStatusType) LevelizeImage(image,black_color->alpha,
3309 white_color->alpha,1.0,exception);
3310 (void) SetImageChannelMask(image,channel_mask);
3313 return(status != 0 ? MagickTrue : MagickFalse);
3347MagickExport MagickBooleanType LinearStretchImage(
Image *image,
3348 const double black_point,
const double white_point,
ExceptionInfo *exception)
3350#define LinearStretchImageTag "LinearStretch/Image"
3356 property[MagickPathExtent];
3373 assert(image != (
Image *) NULL);
3374 assert(image->signature == MagickCoreSignature);
3375 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*histogram));
3376 if (histogram == (
double *) NULL)
3377 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3382 (void) memset(histogram,0,(MaxMap+1)*
sizeof(*histogram));
3383 image_view=AcquireVirtualCacheView(image,exception);
3384 for (y=0; y < (ssize_t) image->rows; y++)
3392 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3393 if (p == (
const Quantum *) NULL)
3395 for (x=0; x < (ssize_t) image->columns; x++)
3397 intensity=GetPixelIntensity(image,p);
3398 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
3399 p+=(ptrdiff_t) GetPixelChannels(image);
3402 image_view=DestroyCacheView(image_view);
3407 for (black=0; black < (ssize_t) MaxMap; black++)
3409 intensity+=histogram[black];
3410 if (intensity >= black_point)
3414 for (white=(ssize_t) MaxMap; white != 0; white--)
3416 intensity+=histogram[white];
3417 if (intensity >= white_point)
3420 histogram=(
double *) RelinquishMagickMemory(histogram);
3421 status=LevelImage(image,(
double) ScaleMapToQuantum((MagickRealType) black),
3422 (
double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
3423 (void) FormatLocaleString(property,MagickPathExtent,
"%gx%g%%",100.0*black/
3424 MaxMap,100.0*white/MaxMap);
3425 (void) SetImageProperty(image,
"histogram:linear-stretch",property,exception);
3461static inline void ModulateHCL(
const double percent_hue,
3462 const double percent_chroma,
const double percent_luma,
double *red,
3463 double *green,
double *blue)
3473 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
3474 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3475 chroma*=0.01*percent_chroma;
3476 luma*=0.01*percent_luma;
3477 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
3480static inline void ModulateHCLp(
const double percent_hue,
3481 const double percent_chroma,
const double percent_luma,
double *red,
3482 double *green,
double *blue)
3492 ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
3493 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3494 chroma*=0.01*percent_chroma;
3495 luma*=0.01*percent_luma;
3496 ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
3499static inline void ModulateHSB(
const double percent_hue,
3500 const double percent_saturation,
const double percent_brightness,
double *red,
3501 double *green,
double *blue)
3511 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
3512 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3513 saturation*=0.01*percent_saturation;
3514 brightness*=0.01*percent_brightness;
3515 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3518static inline void ModulateHSI(
const double percent_hue,
3519 const double percent_saturation,
const double percent_intensity,
double *red,
3520 double *green,
double *blue)
3530 ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3531 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3532 saturation*=0.01*percent_saturation;
3533 intensity*=0.01*percent_intensity;
3534 ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3537static inline void ModulateHSL(
const double percent_hue,
3538 const double percent_saturation,
const double percent_lightness,
double *red,
3539 double *green,
double *blue)
3549 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3550 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3551 saturation*=0.01*percent_saturation;
3552 lightness*=0.01*percent_lightness;
3553 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3556static inline void ModulateHSV(
const double percent_hue,
3557 const double percent_saturation,
const double percent_value,
double *red,
3558 double *green,
double *blue)
3568 ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3569 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3570 saturation*=0.01*percent_saturation;
3571 value*=0.01*percent_value;
3572 ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3575static inline void ModulateHWB(
const double percent_hue,
3576 const double percent_whiteness,
const double percent_blackness,
double *red,
3577 double *green,
double *blue)
3587 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3588 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3589 blackness*=0.01*percent_blackness;
3590 whiteness*=0.01*percent_whiteness;
3591 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3594static inline void ModulateLCHab(
const double percent_luma,
3595 const double percent_chroma,
const double percent_hue,
3596 const IlluminantType illuminant,
double *red,
double *green,
double *blue)
3606 ConvertRGBToLCHab(*red,*green,*blue,illuminant,&luma,&chroma,&hue);
3607 luma*=0.01*percent_luma;
3608 chroma*=0.01*percent_chroma;
3609 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3610 ConvertLCHabToRGB(luma,chroma,hue,illuminant,red,green,blue);
3613static inline void ModulateLCHuv(
const double percent_luma,
3614 const double percent_chroma,
const double percent_hue,
3615 const IlluminantType illuminant,
double *red,
double *green,
double *blue)
3625 ConvertRGBToLCHuv(*red,*green,*blue,illuminant,&luma,&chroma,&hue);
3626 luma*=0.01*percent_luma;
3627 chroma*=0.01*percent_chroma;
3628 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3629 ConvertLCHuvToRGB(luma,chroma,hue,illuminant,red,green,blue);
3632MagickExport MagickBooleanType ModulateImage(
Image *image,
const char *modulate,
3635#define ModulateImageTag "Modulate/Image"
3641 colorspace = UndefinedColorspace;
3647 percent_brightness = 100.0,
3648 percent_hue = 100.0,
3649 percent_saturation = 100.0;
3655 illuminant = D65Illuminant;
3675 assert(image != (
Image *) NULL);
3676 assert(image->signature == MagickCoreSignature);
3677 if (IsEventLogging() != MagickFalse)
3678 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3679 if (modulate == (
char *) NULL)
3680 return(MagickFalse);
3681 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
3682 (void) SetImageColorspace(image,sRGBColorspace,exception);
3683 flags=ParseGeometry(modulate,&geometry_info);
3684 if ((flags & RhoValue) != 0)
3685 percent_brightness=geometry_info.rho;
3686 if ((flags & SigmaValue) != 0)
3687 percent_saturation=geometry_info.sigma;
3688 if ((flags & XiValue) != 0)
3689 percent_hue=geometry_info.xi;
3690 artifact=GetImageArtifact(image,
"modulate:colorspace");
3691 if (artifact != (
const char *) NULL)
3692 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3693 MagickFalse,artifact);
3694 artifact=GetImageArtifact(image,
"color:illuminant");
3695 if (artifact != (
const char *) NULL)
3700 illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
3702 if (illuminant_type < 0)
3704 illuminant=UndefinedIlluminant;
3705 colorspace=UndefinedColorspace;
3708 illuminant=(IlluminantType) illuminant_type;
3710 if (image->storage_class == PseudoClass)
3711 for (i=0; i < (ssize_t) image->colors; i++)
3721 red=(double) image->colormap[i].red;
3722 green=(double) image->colormap[i].green;
3723 blue=(double) image->colormap[i].blue;
3728 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3732 case HCLpColorspace:
3734 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3740 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3746 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3753 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3759 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3765 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3770 case LCHabColorspace:
3772 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3773 illuminant,&red,&green,&blue);
3776 case LCHuvColorspace:
3778 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3779 illuminant,&red,&green,&blue);
3783 image->colormap[i].red=red;
3784 image->colormap[i].green=green;
3785 image->colormap[i].blue=blue;
3790#if defined(MAGICKCORE_OPENCL_SUPPORT)
3791 if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3792 percent_saturation,colorspace,exception) != MagickFalse)
3797 image_view=AcquireAuthenticCacheView(image,exception);
3798#if defined(MAGICKCORE_OPENMP_SUPPORT)
3799 #pragma omp parallel for schedule(static) shared(progress,status) \
3800 magick_number_threads(image,image,image->rows,1)
3802 for (y=0; y < (ssize_t) image->rows; y++)
3810 if (status == MagickFalse)
3812 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3813 if (q == (Quantum *) NULL)
3818 for (x=0; x < (ssize_t) image->columns; x++)
3825 red=(double) GetPixelRed(image,q);
3826 green=(double) GetPixelGreen(image,q);
3827 blue=(double) GetPixelBlue(image,q);
3832 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3836 case HCLpColorspace:
3838 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3844 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3850 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3857 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3863 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3869 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3874 case LCHabColorspace:
3876 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3877 illuminant,&red,&green,&blue);
3880 case LCHuvColorspace:
3882 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3883 illuminant,&red,&green,&blue);
3887 SetPixelRed(image,ClampToQuantum(red),q);
3888 SetPixelGreen(image,ClampToQuantum(green),q);
3889 SetPixelBlue(image,ClampToQuantum(blue),q);
3890 q+=(ptrdiff_t) GetPixelChannels(image);
3892 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3894 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3899#if defined(MAGICKCORE_OPENMP_SUPPORT)
3903 proceed=SetImageProgress(image,ModulateImageTag,progress,image->rows);
3904 if (proceed == MagickFalse)
3908 image_view=DestroyCacheView(image_view);
3940MagickExport MagickBooleanType NegateImage(
Image *image,
3943#define NegateImageTag "Negate/Image"
3960 assert(image != (
Image *) NULL);
3961 assert(image->signature == MagickCoreSignature);
3962 if (IsEventLogging() != MagickFalse)
3963 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3964 if (image->storage_class == PseudoClass)
3965 for (i=0; i < (ssize_t) image->colors; i++)
3970 if (grayscale != MagickFalse)
3971 if ((image->colormap[i].red != image->colormap[i].green) ||
3972 (image->colormap[i].green != image->colormap[i].blue))
3974 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3975 image->colormap[i].red=(double) QuantumRange-image->colormap[i].red;
3976 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3977 image->colormap[i].green=(double) QuantumRange-image->colormap[i].green;
3978 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3979 image->colormap[i].blue=(double) QuantumRange-image->colormap[i].blue;
3986 image_view=AcquireAuthenticCacheView(image,exception);
3987 if( grayscale != MagickFalse )
3989 for (y=0; y < (ssize_t) image->rows; y++)
4000 if (status == MagickFalse)
4002 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4004 if (q == (Quantum *) NULL)
4009 for (x=0; x < (ssize_t) image->columns; x++)
4014 if (IsPixelGray(image,q) == MagickFalse)
4016 q+=(ptrdiff_t) GetPixelChannels(image);
4019 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4021 PixelChannel channel = GetPixelChannelChannel(image,j);
4022 PixelTrait traits = GetPixelChannelTraits(image,channel);
4023 if ((traits & UpdatePixelTrait) == 0)
4025 q[j]=QuantumRange-q[j];
4027 q+=(ptrdiff_t) GetPixelChannels(image);
4029 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4030 if (sync == MagickFalse)
4032 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4038 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4039 if (proceed == MagickFalse)
4043 image_view=DestroyCacheView(image_view);
4049#if defined(MAGICKCORE_OPENMP_SUPPORT)
4050 #pragma omp parallel for schedule(static) shared(progress,status) \
4051 magick_number_threads(image,image,image->rows,1)
4053 for (y=0; y < (ssize_t) image->rows; y++)
4061 if (status == MagickFalse)
4063 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4064 if (q == (Quantum *) NULL)
4069 for (x=0; x < (ssize_t) image->columns; x++)
4074 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4076 PixelChannel channel = GetPixelChannelChannel(image,j);
4077 PixelTrait traits = GetPixelChannelTraits(image,channel);
4078 if ((traits & UpdatePixelTrait) == 0)
4080 q[j]=QuantumRange-q[j];
4082 q+=(ptrdiff_t) GetPixelChannels(image);
4084 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4086 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4091#if defined(MAGICKCORE_OPENMP_SUPPORT)
4095 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4096 if (proceed == MagickFalse)
4100 image_view=DestroyCacheView(image_view);
4130MagickExport MagickBooleanType NormalizeImage(
Image *image,
4137 black_point=0.02*image->columns*image->rows;
4138 white_point=0.99*image->columns*image->rows;
4139 return(ContrastStretchImage(image,black_point,white_point,exception));
4206#if defined(MAGICKCORE_HAVE_ATANH)
4207#define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
4209#define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
4228#define ScaledSigmoidal(a,b,x) ( \
4229 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
4230 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
4240static inline double InverseScaledSigmoidal(
const double a,
const double b,
4243 const double sig0=Sigmoidal(a,b,0.0);
4244 const double sig1=Sigmoidal(a,b,1.0);
4245 const double argument=(sig1-sig0)*x+sig0;
4246 const double clamped=
4248#if defined(MAGICKCORE_HAVE_ATANH)
4249 argument < -1+MagickEpsilon
4253 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4255 return(b+(2.0/a)*atanh(clamped));
4257 argument < MagickEpsilon
4261 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4263 return(b-log(1.0/clamped-1.0)/a);
4267MagickExport MagickBooleanType SigmoidalContrastImage(
Image *image,
4268 const MagickBooleanType sharpen,
const double contrast,
const double midpoint,
4271#define SigmoidalContrastImageTag "SigmoidalContrast/Image"
4272#define ScaledSig(x) (ClampToQuantum((double) QuantumRange* \
4273 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*((double) x))) )
4274#define InverseScaledSig(x) (ClampToQuantum((double) QuantumRange* \
4275 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale* \
4293 assert(image != (
Image *) NULL);
4294 assert(image->signature == MagickCoreSignature);
4295 if (IsEventLogging() != MagickFalse)
4296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4301 if (contrast < MagickEpsilon)
4306 if (image->storage_class == PseudoClass)
4311 if( sharpen != MagickFalse )
4312 for (i=0; i < (ssize_t) image->colors; i++)
4314 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4315 image->colormap[i].red=(MagickRealType) ScaledSig(
4316 image->colormap[i].red);
4317 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4318 image->colormap[i].green=(MagickRealType) ScaledSig(
4319 image->colormap[i].green);
4320 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4321 image->colormap[i].blue=(MagickRealType) ScaledSig(
4322 image->colormap[i].blue);
4323 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4324 image->colormap[i].alpha=(MagickRealType) ScaledSig(
4325 image->colormap[i].alpha);
4328 for (i=0; i < (ssize_t) image->colors; i++)
4330 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4331 image->colormap[i].red=(MagickRealType) InverseScaledSig(
4332 image->colormap[i].red);
4333 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4334 image->colormap[i].green=(MagickRealType) InverseScaledSig(
4335 image->colormap[i].green);
4336 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4337 image->colormap[i].blue=(MagickRealType) InverseScaledSig(
4338 image->colormap[i].blue);
4339 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4340 image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
4341 image->colormap[i].alpha);
4349 image_view=AcquireAuthenticCacheView(image,exception);
4350#if defined(MAGICKCORE_OPENMP_SUPPORT)
4351 #pragma omp parallel for schedule(static) shared(progress,status) \
4352 magick_number_threads(image,image,image->rows,1)
4354 for (y=0; y < (ssize_t) image->rows; y++)
4362 if (status == MagickFalse)
4364 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4365 if (q == (Quantum *) NULL)
4370 for (x=0; x < (ssize_t) image->columns; x++)
4375 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4377 PixelChannel channel = GetPixelChannelChannel(image,i);
4378 PixelTrait traits = GetPixelChannelTraits(image,channel);
4379 if ((traits & UpdatePixelTrait) == 0)
4381 if( sharpen != MagickFalse )
4382 q[i]=ScaledSig(q[i]);
4384 q[i]=InverseScaledSig(q[i]);
4386 q+=(ptrdiff_t) GetPixelChannels(image);
4388 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4390 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4395#if defined(MAGICKCORE_OPENMP_SUPPORT)
4399 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress,
4401 if (proceed == MagickFalse)
4405 image_view=DestroyCacheView(image_view);
4435MagickExport MagickBooleanType WhiteBalanceImage(
Image *image,
4438#define WhiteBalanceImageTag "WhiteBalance/Image"
4462 assert(image != (
Image *) NULL);
4463 assert(image->signature == MagickCoreSignature);
4464 if (IsEventLogging() != MagickFalse)
4465 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4466 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4467 return(MagickFalse);
4468 status=TransformImageColorspace(image,LabColorspace,exception);
4471 image_view=AcquireAuthenticCacheView(image,exception);
4472 for (y=0; y < (ssize_t) image->rows; y++)
4480 if (status == MagickFalse)
4482 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4483 if (p == (Quantum *) NULL)
4488 for (x=0; x < (ssize_t) image->columns; x++)
4490 a_mean+=QuantumScale*(double) GetPixela(image,p)-0.5;
4491 b_mean+=QuantumScale*(double) GetPixelb(image,p)-0.5;
4492 p+=(ptrdiff_t) GetPixelChannels(image);
4495 a_mean/=((double) image->columns*image->rows);
4496 b_mean/=((double) image->columns*image->rows);
4498#if defined(MAGICKCORE_OPENMP_SUPPORT)
4499 #pragma omp parallel for schedule(static) shared(progress,status) \
4500 magick_number_threads(image,image,image->rows,1)
4502 for (y=0; y < (ssize_t) image->rows; y++)
4510 if (status == MagickFalse)
4512 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4513 if (q == (Quantum *) NULL)
4518 for (x=0; x < (ssize_t) image->columns; x++)
4527 a=(double) GetPixela(image,q)-1.1*(double) GetPixelL(image,q)*a_mean;
4528 b=(double) GetPixelb(image,q)-1.1*(double) GetPixelL(image,q)*b_mean;
4529 SetPixela(image,ClampToQuantum(a),q);
4530 SetPixelb(image,ClampToQuantum(b),q);
4531 q+=(ptrdiff_t) GetPixelChannels(image);
4533 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4535 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4540#if defined(MAGICKCORE_OPENMP_SUPPORT)
4544 proceed=SetImageProgress(image,WhiteBalanceImageTag,progress,image->rows);
4545 if (proceed == MagickFalse)
4549 image_view=DestroyCacheView(image_view);
4550 artifact=GetImageArtifact(image,
"white-balance:vibrance");
4551 if (artifact != (
const char *) NULL)
4568 flags=ParseGeometry(artifact,&geometry_info);
4569 if ((flags & RhoValue) != 0)
4570 black_point=geometry_info.rho;
4571 if ((flags & PercentValue) != 0)
4572 black_point*=((double) QuantumRange/100.0);
4573 channel_mask=SetImageChannelMask(image,(ChannelType) (aChannel |
4575 status&=(MagickStatusType) LevelImage(image,black_point,(
double)
4576 QuantumRange-black_point,1.0,exception);
4577 (void) SetImageChannelMask(image,channel_mask);
4579 status&=(MagickStatusType) TransformImageColorspace(image,sRGBColorspace,
4581 return(status != 0 ? MagickTrue : MagickFalse);