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/pixel-private.h"
73#include "MagickCore/property.h"
74#include "MagickCore/quantum.h"
75#include "MagickCore/quantum-private.h"
76#include "MagickCore/resample.h"
77#include "MagickCore/resample-private.h"
78#include "MagickCore/resource_.h"
79#include "MagickCore/statistic.h"
80#include "MagickCore/string_.h"
81#include "MagickCore/string-private.h"
82#include "MagickCore/thread-private.h"
83#include "MagickCore/threshold.h"
84#include "MagickCore/token.h"
85#include "MagickCore/xml-tree.h"
86#include "MagickCore/xml-tree-private.h"
113MagickExport MagickBooleanType AutoGammaImage(
Image *image,
129 if (image->channel_mask == AllChannels)
134 (void) GetImageMean(image,&mean,&sans,exception);
135 gamma=log(mean*QuantumScale)/log_mean;
136 return(LevelImage(image,0.0,(
double) QuantumRange,gamma,exception));
142 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
147 PixelChannel channel = GetPixelChannelChannel(image,i);
148 PixelTrait traits = GetPixelChannelTraits(image,channel);
149 if ((traits & UpdatePixelTrait) == 0)
151 channel_mask=SetImageChannelMask(image,(ChannelType) (1UL << i));
152 status=GetImageMean(image,&mean,&sans,exception);
153 gamma=log(mean*QuantumScale)/log_mean;
154 status&=(MagickStatusType) LevelImage(image,0.0,(
double) QuantumRange,gamma,
156 (void) SetImageChannelMask(image,channel_mask);
157 if (status == MagickFalse)
160 return(status != 0 ? MagickTrue : MagickFalse);
188MagickExport MagickBooleanType AutoLevelImage(
Image *image,
191 return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
225MagickExport MagickBooleanType BrightnessContrastImage(
Image *image,
226 const double brightness,
const double contrast,
ExceptionInfo *exception)
228#define BrightnessContrastImageTag "BrightnessContrast/Image"
241 assert(image != (
Image *) NULL);
242 assert(image->signature == MagickCoreSignature);
243 if (IsEventLogging() != MagickFalse)
244 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
245 slope=100.0*MagickSafeReciprocal(100.0-contrast);
247 slope=0.01*contrast+1.0;
248 intercept=(0.01*brightness-0.5)*slope+0.5;
249 coefficients[0]=slope;
250 coefficients[1]=intercept;
251 status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
303static void ClipCLAHEHistogram(
const double clip_limit,
const size_t number_bins,
306#define NumberCLAHEGrays (65536)
318 if (number_bins == 0)
321 for (i=0; i < (ssize_t) number_bins; i++)
322 if (histogram[i] > clip_limit)
323 cumulative_excess+=(ssize_t) (histogram[i]-clip_limit);
327 step=cumulative_excess/(ssize_t) number_bins;
328 excess=(ssize_t) (clip_limit-step);
329 for (i=0; i < (ssize_t) number_bins; i++)
331 if ((
double) histogram[i] > clip_limit)
332 histogram[i]=(
size_t) clip_limit;
334 if ((ssize_t) histogram[i] > excess)
336 cumulative_excess-=(ssize_t) histogram[i]-excess;
337 histogram[i]=(size_t) clip_limit;
341 cumulative_excess-=step;
342 histogram[i]+=(size_t) step;
356 previous_excess=cumulative_excess;
358 q=histogram+number_bins;
359 while ((cumulative_excess != 0) && (p < q))
361 step=(ssize_t) number_bins/cumulative_excess;
364 for (p=histogram; (p < q) && (cumulative_excess != 0); p+=(ptrdiff_t) step)
365 if ((
double) *p < clip_limit)
372 }
while ((cumulative_excess != 0) && (cumulative_excess < previous_excess));
375static void GenerateCLAHEHistogram(
const RectangleInfo *clahe_info,
377 const unsigned short *lut,
const unsigned short *pixels,
size_t *histogram)
388 for (i=0; i < (ssize_t) number_bins; i++)
391 for (i=0; i < (ssize_t) tile_info->height; i++)
396 q=p+tile_info->width;
398 histogram[lut[*p++]]++;
399 q+=(ptrdiff_t) clahe_info->width;
400 p=q-tile_info->width;
404static void InterpolateCLAHE(
const RectangleInfo *clahe_info,
const size_t *Q12,
405 const size_t *Q22,
const size_t *Q11,
const size_t *Q21,
406 const RectangleInfo *tile,
const unsigned short *lut,
unsigned short *pixels)
417 for (y=(ssize_t) tile->height; y > 0; y--)
422 for (x=(ssize_t) tile->width; x > 0; x--)
424 intensity=lut[*pixels];
425 *pixels++=(
unsigned short) (MagickSafeReciprocal((
double) tile->width*
426 tile->height)*(y*((
double) x*Q12[intensity]+((
double) tile->width-x)*
427 Q22[intensity])+((double) tile->height-y)*((double) x*Q11[intensity]+
428 ((
double) tile->width-x)*Q21[intensity])));
430 pixels+=(clahe_info->width-tile->width);
434static void GenerateCLAHELut(
const RangeInfo *range_info,
435 const size_t number_bins,
unsigned short *lut)
446 delta=(
unsigned short) ((range_info->max-range_info->min)/number_bins+1);
447 for (i=(ssize_t) range_info->min; i <= (ssize_t) range_info->max; i++)
448 lut[i]=(
unsigned short) ((i-range_info->min)/delta);
451static void MapCLAHEHistogram(
const RangeInfo *range_info,
452 const size_t number_bins,
const size_t number_pixels,
size_t *histogram)
464 scale=(double) (range_info->max-range_info->min)/number_pixels;
466 for (i=0; i < (ssize_t) number_bins; i++)
469 histogram[i]=(size_t) (range_info->min+scale*sum);
470 if (histogram[i] > range_info->max)
471 histogram[i]=range_info->max;
475static MagickBooleanType CLAHE(
const RectangleInfo *clahe_info,
477 const size_t number_bins,
const double clip_limit,
unsigned short *pixels)
496 if (clip_limit == 1.0)
498 tile_cache=AcquireVirtualMemory((
size_t) clahe_info->x*number_bins,(
size_t)
499 clahe_info->y*
sizeof(*tiles));
502 lut=(
unsigned short *) AcquireQuantumMemory(NumberCLAHEGrays,
sizeof(*lut));
503 if (lut == (
unsigned short *) NULL)
505 tile_cache=RelinquishVirtualMemory(tile_cache);
508 tiles=(
size_t *) GetVirtualMemoryBlob(tile_cache);
509 limit=(size_t) (clip_limit*((
double) tile_info->width*tile_info->height)/
516 GenerateCLAHELut(range_info,number_bins,lut);
518 for (y=0; y < (ssize_t) clahe_info->y; y++)
523 for (x=0; x < (ssize_t) clahe_info->x; x++)
528 histogram=tiles+((ssize_t) number_bins*(y*clahe_info->x+x));
529 GenerateCLAHEHistogram(clahe_info,tile_info,number_bins,lut,p,histogram);
530 ClipCLAHEHistogram((
double) limit,number_bins,histogram);
531 MapCLAHEHistogram(range_info,number_bins,tile_info->width*
532 tile_info->height,histogram);
533 p+=(ptrdiff_t) tile_info->width;
535 p+=CastDoubleToPtrdiffT((
double) clahe_info->width*(tile_info->height-1));
541 for (y=0; y <= (ssize_t) clahe_info->y; y++)
552 tile.height=tile_info->height;
560 tile.height=tile_info->height >> 1;
565 if (y == (ssize_t) clahe_info->y)
570 tile.height=(tile_info->height+1) >> 1;
571 tile.y=clahe_info->y-1;
574 for (x=0; x <= (ssize_t) clahe_info->x; x++)
582 tile.width=tile_info->width;
590 tile.width=tile_info->width >> 1;
595 if (x == (ssize_t) clahe_info->x)
600 tile.width=(tile_info->width+1) >> 1;
601 tile.x=clahe_info->x-1;
604 Q12=(double) number_bins*(tile.y*clahe_info->x+tile.x);
605 Q22=(double) number_bins*(tile.y*clahe_info->x+offset.x);
606 Q11=(double) number_bins*(offset.y*clahe_info->x+tile.x);
607 Q21=(double) number_bins*(offset.y*clahe_info->x+offset.x);
608 InterpolateCLAHE(clahe_info,tiles+CastDoubleToPtrdiffT(Q12),
609 tiles+CastDoubleToPtrdiffT(Q22),tiles+CastDoubleToPtrdiffT(Q11),
610 tiles+CastDoubleToPtrdiffT(Q21),&tile,lut,p);
611 p+=(ptrdiff_t) tile.width;
613 p+=CastDoubleToPtrdiffT((
double) clahe_info->width*(tile.height-1));
615 lut=(
unsigned short *) RelinquishMagickMemory(lut);
616 tile_cache=RelinquishVirtualMemory(tile_cache);
620MagickExport MagickBooleanType CLAHEImage(
Image *image,
const size_t width,
621 const size_t height,
const size_t number_bins,
const double clip_limit,
624#define CLAHEImageTag "CLAHE/Image"
660 assert(image != (
Image *) NULL);
661 assert(image->signature == MagickCoreSignature);
662 if (IsEventLogging() != MagickFalse)
663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
665 range_info.max=NumberCLAHEGrays-1;
666 tile_info.width=MagickMax(width,2);
667 if (tile_info.width == 0)
668 tile_info.width=image->columns >> 3;
669 tile_info.height=MagickMax(height,2);
670 if (tile_info.height == 0)
671 tile_info.height=image->rows >> 3;
673 if ((image->columns % tile_info.width) != 0)
674 tile_info.x=(ssize_t) (tile_info.width-(image->columns % tile_info.width));
676 if ((image->rows % tile_info.height) != 0)
677 tile_info.y=(ssize_t) (tile_info.height-(image->rows % tile_info.height));
678 clahe_info.width=(size_t) ((ssize_t) image->columns+tile_info.x);
679 clahe_info.height=(size_t) ((ssize_t) image->rows+tile_info.y);
680 clahe_info.x=(ssize_t) (clahe_info.width/tile_info.width);
681 clahe_info.y=(ssize_t) (clahe_info.height/tile_info.height);
682 pixel_cache=AcquireVirtualMemory(clahe_info.width,clahe_info.height*
685 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
687 pixels=(
unsigned short *) GetVirtualMemoryBlob(pixel_cache);
688 colorspace=image->colorspace;
689 if (TransformImageColorspace(image,LabColorspace,exception) == MagickFalse)
691 pixel_cache=RelinquishVirtualMemory(pixel_cache);
697 image_view=AcquireVirtualCacheView(image,exception);
701 for (y=0; y < (ssize_t) clahe_info.height; y++)
709 if (status == MagickFalse)
711 p=GetCacheViewVirtualPixels(image_view,-(tile_info.x >> 1),y-
712 (tile_info.y >> 1),clahe_info.width,1,exception);
713 if (p == (
const Quantum *) NULL)
718 for (x=0; x < (ssize_t) clahe_info.width; x++)
720 pixels[n++]=ScaleQuantumToShort(p[0]);
721 p+=(ptrdiff_t) GetPixelChannels(image);
723 if (image->progress_monitor != (MagickProgressMonitor) NULL)
729 proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
730 GetPixelChannels(image));
731 if (proceed == MagickFalse)
735 image_view=DestroyCacheView(image_view);
736 status=CLAHE(&clahe_info,&tile_info,&range_info,number_bins == 0 ?
737 (
size_t) 128 : MagickMin(number_bins,256),clip_limit,pixels);
738 if (status == MagickFalse)
739 (void) ThrowMagickException(exception,GetMagickModule(),
740 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
744 image_view=AcquireAuthenticCacheView(image,exception);
745 n=clahe_info.width*(size_t) (tile_info.y/2);
746 for (y=0; y < (ssize_t) image->rows; y++)
754 if (status == MagickFalse)
756 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
757 if (q == (Quantum *) NULL)
762 n+=(size_t) (tile_info.x/2);
763 for (x=0; x < (ssize_t) image->columns; x++)
765 q[0]=ScaleShortToQuantum(pixels[n++]);
766 q+=(ptrdiff_t) GetPixelChannels(image);
768 n+=(size_t) ((ssize_t) clahe_info.width-(ssize_t) image->columns-
770 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
772 if (image->progress_monitor != (MagickProgressMonitor) NULL)
778 proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
779 GetPixelChannels(image));
780 if (proceed == MagickFalse)
784 image_view=DestroyCacheView(image_view);
785 pixel_cache=RelinquishVirtualMemory(pixel_cache);
786 if (TransformImageColorspace(image,colorspace,exception) == MagickFalse)
836MagickExport MagickBooleanType ClutImage(
Image *image,
const Image *clut_image,
837 const PixelInterpolateMethod method,
ExceptionInfo *exception)
839#define ClutImageTag "Clut/Image"
859 assert(image != (
Image *) NULL);
860 assert(image->signature == MagickCoreSignature);
861 assert(clut_image != (
Image *) NULL);
862 assert(clut_image->signature == MagickCoreSignature);
863 if (IsEventLogging() != MagickFalse)
864 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
865 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
867 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
868 (IsGrayColorspace(clut_image->colorspace) == MagickFalse))
869 (void) SetImageColorspace(image,sRGBColorspace,exception);
870 clut_map=(
PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*clut_map));
872 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
879 adjust=(ssize_t) (method == IntegerInterpolatePixel ? 0 : 1);
880 clut_view=AcquireVirtualCacheView(clut_image,exception);
881 for (i=0; i <= (ssize_t) MaxMap; i++)
883 GetPixelInfo(clut_image,clut_map+i);
884 status=InterpolatePixelInfo(clut_image,clut_view,method,(
double) i*
885 ((
double) clut_image->columns-adjust)/MaxMap,(
double) i*
886 ((
double) clut_image->rows-adjust)/MaxMap,clut_map+i,exception);
887 if (status == MagickFalse)
890 clut_view=DestroyCacheView(clut_view);
891 image_view=AcquireAuthenticCacheView(image,exception);
892#if defined(MAGICKCORE_OPENMP_SUPPORT)
893 #pragma omp parallel for schedule(static) shared(progress,status) \
894 magick_number_threads(image,image,image->rows,1)
896 for (y=0; y < (ssize_t) image->rows; y++)
907 if (status == MagickFalse)
909 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
910 if (q == (Quantum *) NULL)
915 GetPixelInfo(image,&pixel);
916 for (x=0; x < (ssize_t) image->columns; x++)
921 GetPixelInfoPixel(image,q,&pixel);
922 traits=GetPixelChannelTraits(image,RedPixelChannel);
923 if ((traits & UpdatePixelTrait) != 0)
924 pixel.red=clut_map[ScaleQuantumToMap(ClampToQuantum(
926 traits=GetPixelChannelTraits(image,GreenPixelChannel);
927 if ((traits & UpdatePixelTrait) != 0)
928 pixel.green=clut_map[ScaleQuantumToMap(ClampToQuantum(
929 pixel.green))].green;
930 traits=GetPixelChannelTraits(image,BluePixelChannel);
931 if ((traits & UpdatePixelTrait) != 0)
932 pixel.blue=clut_map[ScaleQuantumToMap(ClampToQuantum(
934 traits=GetPixelChannelTraits(image,BlackPixelChannel);
935 if ((traits & UpdatePixelTrait) != 0)
936 pixel.black=clut_map[ScaleQuantumToMap(ClampToQuantum(
937 pixel.black))].black;
938 traits=GetPixelChannelTraits(image,AlphaPixelChannel);
939 if ((traits & UpdatePixelTrait) != 0)
940 pixel.alpha=clut_map[ScaleQuantumToMap(ClampToQuantum(
941 pixel.alpha))].alpha;
942 SetPixelViaPixelInfo(image,&pixel,q);
943 q+=(ptrdiff_t) GetPixelChannels(image);
945 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
947 if (image->progress_monitor != (MagickProgressMonitor) NULL)
952#if defined(MAGICKCORE_OPENMP_SUPPORT)
956 proceed=SetImageProgress(image,ClutImageTag,progress,image->rows);
957 if (proceed == MagickFalse)
961 image_view=DestroyCacheView(image_view);
962 clut_map=(
PixelInfo *) RelinquishMagickMemory(clut_map);
963 if ((clut_image->alpha_trait != UndefinedPixelTrait) &&
964 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
965 (
void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
1014MagickExport MagickBooleanType ColorDecisionListImage(
Image *image,
1015 const char *color_correction_collection,
ExceptionInfo *exception)
1017#define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
1019 typedef struct _Correction
1027 typedef struct _ColorCorrection
1042 token[MagickPathExtent];
1075 assert(image != (
Image *) NULL);
1076 assert(image->signature == MagickCoreSignature);
1077 if (IsEventLogging() != MagickFalse)
1078 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1079 if (color_correction_collection == (
const char *) NULL)
1080 return(MagickFalse);
1081 ccc=NewXMLTree((
const char *) color_correction_collection,exception);
1083 return(MagickFalse);
1084 cc=GetXMLTreeChild(ccc,
"ColorCorrection");
1087 ccc=DestroyXMLTree(ccc);
1088 return(MagickFalse);
1090 color_correction.red.slope=1.0;
1091 color_correction.red.offset=0.0;
1092 color_correction.red.power=1.0;
1093 color_correction.green.slope=1.0;
1094 color_correction.green.offset=0.0;
1095 color_correction.green.power=1.0;
1096 color_correction.blue.slope=1.0;
1097 color_correction.blue.offset=0.0;
1098 color_correction.blue.power=1.0;
1099 color_correction.saturation=0.0;
1100 sop=GetXMLTreeChild(cc,
"SOPNode");
1108 slope=GetXMLTreeChild(sop,
"Slope");
1111 content=GetXMLTreeContent(slope);
1112 p=(
const char *) content;
1113 for (i=0; (*p !=
'\0') && (i < 3); i++)
1115 (void) GetNextToken(p,&p,MagickPathExtent,token);
1117 (void) GetNextToken(p,&p,MagickPathExtent,token);
1122 color_correction.red.slope=StringToDouble(token,(
char **) NULL);
1127 color_correction.green.slope=StringToDouble(token,
1133 color_correction.blue.slope=StringToDouble(token,
1140 offset=GetXMLTreeChild(sop,
"Offset");
1143 content=GetXMLTreeContent(offset);
1144 p=(
const char *) content;
1145 for (i=0; (*p !=
'\0') && (i < 3); i++)
1147 (void) GetNextToken(p,&p,MagickPathExtent,token);
1149 (void) GetNextToken(p,&p,MagickPathExtent,token);
1154 color_correction.red.offset=StringToDouble(token,
1160 color_correction.green.offset=StringToDouble(token,
1166 color_correction.blue.offset=StringToDouble(token,
1173 power=GetXMLTreeChild(sop,
"Power");
1176 content=GetXMLTreeContent(power);
1177 p=(
const char *) content;
1178 for (i=0; (*p !=
'\0') && (i < 3); i++)
1180 (void) GetNextToken(p,&p,MagickPathExtent,token);
1182 (void) GetNextToken(p,&p,MagickPathExtent,token);
1187 color_correction.red.power=StringToDouble(token,(
char **) NULL);
1192 color_correction.green.power=StringToDouble(token,
1198 color_correction.blue.power=StringToDouble(token,
1206 sat=GetXMLTreeChild(cc,
"SATNode");
1212 saturation=GetXMLTreeChild(sat,
"Saturation");
1215 content=GetXMLTreeContent(saturation);
1216 p=(
const char *) content;
1217 (void) GetNextToken(p,&p,MagickPathExtent,token);
1218 color_correction.saturation=StringToDouble(token,(
char **) NULL);
1221 ccc=DestroyXMLTree(ccc);
1222 if (image->debug != MagickFalse)
1224 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1225 " Color Correction Collection:");
1226 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1227 " color_correction.red.slope: %g",color_correction.red.slope);
1228 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1229 " color_correction.red.offset: %g",color_correction.red.offset);
1230 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1231 " color_correction.red.power: %g",color_correction.red.power);
1232 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1233 " color_correction.green.slope: %g",color_correction.green.slope);
1234 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1235 " color_correction.green.offset: %g",color_correction.green.offset);
1236 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1237 " color_correction.green.power: %g",color_correction.green.power);
1238 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1239 " color_correction.blue.slope: %g",color_correction.blue.slope);
1240 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1241 " color_correction.blue.offset: %g",color_correction.blue.offset);
1242 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1243 " color_correction.blue.power: %g",color_correction.blue.power);
1244 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1245 " color_correction.saturation: %g",color_correction.saturation);
1247 cdl_map=(
PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*cdl_map));
1249 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
1251 for (i=0; i <= (ssize_t) MaxMap; i++)
1253 cdl_map[i].red=(double) ScaleMapToQuantum((
double)
1254 (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
1255 color_correction.red.offset,color_correction.red.power))));
1256 cdl_map[i].green=(double) ScaleMapToQuantum((
double)
1257 (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
1258 color_correction.green.offset,color_correction.green.power))));
1259 cdl_map[i].blue=(double) ScaleMapToQuantum((
double)
1260 (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
1261 color_correction.blue.offset,color_correction.blue.power))));
1263 if (image->storage_class == PseudoClass)
1264 for (i=0; i < (ssize_t) image->colors; i++)
1272 luma=0.21267*image->colormap[i].red+0.71526*image->colormap[i].green+
1273 0.07217*image->colormap[i].blue;
1274 image->colormap[i].red=luma+color_correction.saturation*cdl_map[
1275 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
1276 image->colormap[i].green=luma+color_correction.saturation*cdl_map[
1277 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
1278 image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
1279 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
1286 image_view=AcquireAuthenticCacheView(image,exception);
1287#if defined(MAGICKCORE_OPENMP_SUPPORT)
1288 #pragma omp parallel for schedule(static) shared(progress,status) \
1289 magick_number_threads(image,image,image->rows,1)
1291 for (y=0; y < (ssize_t) image->rows; y++)
1302 if (status == MagickFalse)
1304 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1305 if (q == (Quantum *) NULL)
1310 for (x=0; x < (ssize_t) image->columns; x++)
1312 luma=0.21267*(double) GetPixelRed(image,q)+0.71526*(double)
1313 GetPixelGreen(image,q)+0.07217*(double) GetPixelBlue(image,q);
1314 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
1315 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
1316 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
1317 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
1318 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
1319 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
1320 q+=(ptrdiff_t) GetPixelChannels(image);
1322 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1324 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1329#if defined(MAGICKCORE_OPENMP_SUPPORT)
1333 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
1334 progress,image->rows);
1335 if (proceed == MagickFalse)
1339 image_view=DestroyCacheView(image_view);
1340 cdl_map=(
PixelInfo *) RelinquishMagickMemory(cdl_map);
1374static inline void Contrast(
const int sign,
double *red,
double *green,
1385 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
1386 brightness+=0.5*sign*(0.5*(sin((
double) (MagickPI*(brightness-0.5)))+1.0)-
1388 if (brightness > 1.0)
1391 if (brightness < 0.0)
1393 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
1396MagickExport MagickBooleanType ContrastImage(
Image *image,
1399#define ContrastImageTag "Contrast/Image"
1419 assert(image != (
Image *) NULL);
1420 assert(image->signature == MagickCoreSignature);
1421#if defined(MAGICKCORE_OPENCL_SUPPORT)
1422 if (AccelerateContrastImage(image,sharpen,exception) != MagickFalse)
1425 if (IsEventLogging() != MagickFalse)
1426 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1427 sign=sharpen != MagickFalse ? 1 : -1;
1428 if (image->storage_class == PseudoClass)
1433 for (i=0; i < (ssize_t) image->colors; i++)
1440 red=(double) image->colormap[i].red;
1441 green=(double) image->colormap[i].green;
1442 blue=(double) image->colormap[i].blue;
1443 Contrast(sign,&red,&green,&blue);
1444 image->colormap[i].red=(MagickRealType) red;
1445 image->colormap[i].green=(MagickRealType) green;
1446 image->colormap[i].blue=(MagickRealType) blue;
1454 image_view=AcquireAuthenticCacheView(image,exception);
1455#if defined(MAGICKCORE_OPENMP_SUPPORT)
1456 #pragma omp parallel for schedule(static) shared(progress,status) \
1457 magick_number_threads(image,image,image->rows,1)
1459 for (y=0; y < (ssize_t) image->rows; y++)
1472 if (status == MagickFalse)
1474 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1475 if (q == (Quantum *) NULL)
1480 for (x=0; x < (ssize_t) image->columns; x++)
1482 red=(double) GetPixelRed(image,q);
1483 green=(double) GetPixelGreen(image,q);
1484 blue=(double) GetPixelBlue(image,q);
1485 Contrast(sign,&red,&green,&blue);
1486 SetPixelRed(image,ClampToQuantum(red),q);
1487 SetPixelGreen(image,ClampToQuantum(green),q);
1488 SetPixelBlue(image,ClampToQuantum(blue),q);
1489 q+=(ptrdiff_t) GetPixelChannels(image);
1491 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1493 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1498#if defined(MAGICKCORE_OPENMP_SUPPORT)
1502 proceed=SetImageProgress(image,ContrastImageTag,progress,image->rows);
1503 if (proceed == MagickFalse)
1507 image_view=DestroyCacheView(image_view);
1548MagickExport MagickBooleanType ContrastStretchImage(
Image *image,
1549 const double black_point,
const double white_point,
ExceptionInfo *exception)
1551#define ContrastStretchImageTag "ContrastStretch/Image"
1557 property[MagickPathExtent];
1585 assert(image != (
Image *) NULL);
1586 assert(image->signature == MagickCoreSignature);
1587 if (IsEventLogging() != MagickFalse)
1588 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1589 type=IdentifyImageType(image,exception);
1590 if (IsGrayImageType(type) != MagickFalse)
1591 (void) SetImageColorspace(image,GRAYColorspace,exception);
1592 black=(Quantum *) AcquireQuantumMemory(MaxPixelChannels,
sizeof(*black));
1593 white=(Quantum *) AcquireQuantumMemory(MaxPixelChannels,
sizeof(*white));
1594 stretch_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1595 sizeof(*stretch_map));
1596 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1597 sizeof(*histogram));
1598 if ((black == (Quantum *) NULL) || (white == (Quantum *) NULL) ||
1599 (stretch_map == (Quantum *) NULL) || (histogram == (
double *) NULL))
1601 if (histogram != (
double *) NULL)
1602 histogram=(
double *) RelinquishMagickMemory(histogram);
1603 if (stretch_map != (Quantum *) NULL)
1604 stretch_map=(Quantum *) RelinquishMagickMemory(stretch_map);
1605 if (white != (Quantum *) NULL)
1606 white=(Quantum *) RelinquishMagickMemory(white);
1607 if (black != (Quantum *) NULL)
1608 black=(Quantum *) RelinquishMagickMemory(black);
1609 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
1616 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1617 sizeof(*histogram));
1618 image_view=AcquireVirtualCacheView(image,exception);
1619 for (y=0; y < (ssize_t) image->rows; y++)
1627 if (status == MagickFalse)
1629 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1630 if (p == (
const Quantum *) NULL)
1635 for (x=0; x < (ssize_t) image->columns; x++)
1640 pixel=GetPixelIntensity(image,p);
1641 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1643 if (image->channel_mask != AllChannels)
1644 pixel=(double) p[i];
1645 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1646 ClampToQuantum(pixel))+(size_t) i]++;
1648 p+=(ptrdiff_t) GetPixelChannels(image);
1651 image_view=DestroyCacheView(image_view);
1655 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1663 black[i]=(Quantum) 0;
1664 white[i]=(Quantum) ScaleQuantumToMap(QuantumRange);
1666 for (j=0; j <= (ssize_t) MaxMap; j++)
1668 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
1669 if (intensity > black_point)
1672 black[i]=(Quantum) j;
1674 for (j=(ssize_t) MaxMap; j != 0; j--)
1676 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
1677 if (intensity > ((
double) image->columns*image->rows-white_point))
1680 white[i]=(Quantum) j;
1682 histogram=(
double *) RelinquishMagickMemory(histogram);
1686 (void) memset(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1687 sizeof(*stretch_map));
1688 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1693 for (j=0; j <= (ssize_t) MaxMap; j++)
1698 gamma=MagickSafeReciprocal(white[i]-black[i]);
1699 if (j < (ssize_t) black[i])
1700 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=(Quantum) 0;
1702 if (j > (ssize_t) white[i])
1703 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=QuantumRange;
1705 if (black[i] != white[i])
1706 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=
1707 ScaleMapToQuantum((
double) (MaxMap*gamma*(j-(
double) black[i])));
1710 if (image->storage_class == PseudoClass)
1718 for (j=0; j < (ssize_t) image->colors; j++)
1720 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1722 i=GetPixelChannelOffset(image,RedPixelChannel);
1723 image->colormap[j].red=(MagickRealType) stretch_map[
1724 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1725 image->colormap[j].red))+(size_t) i];
1727 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1729 i=GetPixelChannelOffset(image,GreenPixelChannel);
1730 image->colormap[j].green=(MagickRealType) stretch_map[
1731 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1732 image->colormap[j].green))+(size_t) i];
1734 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1736 i=GetPixelChannelOffset(image,BluePixelChannel);
1737 image->colormap[j].blue=(MagickRealType) stretch_map[
1738 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1739 image->colormap[j].blue))+(size_t) i];
1741 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1743 i=GetPixelChannelOffset(image,AlphaPixelChannel);
1744 image->colormap[j].alpha=(MagickRealType) stretch_map[
1745 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1746 image->colormap[j].alpha))+(size_t) i];
1755 image_view=AcquireAuthenticCacheView(image,exception);
1756#if defined(MAGICKCORE_OPENMP_SUPPORT)
1757 #pragma omp parallel for schedule(static) shared(progress,status) \
1758 magick_number_threads(image,image,image->rows,1)
1760 for (y=0; y < (ssize_t) image->rows; y++)
1768 if (status == MagickFalse)
1770 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1771 if (q == (Quantum *) NULL)
1776 for (x=0; x < (ssize_t) image->columns; x++)
1781 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1783 PixelChannel channel = GetPixelChannelChannel(image,j);
1784 PixelTrait traits = GetPixelChannelTraits(image,channel);
1785 if ((traits & UpdatePixelTrait) == 0)
1787 if (black[j] == white[j])
1789 q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1790 ScaleQuantumToMap(q[j])+(
size_t) j]);
1792 q+=(ptrdiff_t) GetPixelChannels(image);
1794 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1796 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1801#if defined(MAGICKCORE_OPENMP_SUPPORT)
1805 proceed=SetImageProgress(image,ContrastStretchImageTag,progress,
1807 if (proceed == MagickFalse)
1811 image_view=DestroyCacheView(image_view);
1812 (void) FormatLocaleString(property,MagickPathExtent,
"%gx%g%%",100.0*
1813 QuantumScale*GetPixelIntensity(image,black),100.0*QuantumScale*
1814 GetPixelIntensity(image,white));
1815 (void) SetImageProperty(image,
"histogram:contrast-stretch",property,
1817 white=(Quantum *) RelinquishMagickMemory(white);
1818 black=(Quantum *) RelinquishMagickMemory(black);
1819 stretch_map=(Quantum *) RelinquishMagickMemory(stretch_map);
1850#define EnhanceImageTag "Enhance/Image"
1851#define EnhancePixel(weight) \
1852 mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1853 distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1854 distance_squared=(4.0+mean)*distance*distance; \
1855 mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1856 distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1857 distance_squared+=(7.0-mean)*distance*distance; \
1858 mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1859 distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1860 distance_squared+=(5.0-mean)*distance*distance; \
1861 mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1862 distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1863 distance_squared+=(5.0-mean)*distance*distance; \
1864 mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1865 distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1866 distance_squared+=(5.0-mean)*distance*distance; \
1867 if (distance_squared < 0.069) \
1869 aggregate.red+=(weight)*(double) GetPixelRed(image,r); \
1870 aggregate.green+=(weight)*(double) GetPixelGreen(image,r); \
1871 aggregate.blue+=(weight)*(double) GetPixelBlue(image,r); \
1872 aggregate.black+=(weight)*(double) GetPixelBlack(image,r); \
1873 aggregate.alpha+=(weight)*(double) GetPixelAlpha(image,r); \
1874 total_weight+=(weight); \
1876 r+=(ptrdiff_t) GetPixelChannels(image);
1897 assert(image != (
const Image *) NULL);
1898 assert(image->signature == MagickCoreSignature);
1899 if (IsEventLogging() != MagickFalse)
1900 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1902 assert(exception->signature == MagickCoreSignature);
1903 enhance_image=CloneImage(image,0,0,MagickTrue,
1905 if (enhance_image == (
Image *) NULL)
1906 return((
Image *) NULL);
1907 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1909 enhance_image=DestroyImage(enhance_image);
1910 return((
Image *) NULL);
1917 image_view=AcquireVirtualCacheView(image,exception);
1918 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1919#if defined(MAGICKCORE_OPENMP_SUPPORT)
1920 #pragma omp parallel for schedule(static) shared(progress,status) \
1921 magick_number_threads(image,enhance_image,image->rows,1)
1923 for (y=0; y < (ssize_t) image->rows; y++)
1940 if (status == MagickFalse)
1942 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1943 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1945 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
1950 center=(ssize_t) GetPixelChannels(image)*(2*((ssize_t) image->columns+4)+2);
1951 GetPixelInfo(image,&pixel);
1952 for (x=0; x < (ssize_t) image->columns; x++)
1966 GetPixelInfo(image,&aggregate);
1968 GetPixelInfoPixel(image,p+center,&pixel);
1970 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1971 EnhancePixel(8.0); EnhancePixel(5.0);
1972 r=p+GetPixelChannels(image)*(image->columns+4);
1973 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1974 EnhancePixel(20.0); EnhancePixel(8.0);
1975 r=p+2*GetPixelChannels(image)*(image->columns+4);
1976 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1977 EnhancePixel(40.0); EnhancePixel(10.0);
1978 r=p+3*GetPixelChannels(image)*(image->columns+4);
1979 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1980 EnhancePixel(20.0); EnhancePixel(8.0);
1981 r=p+4*GetPixelChannels(image)*(image->columns+4);
1982 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1983 EnhancePixel(8.0); EnhancePixel(5.0);
1984 if (total_weight > MagickEpsilon)
1986 pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1987 pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1988 pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1989 pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1990 pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
1992 SetPixelViaPixelInfo(enhance_image,&pixel,q);
1993 p+=(ptrdiff_t) GetPixelChannels(image);
1994 q+=(ptrdiff_t) GetPixelChannels(enhance_image);
1996 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1998 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2003#if defined(MAGICKCORE_OPENMP_SUPPORT)
2007 proceed=SetImageProgress(image,EnhanceImageTag,progress,image->rows);
2008 if (proceed == MagickFalse)
2012 enhance_view=DestroyCacheView(enhance_view);
2013 image_view=DestroyCacheView(image_view);
2014 if (status == MagickFalse)
2015 enhance_image=DestroyImage(enhance_image);
2016 return(enhance_image);
2043MagickExport MagickBooleanType EqualizeImage(
Image *image,
2046#define EqualizeImageTag "Equalize/Image"
2052 black[2*CompositePixelChannel+1],
2056 white[2*CompositePixelChannel+1];
2073 assert(image != (
Image *) NULL);
2074 assert(image->signature == MagickCoreSignature);
2075#if defined(MAGICKCORE_OPENCL_SUPPORT)
2076 if (AccelerateEqualizeImage(image,exception) != MagickFalse)
2079 if (IsEventLogging() != MagickFalse)
2080 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2081 equalize_map=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2082 sizeof(*equalize_map));
2083 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2084 sizeof(*histogram));
2085 map=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
sizeof(*map));
2086 if ((equalize_map == (
double *) NULL) || (histogram == (
double *) NULL) ||
2087 (map == (
double *) NULL))
2089 if (map != (
double *) NULL)
2090 map=(
double *) RelinquishMagickMemory(map);
2091 if (histogram != (
double *) NULL)
2092 histogram=(
double *) RelinquishMagickMemory(histogram);
2093 if (equalize_map != (
double *) NULL)
2094 equalize_map=(
double *) RelinquishMagickMemory(equalize_map);
2095 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2102 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
2103 sizeof(*histogram));
2104 image_view=AcquireVirtualCacheView(image,exception);
2105 for (y=0; y < (ssize_t) image->rows; y++)
2113 if (status == MagickFalse)
2115 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2116 if (p == (
const Quantum *) NULL)
2121 for (x=0; x < (ssize_t) image->columns; x++)
2123 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2128 intensity=(double) p[i];
2129 if ((image->channel_mask & SyncChannels) != 0)
2130 intensity=GetPixelIntensity(image,p);
2131 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2132 ClampToQuantum(intensity))+(size_t) i]++;
2134 p+=(ptrdiff_t) GetPixelChannels(image);
2137 image_view=DestroyCacheView(image_view);
2141 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2150 for (j=0; j <= (ssize_t) MaxMap; j++)
2152 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
2153 map[(ssize_t) GetPixelChannels(image)*j+i]=intensity;
2156 (void) memset(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
2157 sizeof(*equalize_map));
2158 (void) memset(black,0,
sizeof(*black));
2159 (void) memset(white,0,
sizeof(*white));
2160 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2166 white[i]=map[GetPixelChannels(image)*MaxMap+(size_t) i];
2167 if (black[i] != white[i])
2168 for (j=0; j <= (ssize_t) MaxMap; j++)
2169 equalize_map[GetPixelChannels(image)*(size_t) j+(
size_t) i]=(double)
2170 ScaleMapToQuantum((
double) ((MaxMap*(map[GetPixelChannels(image)*
2171 (size_t) j+(
size_t) i]-black[i]))/(white[i]-black[i])));
2173 histogram=(
double *) RelinquishMagickMemory(histogram);
2174 map=(
double *) RelinquishMagickMemory(map);
2175 if (image->storage_class == PseudoClass)
2183 for (j=0; j < (ssize_t) image->colors; j++)
2185 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2187 PixelChannel channel = GetPixelChannelChannel(image,
2189 if (black[channel] != white[channel])
2190 image->colormap[j].red=equalize_map[(ssize_t)
2191 GetPixelChannels(image)*ScaleQuantumToMap(
2192 ClampToQuantum(image->colormap[j].red))+channel];
2194 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2196 PixelChannel channel = GetPixelChannelChannel(image,
2198 if (black[channel] != white[channel])
2199 image->colormap[j].green=equalize_map[(ssize_t)
2200 GetPixelChannels(image)*ScaleQuantumToMap(
2201 ClampToQuantum(image->colormap[j].green))+channel];
2203 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2205 PixelChannel channel = GetPixelChannelChannel(image,
2207 if (black[channel] != white[channel])
2208 image->colormap[j].blue=equalize_map[(ssize_t)
2209 GetPixelChannels(image)*ScaleQuantumToMap(
2210 ClampToQuantum(image->colormap[j].blue))+channel];
2212 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2214 PixelChannel channel = GetPixelChannelChannel(image,
2216 if (black[channel] != white[channel])
2217 image->colormap[j].alpha=equalize_map[(ssize_t)
2218 GetPixelChannels(image)*ScaleQuantumToMap(
2219 ClampToQuantum(image->colormap[j].alpha))+channel];
2227 image_view=AcquireAuthenticCacheView(image,exception);
2228#if defined(MAGICKCORE_OPENMP_SUPPORT)
2229 #pragma omp parallel for schedule(static) shared(progress,status) \
2230 magick_number_threads(image,image,image->rows,1)
2232 for (y=0; y < (ssize_t) image->rows; y++)
2240 if (status == MagickFalse)
2242 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2243 if (q == (Quantum *) NULL)
2248 for (x=0; x < (ssize_t) image->columns; x++)
2253 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2255 PixelChannel channel = GetPixelChannelChannel(image,j);
2256 PixelTrait traits = GetPixelChannelTraits(image,channel);
2257 if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
2259 q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
2260 ScaleQuantumToMap(q[j])+(
size_t) j]);
2262 q+=(ptrdiff_t) GetPixelChannels(image);
2264 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2266 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2271#if defined(MAGICKCORE_OPENMP_SUPPORT)
2275 proceed=SetImageProgress(image,EqualizeImageTag,progress,image->rows);
2276 if (proceed == MagickFalse)
2280 image_view=DestroyCacheView(image_view);
2281 equalize_map=(
double *) RelinquishMagickMemory(equalize_map);
2320static inline double gamma_pow(
const double value,
const double gamma)
2322 return(value < 0.0 ? value : pow(value,gamma));
2325MagickExport MagickBooleanType GammaImage(
Image *image,
const double gamma,
2328#define GammaImageTag "Gamma/Image"
2351 assert(image != (
Image *) NULL);
2352 assert(image->signature == MagickCoreSignature);
2353 if (IsEventLogging() != MagickFalse)
2354 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2357 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*gamma_map));
2358 if (gamma_map == (Quantum *) NULL)
2359 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2361 (void) memset(gamma_map,0,(MaxMap+1)*
sizeof(*gamma_map));
2363 for (i=0; i <= (ssize_t) MaxMap; i++)
2364 gamma_map[i]=ScaleMapToQuantum((
double) (MaxMap*pow((
double) i/
2365 MaxMap,MagickSafeReciprocal(gamma))));
2366 if (image->storage_class == PseudoClass)
2367 for (i=0; i < (ssize_t) image->colors; i++)
2372 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2373 image->colormap[i].red=(
double) gamma_map[ScaleQuantumToMap(
2374 ClampToQuantum(image->colormap[i].red))];
2375 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2376 image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
2377 ClampToQuantum(image->colormap[i].green))];
2378 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2379 image->colormap[i].blue=(
double) gamma_map[ScaleQuantumToMap(
2380 ClampToQuantum(image->colormap[i].blue))];
2381 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2382 image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
2383 ClampToQuantum(image->colormap[i].alpha))];
2390 image_view=AcquireAuthenticCacheView(image,exception);
2391#if defined(MAGICKCORE_OPENMP_SUPPORT)
2392 #pragma omp parallel for schedule(static) shared(progress,status) \
2393 magick_number_threads(image,image,image->rows,1)
2395 for (y=0; y < (ssize_t) image->rows; y++)
2403 if (status == MagickFalse)
2405 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2406 if (q == (Quantum *) NULL)
2411 for (x=0; x < (ssize_t) image->columns; x++)
2416 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2418 PixelChannel channel = GetPixelChannelChannel(image,j);
2419 PixelTrait traits = GetPixelChannelTraits(image,channel);
2420 if ((traits & UpdatePixelTrait) == 0)
2422 q[j]=gamma_map[ScaleQuantumToMap(ClampToQuantum((MagickRealType)
2425 q+=(ptrdiff_t) GetPixelChannels(image);
2427 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2429 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2434#if defined(MAGICKCORE_OPENMP_SUPPORT)
2438 proceed=SetImageProgress(image,GammaImageTag,progress,image->rows);
2439 if (proceed == MagickFalse)
2443 image_view=DestroyCacheView(image_view);
2444 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
2445 if (image->gamma != 0.0)
2446 image->gamma*=gamma;
2477MagickExport MagickBooleanType GrayscaleImage(
Image *image,
2480#define GrayscaleImageTag "Grayscale/Image"
2494 assert(image != (
Image *) NULL);
2495 assert(image->signature == MagickCoreSignature);
2496 if (IsEventLogging() != MagickFalse)
2497 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2498 if (image->storage_class == PseudoClass)
2500 if (SyncImage(image,exception) == MagickFalse)
2501 return(MagickFalse);
2502 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2503 return(MagickFalse);
2505#if defined(MAGICKCORE_OPENCL_SUPPORT)
2506 if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
2508 image->intensity=method;
2509 image->type=GrayscaleType;
2510 if ((method == Rec601LuminancePixelIntensityMethod) ||
2511 (method == Rec709LuminancePixelIntensityMethod))
2512 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2513 return(SetImageColorspace(image,GRAYColorspace,exception));
2521 image_view=AcquireAuthenticCacheView(image,exception);
2522#if defined(MAGICKCORE_OPENMP_SUPPORT)
2523 #pragma omp parallel for schedule(static) shared(progress,status) \
2524 magick_number_threads(image,image,image->rows,1)
2526 for (y=0; y < (ssize_t) image->rows; y++)
2534 if (status == MagickFalse)
2536 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2537 if (q == (Quantum *) NULL)
2542 for (x=0; x < (ssize_t) image->columns; x++)
2550 red=(MagickRealType) GetPixelRed(image,q);
2551 green=(MagickRealType) GetPixelGreen(image,q);
2552 blue=(MagickRealType) GetPixelBlue(image,q);
2556 case AveragePixelIntensityMethod:
2558 intensity=(red+green+blue)/3.0;
2561 case BrightnessPixelIntensityMethod:
2563 intensity=MagickMax(MagickMax(red,green),blue);
2566 case LightnessPixelIntensityMethod:
2568 intensity=(MagickMin(MagickMin(red,green),blue)+
2569 MagickMax(MagickMax(red,green),blue))/2.0;
2572 case MSPixelIntensityMethod:
2574 intensity=(MagickRealType) (((
double) red*red+green*green+
2578 case Rec601LumaPixelIntensityMethod:
2580 if (image->colorspace == RGBColorspace)
2582 red=EncodePixelGamma(red);
2583 green=EncodePixelGamma(green);
2584 blue=EncodePixelGamma(blue);
2586 intensity=0.298839*red+0.586811*green+0.114350*blue;
2589 case Rec601LuminancePixelIntensityMethod:
2591 if (image->colorspace == sRGBColorspace)
2593 red=DecodePixelGamma(red);
2594 green=DecodePixelGamma(green);
2595 blue=DecodePixelGamma(blue);
2597 intensity=0.298839*red+0.586811*green+0.114350*blue;
2600 case Rec709LumaPixelIntensityMethod:
2603 if (image->colorspace == RGBColorspace)
2605 red=EncodePixelGamma(red);
2606 green=EncodePixelGamma(green);
2607 blue=EncodePixelGamma(blue);
2609 intensity=0.212656*red+0.715158*green+0.072186*blue;
2612 case Rec709LuminancePixelIntensityMethod:
2614 if (image->colorspace == sRGBColorspace)
2616 red=DecodePixelGamma(red);
2617 green=DecodePixelGamma(green);
2618 blue=DecodePixelGamma(blue);
2620 intensity=0.212656*red+0.715158*green+0.072186*blue;
2623 case RMSPixelIntensityMethod:
2625 intensity=(MagickRealType) (sqrt((
double) red*red+green*green+
2626 blue*blue)/sqrt(3.0));
2630 SetPixelGray(image,ClampToQuantum(intensity),q);
2631 q+=(ptrdiff_t) GetPixelChannels(image);
2633 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2635 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2640#if defined(MAGICKCORE_OPENMP_SUPPORT)
2644 proceed=SetImageProgress(image,GrayscaleImageTag,progress,image->rows);
2645 if (proceed == MagickFalse)
2649 image_view=DestroyCacheView(image_view);
2650 image->intensity=method;
2651 image->type=GrayscaleType;
2652 if ((method == Rec601LuminancePixelIntensityMethod) ||
2653 (method == Rec709LuminancePixelIntensityMethod))
2654 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2655 return(SetImageColorspace(image,GRAYColorspace,exception));
2689MagickExport MagickBooleanType HaldClutImage(
Image *image,
2692#define HaldClutImageTag "Clut/Image"
2694 typedef struct _HaldInfo
2726 assert(image != (
Image *) NULL);
2727 assert(image->signature == MagickCoreSignature);
2728 if (IsEventLogging() != MagickFalse)
2729 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2730 assert(hald_image != (
Image *) NULL);
2731 assert(hald_image->signature == MagickCoreSignature);
2732 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2733 return(MagickFalse);
2734 if ((image->alpha_trait & BlendPixelTrait) == 0)
2735 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2736 if (image->colorspace != hald_image->colorspace)
2737 (void) SetImageColorspace(image,hald_image->colorspace,exception);
2743 length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2744 (MagickRealType) hald_image->rows);
2745 for (level=2; (level*level*level) < length; level++) ;
2747 cube_size=level*level;
2748 width=(double) hald_image->columns;
2749 GetPixelInfo(hald_image,&zero);
2750 hald_view=AcquireVirtualCacheView(hald_image,exception);
2751 image_view=AcquireAuthenticCacheView(image,exception);
2752#if defined(MAGICKCORE_OPENMP_SUPPORT)
2753 #pragma omp parallel for schedule(static) shared(progress,status) \
2754 magick_number_threads(image,image,image->rows,1)
2756 for (y=0; y < (ssize_t) image->rows; y++)
2764 if (status == MagickFalse)
2766 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2767 if (q == (Quantum *) NULL)
2772 for (x=0; x < (ssize_t) image->columns; x++)
2779 point = { 0, 0, 0 };
2788 point.x=QuantumScale*(level-1.0)*(
double) GetPixelRed(image,q);
2789 point.y=QuantumScale*(level-1.0)*(
double) GetPixelGreen(image,q);
2790 point.z=QuantumScale*(level-1.0)*(
double) GetPixelBlue(image,q);
2791 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2792 point.x-=floor(point.x);
2793 point.y-=floor(point.y);
2794 point.z-=floor(point.z);
2795 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2796 fmod(offset,width),floor(offset/width),&pixel1,exception);
2797 if (status == MagickFalse)
2799 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2800 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2801 if (status == MagickFalse)
2804 if (hald_image->interpolate == NearestInterpolatePixel)
2805 area=(point.y < 0.5) ? 0.0 : 1.0;
2806 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2809 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2810 fmod(offset,width),floor(offset/width),&pixel1,exception);
2811 if (status == MagickFalse)
2813 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2814 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2815 if (status == MagickFalse)
2817 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2820 if (hald_image->interpolate == NearestInterpolatePixel)
2821 area=(point.z < 0.5)? 0.0 : 1.0;
2822 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2824 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2825 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2826 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2827 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2828 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2829 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2830 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2831 (image->colorspace == CMYKColorspace))
2832 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2833 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2834 (image->alpha_trait != UndefinedPixelTrait))
2835 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2836 q+=(ptrdiff_t) GetPixelChannels(image);
2838 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2840 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2845#if defined(MAGICKCORE_OPENMP_SUPPORT)
2849 proceed=SetImageProgress(image,HaldClutImageTag,progress,image->rows);
2850 if (proceed == MagickFalse)
2854 hald_view=DestroyCacheView(hald_view);
2855 image_view=DestroyCacheView(image_view);
2903static inline double LevelPixel(
const double black_point,
2904 const double white_point,
const double gamma,
const double pixel)
2910 scale=MagickSafeReciprocal(white_point-black_point);
2911 level_pixel=(double) QuantumRange*gamma_pow(scale*((
double) pixel-(
double)
2912 black_point),MagickSafeReciprocal(gamma));
2913 return(level_pixel);
2916MagickExport MagickBooleanType LevelImage(
Image *image,
const double black_point,
2917 const double white_point,
const double gamma,
ExceptionInfo *exception)
2919#define LevelImageTag "Level/Image"
2937 assert(image != (
Image *) NULL);
2938 assert(image->signature == MagickCoreSignature);
2939 if (IsEventLogging() != MagickFalse)
2940 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2941 if (image->storage_class == PseudoClass)
2942 for (i=0; i < (ssize_t) image->colors; i++)
2947 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2948 image->colormap[i].red=(
double) ClampToQuantum(LevelPixel(black_point,
2949 white_point,gamma,image->colormap[i].red));
2950 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2951 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2952 white_point,gamma,image->colormap[i].green));
2953 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2954 image->colormap[i].blue=(
double) ClampToQuantum(LevelPixel(black_point,
2955 white_point,gamma,image->colormap[i].blue));
2956 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2957 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2958 white_point,gamma,image->colormap[i].alpha));
2965 image_view=AcquireAuthenticCacheView(image,exception);
2966#if defined(MAGICKCORE_OPENMP_SUPPORT)
2967 #pragma omp parallel for schedule(static) shared(progress,status) \
2968 magick_number_threads(image,image,image->rows,1)
2970 for (y=0; y < (ssize_t) image->rows; y++)
2978 if (status == MagickFalse)
2980 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2981 if (q == (Quantum *) NULL)
2986 for (x=0; x < (ssize_t) image->columns; x++)
2991 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2993 PixelChannel channel = GetPixelChannelChannel(image,j);
2994 PixelTrait traits = GetPixelChannelTraits(image,channel);
2995 if ((traits & UpdatePixelTrait) == 0)
2997 q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
3000 q+=(ptrdiff_t) GetPixelChannels(image);
3002 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3004 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3009#if defined(MAGICKCORE_OPENMP_SUPPORT)
3013 proceed=SetImageProgress(image,LevelImageTag,progress,image->rows);
3014 if (proceed == MagickFalse)
3018 image_view=DestroyCacheView(image_view);
3019 (void) ClampImage(image,exception);
3065MagickExport MagickBooleanType LevelizeImage(
Image *image,
3066 const double black_point,
const double white_point,
const double gamma,
3069#define LevelizeImageTag "Levelize/Image"
3070#define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
3071 (QuantumScale*((double) x)),gamma))*(white_point-black_point)+black_point)
3091 assert(image != (
Image *) NULL);
3092 assert(image->signature == MagickCoreSignature);
3093 if (IsEventLogging() != MagickFalse)
3094 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3095 if (image->storage_class == PseudoClass)
3096 for (i=0; i < (ssize_t) image->colors; i++)
3101 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3102 image->colormap[i].red=(
double) LevelizeValue(image->colormap[i].red);
3103 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3104 image->colormap[i].green=(double) LevelizeValue(
3105 image->colormap[i].green);
3106 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3107 image->colormap[i].blue=(
double) LevelizeValue(image->colormap[i].blue);
3108 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3109 image->colormap[i].alpha=(double) LevelizeValue(
3110 image->colormap[i].alpha);
3117 image_view=AcquireAuthenticCacheView(image,exception);
3118#if defined(MAGICKCORE_OPENMP_SUPPORT)
3119 #pragma omp parallel for schedule(static) shared(progress,status) \
3120 magick_number_threads(image,image,image->rows,1)
3122 for (y=0; y < (ssize_t) image->rows; y++)
3130 if (status == MagickFalse)
3132 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3133 if (q == (Quantum *) NULL)
3138 for (x=0; x < (ssize_t) image->columns; x++)
3143 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3145 PixelChannel channel = GetPixelChannelChannel(image,j);
3146 PixelTrait traits = GetPixelChannelTraits(image,channel);
3147 if ((traits & UpdatePixelTrait) == 0)
3149 q[j]=LevelizeValue(q[j]);
3151 q+=(ptrdiff_t) GetPixelChannels(image);
3153 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3155 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3160#if defined(MAGICKCORE_OPENMP_SUPPORT)
3164 proceed=SetImageProgress(image,LevelizeImageTag,progress,image->rows);
3165 if (proceed == MagickFalse)
3169 image_view=DestroyCacheView(image_view);
3214MagickExport MagickBooleanType LevelImageColors(
Image *image,
3227 assert(image != (
Image *) NULL);
3228 assert(image->signature == MagickCoreSignature);
3229 if (IsEventLogging() != MagickFalse)
3230 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3231 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
3232 ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
3233 (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
3234 (
void) SetImageColorspace(image,sRGBColorspace,exception);
3236 if (invert == MagickFalse)
3238 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3240 channel_mask=SetImageChannelMask(image,RedChannel);
3241 status&=(MagickStatusType) LevelImage(image,black_color->red,
3242 white_color->red,1.0,exception);
3243 (void) SetImageChannelMask(image,channel_mask);
3245 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3247 channel_mask=SetImageChannelMask(image,GreenChannel);
3248 status&=(MagickStatusType) LevelImage(image,black_color->green,
3249 white_color->green,1.0,exception);
3250 (void) SetImageChannelMask(image,channel_mask);
3252 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3254 channel_mask=SetImageChannelMask(image,BlueChannel);
3255 status&=(MagickStatusType) LevelImage(image,black_color->blue,
3256 white_color->blue,1.0,exception);
3257 (void) SetImageChannelMask(image,channel_mask);
3259 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3260 (image->colorspace == CMYKColorspace))
3262 channel_mask=SetImageChannelMask(image,BlackChannel);
3263 status&=(MagickStatusType) LevelImage(image,black_color->black,
3264 white_color->black,1.0,exception);
3265 (void) SetImageChannelMask(image,channel_mask);
3267 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3268 (image->alpha_trait != UndefinedPixelTrait))
3270 channel_mask=SetImageChannelMask(image,AlphaChannel);
3271 status&=(MagickStatusType) LevelImage(image,black_color->alpha,
3272 white_color->alpha,1.0,exception);
3273 (void) SetImageChannelMask(image,channel_mask);
3278 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3280 channel_mask=SetImageChannelMask(image,RedChannel);
3281 status&=(MagickStatusType) LevelizeImage(image,black_color->red,
3282 white_color->red,1.0,exception);
3283 (void) SetImageChannelMask(image,channel_mask);
3285 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3287 channel_mask=SetImageChannelMask(image,GreenChannel);
3288 status&=(MagickStatusType) LevelizeImage(image,black_color->green,
3289 white_color->green,1.0,exception);
3290 (void) SetImageChannelMask(image,channel_mask);
3292 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3294 channel_mask=SetImageChannelMask(image,BlueChannel);
3295 status&=(MagickStatusType) LevelizeImage(image,black_color->blue,
3296 white_color->blue,1.0,exception);
3297 (void) SetImageChannelMask(image,channel_mask);
3299 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3300 (image->colorspace == CMYKColorspace))
3302 channel_mask=SetImageChannelMask(image,BlackChannel);
3303 status&=(MagickStatusType) LevelizeImage(image,black_color->black,
3304 white_color->black,1.0,exception);
3305 (void) SetImageChannelMask(image,channel_mask);
3307 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3308 (image->alpha_trait != UndefinedPixelTrait))
3310 channel_mask=SetImageChannelMask(image,AlphaChannel);
3311 status&=(MagickStatusType) LevelizeImage(image,black_color->alpha,
3312 white_color->alpha,1.0,exception);
3313 (void) SetImageChannelMask(image,channel_mask);
3316 return(status != 0 ? MagickTrue : MagickFalse);
3350MagickExport MagickBooleanType LinearStretchImage(
Image *image,
3351 const double black_point,
const double white_point,
ExceptionInfo *exception)
3353#define LinearStretchImageTag "LinearStretch/Image"
3359 property[MagickPathExtent];
3376 assert(image != (
Image *) NULL);
3377 assert(image->signature == MagickCoreSignature);
3378 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*histogram));
3379 if (histogram == (
double *) NULL)
3380 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3385 (void) memset(histogram,0,(MaxMap+1)*
sizeof(*histogram));
3386 image_view=AcquireVirtualCacheView(image,exception);
3387 for (y=0; y < (ssize_t) image->rows; y++)
3395 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3396 if (p == (
const Quantum *) NULL)
3398 for (x=0; x < (ssize_t) image->columns; x++)
3400 intensity=GetPixelIntensity(image,p);
3401 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
3402 p+=(ptrdiff_t) GetPixelChannels(image);
3405 image_view=DestroyCacheView(image_view);
3410 for (black=0; black < (ssize_t) MaxMap; black++)
3412 intensity+=histogram[black];
3413 if (intensity >= black_point)
3417 for (white=(ssize_t) MaxMap; white != 0; white--)
3419 intensity+=histogram[white];
3420 if (intensity >= white_point)
3423 histogram=(
double *) RelinquishMagickMemory(histogram);
3424 status=LevelImage(image,(
double) ScaleMapToQuantum((MagickRealType) black),
3425 (
double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
3426 (void) FormatLocaleString(property,MagickPathExtent,
"%gx%g%%",100.0*black/
3427 MaxMap,100.0*white/MaxMap);
3428 (void) SetImageProperty(image,
"histogram:linear-stretch",property,exception);
3464static inline void ModulateHCL(
const double percent_hue,
3465 const double percent_chroma,
const double percent_luma,
double *red,
3466 double *green,
double *blue)
3476 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
3477 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3478 chroma*=0.01*percent_chroma;
3479 luma*=0.01*percent_luma;
3480 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
3483static inline void ModulateHCLp(
const double percent_hue,
3484 const double percent_chroma,
const double percent_luma,
double *red,
3485 double *green,
double *blue)
3495 ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
3496 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3497 chroma*=0.01*percent_chroma;
3498 luma*=0.01*percent_luma;
3499 ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
3502static inline void ModulateHSB(
const double percent_hue,
3503 const double percent_saturation,
const double percent_brightness,
double *red,
3504 double *green,
double *blue)
3514 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
3515 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3516 saturation*=0.01*percent_saturation;
3517 brightness*=0.01*percent_brightness;
3518 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3521static inline void ModulateHSI(
const double percent_hue,
3522 const double percent_saturation,
const double percent_intensity,
double *red,
3523 double *green,
double *blue)
3533 ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3534 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3535 saturation*=0.01*percent_saturation;
3536 intensity*=0.01*percent_intensity;
3537 ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3540static inline void ModulateHSL(
const double percent_hue,
3541 const double percent_saturation,
const double percent_lightness,
double *red,
3542 double *green,
double *blue)
3552 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3553 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3554 saturation*=0.01*percent_saturation;
3555 lightness*=0.01*percent_lightness;
3556 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3559static inline void ModulateHSV(
const double percent_hue,
3560 const double percent_saturation,
const double percent_value,
double *red,
3561 double *green,
double *blue)
3571 ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3572 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3573 saturation*=0.01*percent_saturation;
3574 value*=0.01*percent_value;
3575 ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3578static inline void ModulateHWB(
const double percent_hue,
3579 const double percent_whiteness,
const double percent_blackness,
double *red,
3580 double *green,
double *blue)
3590 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3591 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3592 blackness*=0.01*percent_blackness;
3593 whiteness*=0.01*percent_whiteness;
3594 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3597static inline void ModulateLCHab(
const double percent_luma,
3598 const double percent_chroma,
const double percent_hue,
3599 const IlluminantType illuminant,
double *red,
double *green,
double *blue)
3609 ConvertRGBToLCHab(*red,*green,*blue,illuminant,&luma,&chroma,&hue);
3610 luma*=0.01*percent_luma;
3611 chroma*=0.01*percent_chroma;
3612 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3613 ConvertLCHabToRGB(luma,chroma,hue,illuminant,red,green,blue);
3616static inline void ModulateLCHuv(
const double percent_luma,
3617 const double percent_chroma,
const double percent_hue,
3618 const IlluminantType illuminant,
double *red,
double *green,
double *blue)
3628 ConvertRGBToLCHuv(*red,*green,*blue,illuminant,&luma,&chroma,&hue);
3629 luma*=0.01*percent_luma;
3630 chroma*=0.01*percent_chroma;
3631 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3632 ConvertLCHuvToRGB(luma,chroma,hue,illuminant,red,green,blue);
3635MagickExport MagickBooleanType ModulateImage(
Image *image,
const char *modulate,
3638#define ModulateImageTag "Modulate/Image"
3644 colorspace = UndefinedColorspace;
3650 percent_brightness = 100.0,
3651 percent_hue = 100.0,
3652 percent_saturation = 100.0;
3658 illuminant = D65Illuminant;
3678 assert(image != (
Image *) NULL);
3679 assert(image->signature == MagickCoreSignature);
3680 if (IsEventLogging() != MagickFalse)
3681 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3682 if (modulate == (
char *) NULL)
3683 return(MagickFalse);
3684 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
3685 (
void) SetImageColorspace(image,sRGBColorspace,exception);
3686 flags=ParseGeometry(modulate,&geometry_info);
3687 if ((flags & RhoValue) != 0)
3688 percent_brightness=geometry_info.rho;
3689 if ((flags & SigmaValue) != 0)
3690 percent_saturation=geometry_info.sigma;
3691 if ((flags & XiValue) != 0)
3692 percent_hue=geometry_info.xi;
3693 artifact=GetImageArtifact(image,
"modulate:colorspace");
3694 if (artifact != (
const char *) NULL)
3695 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3696 MagickFalse,artifact);
3697 artifact=GetImageArtifact(image,
"color:illuminant");
3698 if (artifact != (
const char *) NULL)
3703 illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
3705 if (illuminant_type < 0)
3707 illuminant=UndefinedIlluminant;
3708 colorspace=UndefinedColorspace;
3711 illuminant=(IlluminantType) illuminant_type;
3713 if (image->storage_class == PseudoClass)
3714 for (i=0; i < (ssize_t) image->colors; i++)
3724 red=(double) image->colormap[i].red;
3725 green=(double) image->colormap[i].green;
3726 blue=(double) image->colormap[i].blue;
3731 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3735 case HCLpColorspace:
3737 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3743 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3749 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3756 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3762 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3768 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3773 case LCHabColorspace:
3775 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3776 illuminant,&red,&green,&blue);
3779 case LCHuvColorspace:
3781 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3782 illuminant,&red,&green,&blue);
3786 image->colormap[i].red=red;
3787 image->colormap[i].green=green;
3788 image->colormap[i].blue=blue;
3793#if defined(MAGICKCORE_OPENCL_SUPPORT)
3794 if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3795 percent_saturation,colorspace,exception) != MagickFalse)
3800 image_view=AcquireAuthenticCacheView(image,exception);
3801#if defined(MAGICKCORE_OPENMP_SUPPORT)
3802 #pragma omp parallel for schedule(static) shared(progress,status) \
3803 magick_number_threads(image,image,image->rows,1)
3805 for (y=0; y < (ssize_t) image->rows; y++)
3813 if (status == MagickFalse)
3815 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3816 if (q == (Quantum *) NULL)
3821 for (x=0; x < (ssize_t) image->columns; x++)
3828 red=(double) GetPixelRed(image,q);
3829 green=(double) GetPixelGreen(image,q);
3830 blue=(double) GetPixelBlue(image,q);
3835 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3839 case HCLpColorspace:
3841 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3847 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3853 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3860 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3866 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3872 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3877 case LCHabColorspace:
3879 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3880 illuminant,&red,&green,&blue);
3883 case LCHuvColorspace:
3885 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3886 illuminant,&red,&green,&blue);
3890 SetPixelRed(image,ClampToQuantum(red),q);
3891 SetPixelGreen(image,ClampToQuantum(green),q);
3892 SetPixelBlue(image,ClampToQuantum(blue),q);
3893 q+=(ptrdiff_t) GetPixelChannels(image);
3895 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3897 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3902#if defined(MAGICKCORE_OPENMP_SUPPORT)
3906 proceed=SetImageProgress(image,ModulateImageTag,progress,image->rows);
3907 if (proceed == MagickFalse)
3911 image_view=DestroyCacheView(image_view);
3943MagickExport MagickBooleanType NegateImage(
Image *image,
3946#define NegateImageTag "Negate/Image"
3963 assert(image != (
Image *) NULL);
3964 assert(image->signature == MagickCoreSignature);
3965 if (IsEventLogging() != MagickFalse)
3966 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3967 if (image->storage_class == PseudoClass)
3968 for (i=0; i < (ssize_t) image->colors; i++)
3973 if (grayscale != MagickFalse)
3974 if ((image->colormap[i].red != image->colormap[i].green) ||
3975 (image->colormap[i].green != image->colormap[i].blue))
3977 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3978 image->colormap[i].red=(double) QuantumRange-image->colormap[i].red;
3979 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3980 image->colormap[i].green=(double) QuantumRange-image->colormap[i].green;
3981 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3982 image->colormap[i].blue=(double) QuantumRange-image->colormap[i].blue;
3989 image_view=AcquireAuthenticCacheView(image,exception);
3990 if( grayscale != MagickFalse )
3992 for (y=0; y < (ssize_t) image->rows; y++)
4003 if (status == MagickFalse)
4005 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4007 if (q == (Quantum *) NULL)
4012 for (x=0; x < (ssize_t) image->columns; x++)
4017 if (IsPixelGray(image,q) == MagickFalse)
4019 q+=(ptrdiff_t) GetPixelChannels(image);
4022 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4024 PixelChannel channel = GetPixelChannelChannel(image,j);
4025 PixelTrait traits = GetPixelChannelTraits(image,channel);
4026 if ((traits & UpdatePixelTrait) == 0)
4028 q[j]=QuantumRange-q[j];
4030 q+=(ptrdiff_t) GetPixelChannels(image);
4032 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4033 if (sync == MagickFalse)
4035 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4041 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4042 if (proceed == MagickFalse)
4046 image_view=DestroyCacheView(image_view);
4052#if defined(MAGICKCORE_OPENMP_SUPPORT)
4053 #pragma omp parallel for schedule(static) shared(progress,status) \
4054 magick_number_threads(image,image,image->rows,1)
4056 for (y=0; y < (ssize_t) image->rows; y++)
4064 if (status == MagickFalse)
4066 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4067 if (q == (Quantum *) NULL)
4072 for (x=0; x < (ssize_t) image->columns; x++)
4077 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4079 PixelChannel channel = GetPixelChannelChannel(image,j);
4080 PixelTrait traits = GetPixelChannelTraits(image,channel);
4081 if ((traits & UpdatePixelTrait) == 0)
4083 q[j]=QuantumRange-q[j];
4085 q+=(ptrdiff_t) GetPixelChannels(image);
4087 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4089 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4094#if defined(MAGICKCORE_OPENMP_SUPPORT)
4098 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4099 if (proceed == MagickFalse)
4103 image_view=DestroyCacheView(image_view);
4133MagickExport MagickBooleanType NormalizeImage(
Image *image,
4140 black_point=0.02*image->columns*image->rows;
4141 white_point=0.99*image->columns*image->rows;
4142 return(ContrastStretchImage(image,black_point,white_point,exception));
4209#if defined(MAGICKCORE_HAVE_ATANH)
4210#define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
4212#define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
4231#define ScaledSigmoidal(a,b,x) ( \
4232 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
4233 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
4243static inline double InverseScaledSigmoidal(
const double a,
const double b,
4246 const double sig0=Sigmoidal(a,b,0.0);
4247 const double sig1=Sigmoidal(a,b,1.0);
4248 const double argument=(sig1-sig0)*x+sig0;
4249 const double clamped=
4251#if defined(MAGICKCORE_HAVE_ATANH)
4252 argument < -1+MagickEpsilon
4256 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4258 return(b+(2.0/a)*atanh(clamped));
4260 argument < MagickEpsilon
4264 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4266 return(b-log(1.0/clamped-1.0)/a);
4270MagickExport MagickBooleanType SigmoidalContrastImage(
Image *image,
4271 const MagickBooleanType sharpen,
const double contrast,
const double midpoint,
4274#define SigmoidalContrastImageTag "SigmoidalContrast/Image"
4275#define ScaledSig(x) (ClampToQuantum((double) QuantumRange* \
4276 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*((double) x))) )
4277#define InverseScaledSig(x) (ClampToQuantum((double) QuantumRange* \
4278 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale* \
4296 assert(image != (
Image *) NULL);
4297 assert(image->signature == MagickCoreSignature);
4298 if (IsEventLogging() != MagickFalse)
4299 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4304 if (contrast < MagickEpsilon)
4309 if (image->storage_class == PseudoClass)
4314 if( sharpen != MagickFalse )
4315 for (i=0; i < (ssize_t) image->colors; i++)
4317 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4318 image->colormap[i].red=(MagickRealType) ScaledSig(
4319 image->colormap[i].red);
4320 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4321 image->colormap[i].green=(MagickRealType) ScaledSig(
4322 image->colormap[i].green);
4323 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4324 image->colormap[i].blue=(MagickRealType) ScaledSig(
4325 image->colormap[i].blue);
4326 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4327 image->colormap[i].alpha=(MagickRealType) ScaledSig(
4328 image->colormap[i].alpha);
4331 for (i=0; i < (ssize_t) image->colors; i++)
4333 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4334 image->colormap[i].red=(MagickRealType) InverseScaledSig(
4335 image->colormap[i].red);
4336 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4337 image->colormap[i].green=(MagickRealType) InverseScaledSig(
4338 image->colormap[i].green);
4339 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4340 image->colormap[i].blue=(MagickRealType) InverseScaledSig(
4341 image->colormap[i].blue);
4342 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4343 image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
4344 image->colormap[i].alpha);
4352 image_view=AcquireAuthenticCacheView(image,exception);
4353#if defined(MAGICKCORE_OPENMP_SUPPORT)
4354 #pragma omp parallel for schedule(static) shared(progress,status) \
4355 magick_number_threads(image,image,image->rows,1)
4357 for (y=0; y < (ssize_t) image->rows; y++)
4365 if (status == MagickFalse)
4367 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4368 if (q == (Quantum *) NULL)
4373 for (x=0; x < (ssize_t) image->columns; x++)
4378 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4380 PixelChannel channel = GetPixelChannelChannel(image,i);
4381 PixelTrait traits = GetPixelChannelTraits(image,channel);
4382 if ((traits & UpdatePixelTrait) == 0)
4384 if( sharpen != MagickFalse )
4385 q[i]=ScaledSig(q[i]);
4387 q[i]=InverseScaledSig(q[i]);
4389 q+=(ptrdiff_t) GetPixelChannels(image);
4391 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4393 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4398#if defined(MAGICKCORE_OPENMP_SUPPORT)
4402 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress,
4404 if (proceed == MagickFalse)
4408 image_view=DestroyCacheView(image_view);
4438MagickExport MagickBooleanType WhiteBalanceImage(
Image *image,
4441#define WhiteBalanceImageTag "WhiteBalance/Image"
4465 assert(image != (
Image *) NULL);
4466 assert(image->signature == MagickCoreSignature);
4467 if (IsEventLogging() != MagickFalse)
4468 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4469 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4470 return(MagickFalse);
4471 status=TransformImageColorspace(image,LabColorspace,exception);
4474 image_view=AcquireAuthenticCacheView(image,exception);
4475 for (y=0; y < (ssize_t) image->rows; y++)
4483 if (status == MagickFalse)
4485 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4486 if (p == (Quantum *) NULL)
4491 for (x=0; x < (ssize_t) image->columns; x++)
4493 a_mean+=QuantumScale*(double) GetPixela(image,p)-0.5;
4494 b_mean+=QuantumScale*(double) GetPixelb(image,p)-0.5;
4495 p+=(ptrdiff_t) GetPixelChannels(image);
4498 a_mean/=((double) image->columns*image->rows);
4499 b_mean/=((double) image->columns*image->rows);
4501#if defined(MAGICKCORE_OPENMP_SUPPORT)
4502 #pragma omp parallel for schedule(static) shared(progress,status) \
4503 magick_number_threads(image,image,image->rows,1)
4505 for (y=0; y < (ssize_t) image->rows; y++)
4513 if (status == MagickFalse)
4515 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4516 if (q == (Quantum *) NULL)
4521 for (x=0; x < (ssize_t) image->columns; x++)
4530 a=(double) GetPixela(image,q)-1.1*(double) GetPixelL(image,q)*a_mean;
4531 b=(double) GetPixelb(image,q)-1.1*(double) GetPixelL(image,q)*b_mean;
4532 SetPixela(image,ClampToQuantum(a),q);
4533 SetPixelb(image,ClampToQuantum(b),q);
4534 q+=(ptrdiff_t) GetPixelChannels(image);
4536 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4538 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4543#if defined(MAGICKCORE_OPENMP_SUPPORT)
4547 proceed=SetImageProgress(image,WhiteBalanceImageTag,progress,image->rows);
4548 if (proceed == MagickFalse)
4552 image_view=DestroyCacheView(image_view);
4553 artifact=GetImageArtifact(image,
"white-balance:vibrance");
4554 if (artifact != (
const char *) NULL)
4571 flags=ParseGeometry(artifact,&geometry_info);
4572 if ((flags & RhoValue) != 0)
4573 black_point=geometry_info.rho;
4574 if ((flags & PercentValue) != 0)
4575 black_point*=((double) QuantumRange/100.0);
4576 channel_mask=SetImageChannelMask(image,(ChannelType) (aChannel |
4578 status&=(MagickStatusType) LevelImage(image,black_point,(
double)
4579 QuantumRange-black_point,1.0,exception);
4580 (void) SetImageChannelMask(image,channel_mask);
4582 status&=(MagickStatusType) TransformImageColorspace(image,sRGBColorspace,
4584 return(status != 0 ? MagickTrue : MagickFalse);