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,
114 ExceptionInfo *exception)
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,
189 ExceptionInfo *exception)
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,
376 const RectangleInfo *tile_info,
const size_t number_bins,
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,
476 const RectangleInfo *tile_info,
const RangeInfo *range_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));
500 if (tile_cache == (MemoryInfo *) NULL)
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,
622 ExceptionInfo *exception)
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=width;
667 if (tile_info.width == 0)
668 tile_info.width=image->columns >> 3;
669 if (tile_info.width < 2)
671 tile_info.height=height;
672 if (tile_info.height == 0)
673 tile_info.height=image->rows >> 3;
674 if (tile_info.height < 2)
677 if ((image->columns % tile_info.width) != 0)
678 tile_info.x=(ssize_t) (tile_info.width-(image->columns % tile_info.width));
680 if ((image->rows % tile_info.height) != 0)
681 tile_info.y=(ssize_t) (tile_info.height-(image->rows % tile_info.height));
682 clahe_info.width=(size_t) ((ssize_t) image->columns+tile_info.x);
683 clahe_info.height=(size_t) ((ssize_t) image->rows+tile_info.y);
684 clahe_info.x=(ssize_t) (clahe_info.width/tile_info.width);
685 clahe_info.y=(ssize_t) (clahe_info.height/tile_info.height);
686 pixel_cache=AcquireVirtualMemory(clahe_info.width,clahe_info.height*
688 if (pixel_cache == (MemoryInfo *) NULL)
689 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
691 pixels=(
unsigned short *) GetVirtualMemoryBlob(pixel_cache);
692 colorspace=image->colorspace;
693 if (TransformImageColorspace(image,LabColorspace,exception) == MagickFalse)
695 pixel_cache=RelinquishVirtualMemory(pixel_cache);
701 image_view=AcquireVirtualCacheView(image,exception);
705 for (y=0; y < (ssize_t) clahe_info.height; y++)
713 if (status == MagickFalse)
715 p=GetCacheViewVirtualPixels(image_view,-(tile_info.x >> 1),y-
716 (tile_info.y >> 1),clahe_info.width,1,exception);
717 if (p == (
const Quantum *) NULL)
722 for (x=0; x < (ssize_t) clahe_info.width; x++)
724 pixels[n++]=ScaleQuantumToShort(p[0]);
725 p+=(ptrdiff_t) GetPixelChannels(image);
727 if (image->progress_monitor != (MagickProgressMonitor) NULL)
733 proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
734 GetPixelChannels(image));
735 if (proceed == MagickFalse)
739 image_view=DestroyCacheView(image_view);
740 status=CLAHE(&clahe_info,&tile_info,&range_info,number_bins == 0 ?
741 (
size_t) 128 : MagickMin(number_bins,256),clip_limit,pixels);
742 if (status == MagickFalse)
743 (void) ThrowMagickException(exception,GetMagickModule(),
744 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
748 image_view=AcquireAuthenticCacheView(image,exception);
749 n=clahe_info.width*(size_t) (tile_info.y/2);
750 for (y=0; y < (ssize_t) image->rows; y++)
758 if (status == MagickFalse)
760 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
761 if (q == (Quantum *) NULL)
766 n+=(size_t) (tile_info.x/2);
767 for (x=0; x < (ssize_t) image->columns; x++)
769 q[0]=ScaleShortToQuantum(pixels[n++]);
770 q+=(ptrdiff_t) GetPixelChannels(image);
772 n+=(size_t) ((ssize_t) clahe_info.width-(ssize_t) image->columns-
774 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
776 if (image->progress_monitor != (MagickProgressMonitor) NULL)
782 proceed=SetImageProgress(image,CLAHEImageTag,progress,2*
783 GetPixelChannels(image));
784 if (proceed == MagickFalse)
788 image_view=DestroyCacheView(image_view);
789 pixel_cache=RelinquishVirtualMemory(pixel_cache);
790 if (TransformImageColorspace(image,colorspace,exception) == MagickFalse)
840MagickExport MagickBooleanType ClutImage(Image *image,
const Image *clut_image,
841 const PixelInterpolateMethod method,ExceptionInfo *exception)
843#define ClutImageTag "Clut/Image"
863 assert(image != (Image *) NULL);
864 assert(image->signature == MagickCoreSignature);
865 assert(clut_image != (Image *) NULL);
866 assert(clut_image->signature == MagickCoreSignature);
867 if (IsEventLogging() != MagickFalse)
868 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
869 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
871 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
872 (IsGrayColorspace(clut_image->colorspace) == MagickFalse))
873 (void) SetImageColorspace(image,sRGBColorspace,exception);
874 clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*clut_map));
875 if (clut_map == (PixelInfo *) NULL)
876 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
883 adjust=(ssize_t) (method == IntegerInterpolatePixel ? 0 : 1);
884 clut_view=AcquireVirtualCacheView(clut_image,exception);
885 for (i=0; i <= (ssize_t) MaxMap; i++)
887 GetPixelInfo(clut_image,clut_map+i);
888 status=InterpolatePixelInfo(clut_image,clut_view,method,(
double) i*
889 ((
double) clut_image->columns-adjust)/MaxMap,(
double) i*
890 ((
double) clut_image->rows-adjust)/MaxMap,clut_map+i,exception);
891 if (status == MagickFalse)
894 clut_view=DestroyCacheView(clut_view);
895 image_view=AcquireAuthenticCacheView(image,exception);
896#if defined(MAGICKCORE_OPENMP_SUPPORT)
897 #pragma omp parallel for schedule(static) shared(progress,status) \
898 magick_number_threads(image,image,image->rows,1)
900 for (y=0; y < (ssize_t) image->rows; y++)
911 if (status == MagickFalse)
913 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
914 if (q == (Quantum *) NULL)
919 GetPixelInfo(image,&pixel);
920 for (x=0; x < (ssize_t) image->columns; x++)
925 GetPixelInfoPixel(image,q,&pixel);
926 traits=GetPixelChannelTraits(image,RedPixelChannel);
927 if ((traits & UpdatePixelTrait) != 0)
928 pixel.red=clut_map[ScaleQuantumToMap(ClampToQuantum(
930 traits=GetPixelChannelTraits(image,GreenPixelChannel);
931 if ((traits & UpdatePixelTrait) != 0)
932 pixel.green=clut_map[ScaleQuantumToMap(ClampToQuantum(
933 pixel.green))].green;
934 traits=GetPixelChannelTraits(image,BluePixelChannel);
935 if ((traits & UpdatePixelTrait) != 0)
936 pixel.blue=clut_map[ScaleQuantumToMap(ClampToQuantum(
938 traits=GetPixelChannelTraits(image,BlackPixelChannel);
939 if ((traits & UpdatePixelTrait) != 0)
940 pixel.black=clut_map[ScaleQuantumToMap(ClampToQuantum(
941 pixel.black))].black;
942 traits=GetPixelChannelTraits(image,AlphaPixelChannel);
943 if ((traits & UpdatePixelTrait) != 0)
944 pixel.alpha=clut_map[ScaleQuantumToMap(ClampToQuantum(
945 pixel.alpha))].alpha;
946 SetPixelViaPixelInfo(image,&pixel,q);
947 q+=(ptrdiff_t) GetPixelChannels(image);
949 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
951 if (image->progress_monitor != (MagickProgressMonitor) NULL)
956#if defined(MAGICKCORE_OPENMP_SUPPORT)
960 proceed=SetImageProgress(image,ClutImageTag,progress,image->rows);
961 if (proceed == MagickFalse)
965 image_view=DestroyCacheView(image_view);
966 clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
967 if ((clut_image->alpha_trait != UndefinedPixelTrait) &&
968 ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
969 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
1018MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
1019 const char *color_correction_collection,ExceptionInfo *exception)
1021#define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
1023 typedef struct _Correction
1031 typedef struct _ColorCorrection
1046 token[MagickPathExtent];
1079 assert(image != (Image *) NULL);
1080 assert(image->signature == MagickCoreSignature);
1081 if (IsEventLogging() != MagickFalse)
1082 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1083 if (color_correction_collection == (
const char *) NULL)
1084 return(MagickFalse);
1085 ccc=NewXMLTree((
const char *) color_correction_collection,exception);
1086 if (ccc == (XMLTreeInfo *) NULL)
1087 return(MagickFalse);
1088 cc=GetXMLTreeChild(ccc,
"ColorCorrection");
1089 if (cc == (XMLTreeInfo *) NULL)
1091 ccc=DestroyXMLTree(ccc);
1092 return(MagickFalse);
1094 color_correction.red.slope=1.0;
1095 color_correction.red.offset=0.0;
1096 color_correction.red.power=1.0;
1097 color_correction.green.slope=1.0;
1098 color_correction.green.offset=0.0;
1099 color_correction.green.power=1.0;
1100 color_correction.blue.slope=1.0;
1101 color_correction.blue.offset=0.0;
1102 color_correction.blue.power=1.0;
1103 color_correction.saturation=0.0;
1104 sop=GetXMLTreeChild(cc,
"SOPNode");
1105 if (sop != (XMLTreeInfo *) NULL)
1112 slope=GetXMLTreeChild(sop,
"Slope");
1113 if (slope != (XMLTreeInfo *) NULL)
1115 content=GetXMLTreeContent(slope);
1116 p=(
const char *) content;
1117 for (i=0; (*p !=
'\0') && (i < 3); i++)
1119 (void) GetNextToken(p,&p,MagickPathExtent,token);
1121 (void) GetNextToken(p,&p,MagickPathExtent,token);
1126 color_correction.red.slope=StringToDouble(token,(
char **) NULL);
1131 color_correction.green.slope=StringToDouble(token,
1137 color_correction.blue.slope=StringToDouble(token,
1144 offset=GetXMLTreeChild(sop,
"Offset");
1145 if (offset != (XMLTreeInfo *) NULL)
1147 content=GetXMLTreeContent(offset);
1148 p=(
const char *) content;
1149 for (i=0; (*p !=
'\0') && (i < 3); i++)
1151 (void) GetNextToken(p,&p,MagickPathExtent,token);
1153 (void) GetNextToken(p,&p,MagickPathExtent,token);
1158 color_correction.red.offset=StringToDouble(token,
1164 color_correction.green.offset=StringToDouble(token,
1170 color_correction.blue.offset=StringToDouble(token,
1177 power=GetXMLTreeChild(sop,
"Power");
1178 if (power != (XMLTreeInfo *) NULL)
1180 content=GetXMLTreeContent(power);
1181 p=(
const char *) content;
1182 for (i=0; (*p !=
'\0') && (i < 3); i++)
1184 (void) GetNextToken(p,&p,MagickPathExtent,token);
1186 (void) GetNextToken(p,&p,MagickPathExtent,token);
1191 color_correction.red.power=StringToDouble(token,(
char **) NULL);
1196 color_correction.green.power=StringToDouble(token,
1202 color_correction.blue.power=StringToDouble(token,
1210 sat=GetXMLTreeChild(cc,
"SATNode");
1211 if (sat != (XMLTreeInfo *) NULL)
1216 saturation=GetXMLTreeChild(sat,
"Saturation");
1217 if (saturation != (XMLTreeInfo *) NULL)
1219 content=GetXMLTreeContent(saturation);
1220 p=(
const char *) content;
1221 (void) GetNextToken(p,&p,MagickPathExtent,token);
1222 color_correction.saturation=StringToDouble(token,(
char **) NULL);
1225 ccc=DestroyXMLTree(ccc);
1226 if (image->debug != MagickFalse)
1228 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1229 " Color Correction Collection:");
1230 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1231 " color_correction.red.slope: %g",color_correction.red.slope);
1232 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1233 " color_correction.red.offset: %g",color_correction.red.offset);
1234 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1235 " color_correction.red.power: %g",color_correction.red.power);
1236 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1237 " color_correction.green.slope: %g",color_correction.green.slope);
1238 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1239 " color_correction.green.offset: %g",color_correction.green.offset);
1240 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1241 " color_correction.green.power: %g",color_correction.green.power);
1242 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1243 " color_correction.blue.slope: %g",color_correction.blue.slope);
1244 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1245 " color_correction.blue.offset: %g",color_correction.blue.offset);
1246 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1247 " color_correction.blue.power: %g",color_correction.blue.power);
1248 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1249 " color_correction.saturation: %g",color_correction.saturation);
1251 cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*cdl_map));
1252 if (cdl_map == (PixelInfo *) NULL)
1253 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
1255 for (i=0; i <= (ssize_t) MaxMap; i++)
1257 cdl_map[i].red=(double) ScaleMapToQuantum((
double)
1258 (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
1259 color_correction.red.offset,color_correction.red.power))));
1260 cdl_map[i].green=(double) ScaleMapToQuantum((
double)
1261 (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
1262 color_correction.green.offset,color_correction.green.power))));
1263 cdl_map[i].blue=(double) ScaleMapToQuantum((
double)
1264 (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
1265 color_correction.blue.offset,color_correction.blue.power))));
1267 if (image->storage_class == PseudoClass)
1269 for (i=0; i < (ssize_t) image->colors; i++)
1277 luma=0.21267*image->colormap[i].red+0.71526*image->colormap[i].green+
1278 0.07217*image->colormap[i].blue;
1279 image->colormap[i].red=luma+color_correction.saturation*cdl_map[
1280 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
1281 image->colormap[i].green=luma+color_correction.saturation*cdl_map[
1282 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
1283 image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
1284 ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
1286 cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
1287 (void) SyncImage(image, exception);
1295 image_view=AcquireAuthenticCacheView(image,exception);
1296#if defined(MAGICKCORE_OPENMP_SUPPORT)
1297 #pragma omp parallel for schedule(static) shared(progress,status) \
1298 magick_number_threads(image,image,image->rows,1)
1300 for (y=0; y < (ssize_t) image->rows; y++)
1311 if (status == MagickFalse)
1313 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1314 if (q == (Quantum *) NULL)
1319 for (x=0; x < (ssize_t) image->columns; x++)
1321 luma=0.21267*(double) GetPixelRed(image,q)+0.71526*(double)
1322 GetPixelGreen(image,q)+0.07217*(double) GetPixelBlue(image,q);
1323 SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
1324 (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
1325 SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
1326 (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
1327 SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
1328 (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
1329 q+=(ptrdiff_t) GetPixelChannels(image);
1331 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1333 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1338#if defined(MAGICKCORE_OPENMP_SUPPORT)
1342 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
1343 progress,image->rows);
1344 if (proceed == MagickFalse)
1348 image_view=DestroyCacheView(image_view);
1349 cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
1383static inline void Contrast(
const int sign,
double *red,
double *green,
1394 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
1395 brightness+=0.5*sign*(0.5*(sin((
double) (MagickPI*(brightness-0.5)))+1.0)-
1397 if (brightness > 1.0)
1400 if (brightness < 0.0)
1402 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
1405MagickExport MagickBooleanType ContrastImage(Image *image,
1406 const MagickBooleanType sharpen,ExceptionInfo *exception)
1408#define ContrastImageTag "Contrast/Image"
1428 assert(image != (Image *) NULL);
1429 assert(image->signature == MagickCoreSignature);
1430#if defined(MAGICKCORE_OPENCL_SUPPORT)
1431 if (AccelerateContrastImage(image,sharpen,exception) != MagickFalse)
1434 if (IsEventLogging() != MagickFalse)
1435 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1436 sign=sharpen != MagickFalse ? 1 : -1;
1437 if (image->storage_class == PseudoClass)
1442 for (i=0; i < (ssize_t) image->colors; i++)
1449 red=(double) image->colormap[i].red;
1450 green=(double) image->colormap[i].green;
1451 blue=(double) image->colormap[i].blue;
1452 Contrast(sign,&red,&green,&blue);
1453 image->colormap[i].red=(MagickRealType) red;
1454 image->colormap[i].green=(MagickRealType) green;
1455 image->colormap[i].blue=(MagickRealType) blue;
1463 image_view=AcquireAuthenticCacheView(image,exception);
1464#if defined(MAGICKCORE_OPENMP_SUPPORT)
1465 #pragma omp parallel for schedule(static) shared(progress,status) \
1466 magick_number_threads(image,image,image->rows,1)
1468 for (y=0; y < (ssize_t) image->rows; y++)
1481 if (status == MagickFalse)
1483 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1484 if (q == (Quantum *) NULL)
1489 for (x=0; x < (ssize_t) image->columns; x++)
1491 red=(double) GetPixelRed(image,q);
1492 green=(double) GetPixelGreen(image,q);
1493 blue=(double) GetPixelBlue(image,q);
1494 Contrast(sign,&red,&green,&blue);
1495 SetPixelRed(image,ClampToQuantum(red),q);
1496 SetPixelGreen(image,ClampToQuantum(green),q);
1497 SetPixelBlue(image,ClampToQuantum(blue),q);
1498 q+=(ptrdiff_t) GetPixelChannels(image);
1500 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1502 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1507#if defined(MAGICKCORE_OPENMP_SUPPORT)
1511 proceed=SetImageProgress(image,ContrastImageTag,progress,image->rows);
1512 if (proceed == MagickFalse)
1516 image_view=DestroyCacheView(image_view);
1557MagickExport MagickBooleanType ContrastStretchImage(Image *image,
1558 const double black_point,
const double white_point,ExceptionInfo *exception)
1560#define ContrastStretchImageTag "ContrastStretch/Image"
1566 property[MagickPathExtent];
1594 assert(image != (Image *) NULL);
1595 assert(image->signature == MagickCoreSignature);
1596 if (IsEventLogging() != MagickFalse)
1597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1598 type=IdentifyImageType(image,exception);
1599 if (IsGrayImageType(type) != MagickFalse)
1600 (void) SetImageColorspace(image,GRAYColorspace,exception);
1601 black=(Quantum *) AcquireQuantumMemory(MaxPixelChannels,
sizeof(*black));
1602 white=(Quantum *) AcquireQuantumMemory(MaxPixelChannels,
sizeof(*white));
1603 stretch_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1604 sizeof(*stretch_map));
1605 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
1606 sizeof(*histogram));
1607 if ((black == (Quantum *) NULL) || (white == (Quantum *) NULL) ||
1608 (stretch_map == (Quantum *) NULL) || (histogram == (
double *) NULL))
1610 if (histogram != (
double *) NULL)
1611 histogram=(
double *) RelinquishMagickMemory(histogram);
1612 if (stretch_map != (Quantum *) NULL)
1613 stretch_map=(Quantum *) RelinquishMagickMemory(stretch_map);
1614 if (white != (Quantum *) NULL)
1615 white=(Quantum *) RelinquishMagickMemory(white);
1616 if (black != (Quantum *) NULL)
1617 black=(Quantum *) RelinquishMagickMemory(black);
1618 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
1625 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1626 sizeof(*histogram));
1627 image_view=AcquireVirtualCacheView(image,exception);
1628 for (y=0; y < (ssize_t) image->rows; y++)
1636 if (status == MagickFalse)
1638 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1639 if (p == (
const Quantum *) NULL)
1644 for (x=0; x < (ssize_t) image->columns; x++)
1649 pixel=GetPixelIntensity(image,p);
1650 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1652 if (image->channel_mask != AllChannels)
1653 pixel=(double) p[i];
1654 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1655 ClampToQuantum(pixel))+(size_t) i]++;
1657 p+=(ptrdiff_t) GetPixelChannels(image);
1660 image_view=DestroyCacheView(image_view);
1664 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1672 black[i]=(Quantum) 0;
1673 white[i]=(Quantum) ScaleQuantumToMap(QuantumRange);
1675 for (j=0; j <= (ssize_t) MaxMap; j++)
1677 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
1678 if (intensity > black_point)
1681 black[i]=(Quantum) j;
1683 for (j=(ssize_t) MaxMap; j != 0; j--)
1685 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
1686 if (intensity > ((
double) image->columns*image->rows-white_point))
1689 white[i]=(Quantum) j;
1691 histogram=(
double *) RelinquishMagickMemory(histogram);
1695 (void) memset(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1696 sizeof(*stretch_map));
1697 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1702 for (j=0; j <= (ssize_t) MaxMap; j++)
1707 gamma=MagickSafeReciprocal(white[i]-black[i]);
1708 if (j < (ssize_t) black[i])
1709 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=(Quantum) 0;
1711 if (j > (ssize_t) white[i])
1712 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=QuantumRange;
1714 if (black[i] != white[i])
1715 stretch_map[(ssize_t) GetPixelChannels(image)*j+i]=
1716 ScaleMapToQuantum((
double) (MaxMap*gamma*(j-(
double) black[i])));
1719 if (image->storage_class == PseudoClass)
1727 for (j=0; j < (ssize_t) image->colors; j++)
1729 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1731 i=GetPixelChannelOffset(image,RedPixelChannel);
1732 image->colormap[j].red=(MagickRealType) stretch_map[
1733 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1734 image->colormap[j].red))+(size_t) i];
1736 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1738 i=GetPixelChannelOffset(image,GreenPixelChannel);
1739 image->colormap[j].green=(MagickRealType) stretch_map[
1740 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1741 image->colormap[j].green))+(size_t) i];
1743 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1745 i=GetPixelChannelOffset(image,BluePixelChannel);
1746 image->colormap[j].blue=(MagickRealType) stretch_map[
1747 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1748 image->colormap[j].blue))+(size_t) i];
1750 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1752 i=GetPixelChannelOffset(image,AlphaPixelChannel);
1753 image->colormap[j].alpha=(MagickRealType) stretch_map[
1754 GetPixelChannels(image)*ScaleQuantumToMap(ClampToQuantum(
1755 image->colormap[j].alpha))+(size_t) i];
1764 image_view=AcquireAuthenticCacheView(image,exception);
1765#if defined(MAGICKCORE_OPENMP_SUPPORT)
1766 #pragma omp parallel for schedule(static) shared(progress,status) \
1767 magick_number_threads(image,image,image->rows,1)
1769 for (y=0; y < (ssize_t) image->rows; y++)
1777 if (status == MagickFalse)
1779 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1780 if (q == (Quantum *) NULL)
1785 for (x=0; x < (ssize_t) image->columns; x++)
1790 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1792 PixelChannel channel = GetPixelChannelChannel(image,j);
1793 PixelTrait traits = GetPixelChannelTraits(image,channel);
1794 if ((traits & UpdatePixelTrait) == 0)
1796 if (black[j] == white[j])
1798 q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1799 ScaleQuantumToMap(q[j])+(
size_t) j]);
1801 q+=(ptrdiff_t) GetPixelChannels(image);
1803 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1805 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1810#if defined(MAGICKCORE_OPENMP_SUPPORT)
1814 proceed=SetImageProgress(image,ContrastStretchImageTag,progress,
1816 if (proceed == MagickFalse)
1820 image_view=DestroyCacheView(image_view);
1821 (void) FormatLocaleString(property,MagickPathExtent,
"%gx%g%%",100.0*
1822 QuantumScale*GetPixelIntensity(image,black),100.0*QuantumScale*
1823 GetPixelIntensity(image,white));
1824 (void) SetImageProperty(image,
"histogram:contrast-stretch",property,
1826 white=(Quantum *) RelinquishMagickMemory(white);
1827 black=(Quantum *) RelinquishMagickMemory(black);
1828 stretch_map=(Quantum *) RelinquishMagickMemory(stretch_map);
1857MagickExport Image *EnhanceImage(
const Image *image,ExceptionInfo *exception)
1859#define EnhanceImageTag "Enhance/Image"
1860#define EnhancePixel(weight) \
1861 mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1862 distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1863 distance_squared=(4.0+mean)*distance*distance; \
1864 mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1865 distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1866 distance_squared+=(7.0-mean)*distance*distance; \
1867 mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1868 distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1869 distance_squared+=(5.0-mean)*distance*distance; \
1870 mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1871 distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1872 distance_squared+=(5.0-mean)*distance*distance; \
1873 mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1874 distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1875 distance_squared+=(5.0-mean)*distance*distance; \
1876 if (distance_squared < 0.069) \
1878 aggregate.red+=(weight)*(double) GetPixelRed(image,r); \
1879 aggregate.green+=(weight)*(double) GetPixelGreen(image,r); \
1880 aggregate.blue+=(weight)*(double) GetPixelBlue(image,r); \
1881 aggregate.black+=(weight)*(double) GetPixelBlack(image,r); \
1882 aggregate.alpha+=(weight)*(double) GetPixelAlpha(image,r); \
1883 total_weight+=(weight); \
1885 r+=(ptrdiff_t) GetPixelChannels(image);
1906 assert(image != (
const Image *) NULL);
1907 assert(image->signature == MagickCoreSignature);
1908 if (IsEventLogging() != MagickFalse)
1909 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1910 assert(exception != (ExceptionInfo *) NULL);
1911 assert(exception->signature == MagickCoreSignature);
1912 enhance_image=CloneImage(image,0,0,MagickTrue,
1914 if (enhance_image == (Image *) NULL)
1915 return((Image *) NULL);
1916 if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1918 enhance_image=DestroyImage(enhance_image);
1919 return((Image *) NULL);
1926 image_view=AcquireVirtualCacheView(image,exception);
1927 enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1928#if defined(MAGICKCORE_OPENMP_SUPPORT)
1929 #pragma omp parallel for schedule(static) shared(progress,status) \
1930 magick_number_threads(image,enhance_image,image->rows,1)
1932 for (y=0; y < (ssize_t) image->rows; y++)
1949 if (status == MagickFalse)
1951 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1952 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1954 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
1959 center=(ssize_t) GetPixelChannels(image)*(2*((ssize_t) image->columns+4)+2);
1960 GetPixelInfo(image,&pixel);
1961 for (x=0; x < (ssize_t) image->columns; x++)
1975 GetPixelInfo(image,&aggregate);
1977 GetPixelInfoPixel(image,p+center,&pixel);
1979 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1980 EnhancePixel(8.0); EnhancePixel(5.0);
1981 r=p+GetPixelChannels(image)*(image->columns+4);
1982 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1983 EnhancePixel(20.0); EnhancePixel(8.0);
1984 r=p+2*GetPixelChannels(image)*(image->columns+4);
1985 EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1986 EnhancePixel(40.0); EnhancePixel(10.0);
1987 r=p+3*GetPixelChannels(image)*(image->columns+4);
1988 EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1989 EnhancePixel(20.0); EnhancePixel(8.0);
1990 r=p+4*GetPixelChannels(image)*(image->columns+4);
1991 EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1992 EnhancePixel(8.0); EnhancePixel(5.0);
1993 if (total_weight > MagickEpsilon)
1995 pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1996 pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1997 pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1998 pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1999 pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
2001 SetPixelViaPixelInfo(enhance_image,&pixel,q);
2002 p+=(ptrdiff_t) GetPixelChannels(image);
2003 q+=(ptrdiff_t) GetPixelChannels(enhance_image);
2005 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
2007 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2012#if defined(MAGICKCORE_OPENMP_SUPPORT)
2016 proceed=SetImageProgress(image,EnhanceImageTag,progress,image->rows);
2017 if (proceed == MagickFalse)
2021 enhance_view=DestroyCacheView(enhance_view);
2022 image_view=DestroyCacheView(image_view);
2023 if (status == MagickFalse)
2024 enhance_image=DestroyImage(enhance_image);
2025 return(enhance_image);
2052MagickExport MagickBooleanType EqualizeImage(Image *image,
2053 ExceptionInfo *exception)
2055#define EqualizeImageTag "Equalize/Image"
2061 black[2*CompositePixelChannel+1],
2065 white[2*CompositePixelChannel+1];
2082 assert(image != (Image *) NULL);
2083 assert(image->signature == MagickCoreSignature);
2084#if defined(MAGICKCORE_OPENCL_SUPPORT)
2085 if (AccelerateEqualizeImage(image,exception) != MagickFalse)
2088 if (IsEventLogging() != MagickFalse)
2089 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2090 equalize_map=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2091 sizeof(*equalize_map));
2092 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
2093 sizeof(*histogram));
2094 map=(
double *) AcquireQuantumMemory(MaxMap+1UL,MaxPixelChannels*
sizeof(*map));
2095 if ((equalize_map == (
double *) NULL) || (histogram == (
double *) NULL) ||
2096 (map == (
double *) NULL))
2098 if (map != (
double *) NULL)
2099 map=(
double *) RelinquishMagickMemory(map);
2100 if (histogram != (
double *) NULL)
2101 histogram=(
double *) RelinquishMagickMemory(histogram);
2102 if (equalize_map != (
double *) NULL)
2103 equalize_map=(
double *) RelinquishMagickMemory(equalize_map);
2104 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2111 (void) memset(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
2112 sizeof(*histogram));
2113 image_view=AcquireVirtualCacheView(image,exception);
2114 for (y=0; y < (ssize_t) image->rows; y++)
2122 if (status == MagickFalse)
2124 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2125 if (p == (
const Quantum *) NULL)
2130 for (x=0; x < (ssize_t) image->columns; x++)
2132 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2137 intensity=(double) p[i];
2138 if ((image->channel_mask & SyncChannels) != 0)
2139 intensity=GetPixelIntensity(image,p);
2140 histogram[GetPixelChannels(image)*ScaleQuantumToMap(
2141 ClampToQuantum(intensity))+(size_t) i]++;
2143 p+=(ptrdiff_t) GetPixelChannels(image);
2146 image_view=DestroyCacheView(image_view);
2150 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2159 for (j=0; j <= (ssize_t) MaxMap; j++)
2161 intensity+=histogram[(ssize_t) GetPixelChannels(image)*j+i];
2162 map[(ssize_t) GetPixelChannels(image)*j+i]=intensity;
2165 (void) memset(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
2166 sizeof(*equalize_map));
2167 (void) memset(black,0,
sizeof(*black));
2168 (void) memset(white,0,
sizeof(*white));
2169 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2175 white[i]=map[GetPixelChannels(image)*MaxMap+(size_t) i];
2176 if (black[i] != white[i])
2177 for (j=0; j <= (ssize_t) MaxMap; j++)
2178 equalize_map[GetPixelChannels(image)*(size_t) j+(
size_t) i]=(double)
2179 ScaleMapToQuantum((
double) ((MaxMap*(map[GetPixelChannels(image)*
2180 (
size_t) j+(
size_t) i]-black[i]))/(white[i]-black[i])));
2182 histogram=(
double *) RelinquishMagickMemory(histogram);
2183 map=(
double *) RelinquishMagickMemory(map);
2184 if (image->storage_class == PseudoClass)
2192 for (j=0; j < (ssize_t) image->colors; j++)
2194 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2196 PixelChannel channel = GetPixelChannelChannel(image,
2198 if (black[channel] != white[channel])
2199 image->colormap[j].red=equalize_map[(ssize_t)
2200 GetPixelChannels(image)*ScaleQuantumToMap(
2201 ClampToQuantum(image->colormap[j].red))+channel];
2203 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2205 PixelChannel channel = GetPixelChannelChannel(image,
2207 if (black[channel] != white[channel])
2208 image->colormap[j].green=equalize_map[(ssize_t)
2209 GetPixelChannels(image)*ScaleQuantumToMap(
2210 ClampToQuantum(image->colormap[j].green))+channel];
2212 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2214 PixelChannel channel = GetPixelChannelChannel(image,
2216 if (black[channel] != white[channel])
2217 image->colormap[j].blue=equalize_map[(ssize_t)
2218 GetPixelChannels(image)*ScaleQuantumToMap(
2219 ClampToQuantum(image->colormap[j].blue))+channel];
2221 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2223 PixelChannel channel = GetPixelChannelChannel(image,
2225 if (black[channel] != white[channel])
2226 image->colormap[j].alpha=equalize_map[(ssize_t)
2227 GetPixelChannels(image)*ScaleQuantumToMap(
2228 ClampToQuantum(image->colormap[j].alpha))+channel];
2236 image_view=AcquireAuthenticCacheView(image,exception);
2237#if defined(MAGICKCORE_OPENMP_SUPPORT)
2238 #pragma omp parallel for schedule(static) shared(progress,status) \
2239 magick_number_threads(image,image,image->rows,1)
2241 for (y=0; y < (ssize_t) image->rows; y++)
2249 if (status == MagickFalse)
2251 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2252 if (q == (Quantum *) NULL)
2257 for (x=0; x < (ssize_t) image->columns; x++)
2262 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2264 PixelChannel channel = GetPixelChannelChannel(image,j);
2265 PixelTrait traits = GetPixelChannelTraits(image,channel);
2266 if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
2268 q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
2269 ScaleQuantumToMap(q[j])+(
size_t) j]);
2271 q+=(ptrdiff_t) GetPixelChannels(image);
2273 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2275 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2280#if defined(MAGICKCORE_OPENMP_SUPPORT)
2284 proceed=SetImageProgress(image,EqualizeImageTag,progress,image->rows);
2285 if (proceed == MagickFalse)
2289 image_view=DestroyCacheView(image_view);
2290 equalize_map=(
double *) RelinquishMagickMemory(equalize_map);
2329static inline double gamma_pow(
const double value,
const double gamma)
2331 return(value < 0.0 ? value : pow(value,gamma));
2334MagickExport MagickBooleanType GammaImage(Image *image,
const double gamma,
2335 ExceptionInfo *exception)
2337#define GammaImageTag "Gamma/Image"
2360 assert(image != (Image *) NULL);
2361 assert(image->signature == MagickCoreSignature);
2362 if (IsEventLogging() != MagickFalse)
2363 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2366 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*gamma_map));
2367 if (gamma_map == (Quantum *) NULL)
2368 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2370 (void) memset(gamma_map,0,(MaxMap+1)*
sizeof(*gamma_map));
2372 for (i=0; i <= (ssize_t) MaxMap; i++)
2373 gamma_map[i]=ScaleMapToQuantum((
double) (MaxMap*pow((
double) i/
2374 MaxMap,MagickSafeReciprocal(gamma))));
2375 if (image->storage_class == PseudoClass)
2376 for (i=0; i < (ssize_t) image->colors; i++)
2381 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2382 image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
2383 ClampToQuantum(image->colormap[i].red))];
2384 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2385 image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
2386 ClampToQuantum(image->colormap[i].green))];
2387 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2388 image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
2389 ClampToQuantum(image->colormap[i].blue))];
2390 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2391 image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
2392 ClampToQuantum(image->colormap[i].alpha))];
2399 image_view=AcquireAuthenticCacheView(image,exception);
2400#if defined(MAGICKCORE_OPENMP_SUPPORT)
2401 #pragma omp parallel for schedule(static) shared(progress,status) \
2402 magick_number_threads(image,image,image->rows,1)
2404 for (y=0; y < (ssize_t) image->rows; y++)
2412 if (status == MagickFalse)
2414 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2415 if (q == (Quantum *) NULL)
2420 for (x=0; x < (ssize_t) image->columns; x++)
2425 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2427 PixelChannel channel = GetPixelChannelChannel(image,j);
2428 PixelTrait traits = GetPixelChannelTraits(image,channel);
2429 if ((traits & UpdatePixelTrait) == 0)
2431 q[j]=gamma_map[ScaleQuantumToMap(ClampToQuantum((MagickRealType)
2434 q+=(ptrdiff_t) GetPixelChannels(image);
2436 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2438 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2443#if defined(MAGICKCORE_OPENMP_SUPPORT)
2447 proceed=SetImageProgress(image,GammaImageTag,progress,image->rows);
2448 if (proceed == MagickFalse)
2452 image_view=DestroyCacheView(image_view);
2453 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
2454 if (image->gamma != 0.0)
2455 image->gamma*=gamma;
2486MagickExport MagickBooleanType GrayscaleImage(Image *image,
2487 const PixelIntensityMethod method,ExceptionInfo *exception)
2489#define GrayscaleImageTag "Grayscale/Image"
2503 assert(image != (Image *) NULL);
2504 assert(image->signature == MagickCoreSignature);
2505 if (IsEventLogging() != MagickFalse)
2506 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2507 if (image->storage_class == PseudoClass)
2509 if (SyncImage(image,exception) == MagickFalse)
2510 return(MagickFalse);
2511 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2512 return(MagickFalse);
2514#if defined(MAGICKCORE_OPENCL_SUPPORT)
2515 if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
2517 image->intensity=method;
2518 image->type=GrayscaleType;
2519 if ((method == Rec601LuminancePixelIntensityMethod) ||
2520 (method == Rec709LuminancePixelIntensityMethod))
2521 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2522 return(SetImageColorspace(image,GRAYColorspace,exception));
2530 image_view=AcquireAuthenticCacheView(image,exception);
2531#if defined(MAGICKCORE_OPENMP_SUPPORT)
2532 #pragma omp parallel for schedule(static) shared(progress,status) \
2533 magick_number_threads(image,image,image->rows,1)
2535 for (y=0; y < (ssize_t) image->rows; y++)
2543 if (status == MagickFalse)
2545 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2546 if (q == (Quantum *) NULL)
2551 for (x=0; x < (ssize_t) image->columns; x++)
2559 red=(MagickRealType) GetPixelRed(image,q);
2560 green=(MagickRealType) GetPixelGreen(image,q);
2561 blue=(MagickRealType) GetPixelBlue(image,q);
2565 case AveragePixelIntensityMethod:
2567 intensity=(red+green+blue)/3.0;
2570 case BrightnessPixelIntensityMethod:
2572 intensity=MagickMax(MagickMax(red,green),blue);
2575 case LightnessPixelIntensityMethod:
2577 intensity=(MagickMin(MagickMin(red,green),blue)+
2578 MagickMax(MagickMax(red,green),blue))/2.0;
2581 case MSPixelIntensityMethod:
2583 intensity=(MagickRealType) (((
double) red*red+green*green+
2587 case Rec601LumaPixelIntensityMethod:
2589 if (image->colorspace == RGBColorspace)
2591 red=EncodePixelGamma(red);
2592 green=EncodePixelGamma(green);
2593 blue=EncodePixelGamma(blue);
2595 intensity=0.298839*red+0.586811*green+0.114350*blue;
2598 case Rec601LuminancePixelIntensityMethod:
2600 if (image->colorspace == sRGBColorspace)
2602 red=DecodePixelGamma(red);
2603 green=DecodePixelGamma(green);
2604 blue=DecodePixelGamma(blue);
2606 intensity=0.298839*red+0.586811*green+0.114350*blue;
2609 case Rec709LumaPixelIntensityMethod:
2612 if (image->colorspace == RGBColorspace)
2614 red=EncodePixelGamma(red);
2615 green=EncodePixelGamma(green);
2616 blue=EncodePixelGamma(blue);
2618 intensity=0.212656*red+0.715158*green+0.072186*blue;
2621 case Rec709LuminancePixelIntensityMethod:
2623 if (image->colorspace == sRGBColorspace)
2625 red=DecodePixelGamma(red);
2626 green=DecodePixelGamma(green);
2627 blue=DecodePixelGamma(blue);
2629 intensity=0.212656*red+0.715158*green+0.072186*blue;
2632 case RMSPixelIntensityMethod:
2634 intensity=(MagickRealType) (sqrt((
double) red*red+green*green+
2635 blue*blue)/sqrt(3.0));
2639 SetPixelGray(image,ClampToQuantum(intensity),q);
2640 q+=(ptrdiff_t) GetPixelChannels(image);
2642 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2644 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2649#if defined(MAGICKCORE_OPENMP_SUPPORT)
2653 proceed=SetImageProgress(image,GrayscaleImageTag,progress,image->rows);
2654 if (proceed == MagickFalse)
2658 image_view=DestroyCacheView(image_view);
2659 image->intensity=method;
2660 image->type=GrayscaleType;
2661 if ((method == Rec601LuminancePixelIntensityMethod) ||
2662 (method == Rec709LuminancePixelIntensityMethod))
2663 return(SetImageColorspace(image,LinearGRAYColorspace,exception));
2664 return(SetImageColorspace(image,GRAYColorspace,exception));
2698MagickExport MagickBooleanType HaldClutImage(Image *image,
2699 const Image *hald_image,ExceptionInfo *exception)
2701#define HaldClutImageTag "Clut/Image"
2703 typedef struct _HaldInfo
2735 assert(image != (Image *) NULL);
2736 assert(image->signature == MagickCoreSignature);
2737 if (IsEventLogging() != MagickFalse)
2738 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2739 assert(hald_image != (Image *) NULL);
2740 assert(hald_image->signature == MagickCoreSignature);
2741 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2742 return(MagickFalse);
2743 if ((image->alpha_trait & BlendPixelTrait) == 0)
2744 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2745 if (image->colorspace != hald_image->colorspace)
2746 (void) SetImageColorspace(image,hald_image->colorspace,exception);
2752 length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2753 (MagickRealType) hald_image->rows);
2754 for (level=2; (level*level*level) < length; level++) ;
2756 cube_size=level*level;
2757 width=(double) hald_image->columns;
2758 GetPixelInfo(hald_image,&zero);
2759 hald_view=AcquireVirtualCacheView(hald_image,exception);
2760 image_view=AcquireAuthenticCacheView(image,exception);
2761#if defined(MAGICKCORE_OPENMP_SUPPORT)
2762 #pragma omp parallel for schedule(static) shared(progress,status) \
2763 magick_number_threads(image,image,image->rows,1)
2765 for (y=0; y < (ssize_t) image->rows; y++)
2773 if (status == MagickFalse)
2775 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2776 if (q == (Quantum *) NULL)
2781 for (x=0; x < (ssize_t) image->columns; x++)
2788 point = { 0, 0, 0 };
2797 point.x=QuantumScale*(level-1.0)*(
double) GetPixelRed(image,q);
2798 point.y=QuantumScale*(level-1.0)*(
double) GetPixelGreen(image,q);
2799 point.z=QuantumScale*(level-1.0)*(
double) GetPixelBlue(image,q);
2800 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2801 point.x-=floor(point.x);
2802 point.y-=floor(point.y);
2803 point.z-=floor(point.z);
2804 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2805 fmod(offset,width),floor(offset/width),&pixel1,exception);
2806 if (status == MagickFalse)
2808 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2809 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2810 if (status == MagickFalse)
2813 if (hald_image->interpolate == NearestInterpolatePixel)
2814 area=(point.y < 0.5) ? 0.0 : 1.0;
2815 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2818 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2819 fmod(offset,width),floor(offset/width),&pixel1,exception);
2820 if (status == MagickFalse)
2822 status=InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2823 fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2824 if (status == MagickFalse)
2826 CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2829 if (hald_image->interpolate == NearestInterpolatePixel)
2830 area=(point.z < 0.5)? 0.0 : 1.0;
2831 CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2833 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2834 SetPixelRed(image,ClampToQuantum(pixel.red),q);
2835 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2836 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2837 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2838 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2839 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2840 (image->colorspace == CMYKColorspace))
2841 SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2842 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2843 (image->alpha_trait != UndefinedPixelTrait))
2844 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2845 q+=(ptrdiff_t) GetPixelChannels(image);
2847 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2849 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2854#if defined(MAGICKCORE_OPENMP_SUPPORT)
2858 proceed=SetImageProgress(image,HaldClutImageTag,progress,image->rows);
2859 if (proceed == MagickFalse)
2863 hald_view=DestroyCacheView(hald_view);
2864 image_view=DestroyCacheView(image_view);
2912static inline double LevelPixel(
const double black_point,
2913 const double white_point,
const double gamma,
const double pixel)
2919 scale=MagickSafeReciprocal(white_point-black_point);
2920 level_pixel=(double) QuantumRange*gamma_pow(scale*((
double) pixel-(
double)
2921 black_point),MagickSafeReciprocal(gamma));
2922 return(level_pixel);
2925MagickExport MagickBooleanType LevelImage(Image *image,
const double black_point,
2926 const double white_point,
const double gamma,ExceptionInfo *exception)
2928#define LevelImageTag "Level/Image"
2946 assert(image != (Image *) NULL);
2947 assert(image->signature == MagickCoreSignature);
2948 if (IsEventLogging() != MagickFalse)
2949 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2950 if (image->storage_class == PseudoClass)
2951 for (i=0; i < (ssize_t) image->colors; i++)
2956 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2957 image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2958 white_point,gamma,image->colormap[i].red));
2959 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2960 image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2961 white_point,gamma,image->colormap[i].green));
2962 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2963 image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2964 white_point,gamma,image->colormap[i].blue));
2965 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2966 image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2967 white_point,gamma,image->colormap[i].alpha));
2974 image_view=AcquireAuthenticCacheView(image,exception);
2975#if defined(MAGICKCORE_OPENMP_SUPPORT)
2976 #pragma omp parallel for schedule(static) shared(progress,status) \
2977 magick_number_threads(image,image,image->rows,1)
2979 for (y=0; y < (ssize_t) image->rows; y++)
2987 if (status == MagickFalse)
2989 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2990 if (q == (Quantum *) NULL)
2995 for (x=0; x < (ssize_t) image->columns; x++)
3000 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3002 PixelChannel channel = GetPixelChannelChannel(image,j);
3003 PixelTrait traits = GetPixelChannelTraits(image,channel);
3004 if ((traits & UpdatePixelTrait) == 0)
3006 q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
3009 q+=(ptrdiff_t) GetPixelChannels(image);
3011 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3013 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3018#if defined(MAGICKCORE_OPENMP_SUPPORT)
3022 proceed=SetImageProgress(image,LevelImageTag,progress,image->rows);
3023 if (proceed == MagickFalse)
3027 image_view=DestroyCacheView(image_view);
3028 (void) ClampImage(image,exception);
3074MagickExport MagickBooleanType LevelizeImage(Image *image,
3075 const double black_point,
const double white_point,
const double gamma,
3076 ExceptionInfo *exception)
3078#define LevelizeImageTag "Levelize/Image"
3079#define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
3080 (QuantumScale*((double) x)),gamma))*(white_point-black_point)+black_point)
3100 assert(image != (Image *) NULL);
3101 assert(image->signature == MagickCoreSignature);
3102 if (IsEventLogging() != MagickFalse)
3103 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3104 if (image->storage_class == PseudoClass)
3105 for (i=0; i < (ssize_t) image->colors; i++)
3110 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3111 image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
3112 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3113 image->colormap[i].green=(double) LevelizeValue(
3114 image->colormap[i].green);
3115 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3116 image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
3117 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3118 image->colormap[i].alpha=(double) LevelizeValue(
3119 image->colormap[i].alpha);
3126 image_view=AcquireAuthenticCacheView(image,exception);
3127#if defined(MAGICKCORE_OPENMP_SUPPORT)
3128 #pragma omp parallel for schedule(static) shared(progress,status) \
3129 magick_number_threads(image,image,image->rows,1)
3131 for (y=0; y < (ssize_t) image->rows; y++)
3139 if (status == MagickFalse)
3141 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3142 if (q == (Quantum *) NULL)
3147 for (x=0; x < (ssize_t) image->columns; x++)
3152 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3154 PixelChannel channel = GetPixelChannelChannel(image,j);
3155 PixelTrait traits = GetPixelChannelTraits(image,channel);
3156 if ((traits & UpdatePixelTrait) == 0)
3158 q[j]=LevelizeValue(q[j]);
3160 q+=(ptrdiff_t) GetPixelChannels(image);
3162 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3164 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3169#if defined(MAGICKCORE_OPENMP_SUPPORT)
3173 proceed=SetImageProgress(image,LevelizeImageTag,progress,image->rows);
3174 if (proceed == MagickFalse)
3178 image_view=DestroyCacheView(image_view);
3223MagickExport MagickBooleanType LevelImageColors(Image *image,
3224 const PixelInfo *black_color,
const PixelInfo *white_color,
3225 const MagickBooleanType invert,ExceptionInfo *exception)
3236 assert(image != (Image *) NULL);
3237 assert(image->signature == MagickCoreSignature);
3238 if (IsEventLogging() != MagickFalse)
3239 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3240 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
3241 ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
3242 (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
3243 (void) SetImageColorspace(image,sRGBColorspace,exception);
3245 if (invert == MagickFalse)
3247 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3249 channel_mask=SetImageChannelMask(image,RedChannel);
3250 status&=(MagickStatusType) LevelImage(image,black_color->red,
3251 white_color->red,1.0,exception);
3252 (void) SetImageChannelMask(image,channel_mask);
3254 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3256 channel_mask=SetImageChannelMask(image,GreenChannel);
3257 status&=(MagickStatusType) LevelImage(image,black_color->green,
3258 white_color->green,1.0,exception);
3259 (void) SetImageChannelMask(image,channel_mask);
3261 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3263 channel_mask=SetImageChannelMask(image,BlueChannel);
3264 status&=(MagickStatusType) LevelImage(image,black_color->blue,
3265 white_color->blue,1.0,exception);
3266 (void) SetImageChannelMask(image,channel_mask);
3268 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3269 (image->colorspace == CMYKColorspace))
3271 channel_mask=SetImageChannelMask(image,BlackChannel);
3272 status&=(MagickStatusType) LevelImage(image,black_color->black,
3273 white_color->black,1.0,exception);
3274 (void) SetImageChannelMask(image,channel_mask);
3276 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3277 (image->alpha_trait != UndefinedPixelTrait))
3279 channel_mask=SetImageChannelMask(image,AlphaChannel);
3280 status&=(MagickStatusType) LevelImage(image,black_color->alpha,
3281 white_color->alpha,1.0,exception);
3282 (void) SetImageChannelMask(image,channel_mask);
3287 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3289 channel_mask=SetImageChannelMask(image,RedChannel);
3290 status&=(MagickStatusType) LevelizeImage(image,black_color->red,
3291 white_color->red,1.0,exception);
3292 (void) SetImageChannelMask(image,channel_mask);
3294 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3296 channel_mask=SetImageChannelMask(image,GreenChannel);
3297 status&=(MagickStatusType) LevelizeImage(image,black_color->green,
3298 white_color->green,1.0,exception);
3299 (void) SetImageChannelMask(image,channel_mask);
3301 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3303 channel_mask=SetImageChannelMask(image,BlueChannel);
3304 status&=(MagickStatusType) LevelizeImage(image,black_color->blue,
3305 white_color->blue,1.0,exception);
3306 (void) SetImageChannelMask(image,channel_mask);
3308 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3309 (image->colorspace == CMYKColorspace))
3311 channel_mask=SetImageChannelMask(image,BlackChannel);
3312 status&=(MagickStatusType) LevelizeImage(image,black_color->black,
3313 white_color->black,1.0,exception);
3314 (void) SetImageChannelMask(image,channel_mask);
3316 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3317 (image->alpha_trait != UndefinedPixelTrait))
3319 channel_mask=SetImageChannelMask(image,AlphaChannel);
3320 status&=(MagickStatusType) LevelizeImage(image,black_color->alpha,
3321 white_color->alpha,1.0,exception);
3322 (void) SetImageChannelMask(image,channel_mask);
3325 return(status != 0 ? MagickTrue : MagickFalse);
3359MagickExport MagickBooleanType LinearStretchImage(Image *image,
3360 const double black_point,
const double white_point,ExceptionInfo *exception)
3362#define LinearStretchImageTag "LinearStretch/Image"
3368 property[MagickPathExtent];
3385 assert(image != (Image *) NULL);
3386 assert(image->signature == MagickCoreSignature);
3387 histogram=(
double *) AcquireQuantumMemory(MaxMap+1UL,
sizeof(*histogram));
3388 if (histogram == (
double *) NULL)
3389 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3394 (void) memset(histogram,0,(MaxMap+1)*
sizeof(*histogram));
3395 image_view=AcquireVirtualCacheView(image,exception);
3396 for (y=0; y < (ssize_t) image->rows; y++)
3404 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3405 if (p == (
const Quantum *) NULL)
3407 for (x=0; x < (ssize_t) image->columns; x++)
3409 intensity=GetPixelIntensity(image,p);
3410 histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
3411 p+=(ptrdiff_t) GetPixelChannels(image);
3414 image_view=DestroyCacheView(image_view);
3419 for (black=0; black < (ssize_t) MaxMap; black++)
3421 intensity+=histogram[black];
3422 if (intensity >= black_point)
3426 for (white=(ssize_t) MaxMap; white != 0; white--)
3428 intensity+=histogram[white];
3429 if (intensity >= white_point)
3432 histogram=(
double *) RelinquishMagickMemory(histogram);
3433 status=LevelImage(image,(
double) ScaleMapToQuantum((MagickRealType) black),
3434 (
double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
3435 (void) FormatLocaleString(property,MagickPathExtent,
"%gx%g%%",100.0*black/
3436 MaxMap,100.0*white/MaxMap);
3437 (void) SetImageProperty(image,
"histogram:linear-stretch",property,exception);
3473static inline void ModulateHCL(
const double percent_hue,
3474 const double percent_chroma,
const double percent_luma,
double *red,
3475 double *green,
double *blue)
3485 ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
3486 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3487 chroma*=0.01*percent_chroma;
3488 luma*=0.01*percent_luma;
3489 ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
3492static inline void ModulateHCLp(
const double percent_hue,
3493 const double percent_chroma,
const double percent_luma,
double *red,
3494 double *green,
double *blue)
3504 ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
3505 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3506 chroma*=0.01*percent_chroma;
3507 luma*=0.01*percent_luma;
3508 ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
3511static inline void ModulateHSB(
const double percent_hue,
3512 const double percent_saturation,
const double percent_brightness,
double *red,
3513 double *green,
double *blue)
3523 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
3524 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3525 saturation*=0.01*percent_saturation;
3526 brightness*=0.01*percent_brightness;
3527 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3530static inline void ModulateHSI(
const double percent_hue,
3531 const double percent_saturation,
const double percent_intensity,
double *red,
3532 double *green,
double *blue)
3542 ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3543 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3544 saturation*=0.01*percent_saturation;
3545 intensity*=0.01*percent_intensity;
3546 ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3549static inline void ModulateHSL(
const double percent_hue,
3550 const double percent_saturation,
const double percent_lightness,
double *red,
3551 double *green,
double *blue)
3561 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3562 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3563 saturation*=0.01*percent_saturation;
3564 lightness*=0.01*percent_lightness;
3565 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3568static inline void ModulateHSV(
const double percent_hue,
3569 const double percent_saturation,
const double percent_value,
double *red,
3570 double *green,
double *blue)
3580 ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3581 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3582 saturation*=0.01*percent_saturation;
3583 value*=0.01*percent_value;
3584 ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3587static inline void ModulateHWB(
const double percent_hue,
3588 const double percent_whiteness,
const double percent_blackness,
double *red,
3589 double *green,
double *blue)
3599 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3600 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3601 blackness*=0.01*percent_blackness;
3602 whiteness*=0.01*percent_whiteness;
3603 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3606static inline void ModulateLCHab(
const double percent_luma,
3607 const double percent_chroma,
const double percent_hue,
3608 const IlluminantType illuminant,
double *red,
double *green,
double *blue)
3618 ConvertRGBToLCHab(*red,*green,*blue,illuminant,&luma,&chroma,&hue);
3619 luma*=0.01*percent_luma;
3620 chroma*=0.01*percent_chroma;
3621 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3622 ConvertLCHabToRGB(luma,chroma,hue,illuminant,red,green,blue);
3625static inline void ModulateLCHuv(
const double percent_luma,
3626 const double percent_chroma,
const double percent_hue,
3627 const IlluminantType illuminant,
double *red,
double *green,
double *blue)
3637 ConvertRGBToLCHuv(*red,*green,*blue,illuminant,&luma,&chroma,&hue);
3638 luma*=0.01*percent_luma;
3639 chroma*=0.01*percent_chroma;
3640 hue+=fmod((percent_hue-100.0),200.0)/200.0;
3641 ConvertLCHuvToRGB(luma,chroma,hue,illuminant,red,green,blue);
3644MagickExport MagickBooleanType ModulateImage(Image *image,
const char *modulate,
3645 ExceptionInfo *exception)
3647#define ModulateImageTag "Modulate/Image"
3653 colorspace = UndefinedColorspace;
3659 percent_brightness = 100.0,
3660 percent_hue = 100.0,
3661 percent_saturation = 100.0;
3667 illuminant = D65Illuminant;
3687 assert(image != (Image *) NULL);
3688 assert(image->signature == MagickCoreSignature);
3689 if (IsEventLogging() != MagickFalse)
3690 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3691 if (modulate == (
char *) NULL)
3692 return(MagickFalse);
3693 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
3694 (void) SetImageColorspace(image,sRGBColorspace,exception);
3695 flags=ParseGeometry(modulate,&geometry_info);
3696 if ((flags & RhoValue) != 0)
3697 percent_brightness=geometry_info.rho;
3698 if ((flags & SigmaValue) != 0)
3699 percent_saturation=geometry_info.sigma;
3700 if ((flags & XiValue) != 0)
3701 percent_hue=geometry_info.xi;
3702 artifact=GetImageArtifact(image,
"modulate:colorspace");
3703 if (artifact != (
const char *) NULL)
3704 colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3705 MagickFalse,artifact);
3706 artifact=GetImageArtifact(image,
"color:illuminant");
3707 if (artifact != (
const char *) NULL)
3712 illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
3714 if (illuminant_type < 0)
3716 illuminant=UndefinedIlluminant;
3717 colorspace=UndefinedColorspace;
3720 illuminant=(IlluminantType) illuminant_type;
3722 if (image->storage_class == PseudoClass)
3723 for (i=0; i < (ssize_t) image->colors; i++)
3733 red=(double) image->colormap[i].red;
3734 green=(double) image->colormap[i].green;
3735 blue=(double) image->colormap[i].blue;
3740 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3744 case HCLpColorspace:
3746 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3752 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3758 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3765 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3771 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3777 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3782 case LCHabColorspace:
3784 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3785 illuminant,&red,&green,&blue);
3788 case LCHuvColorspace:
3790 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3791 illuminant,&red,&green,&blue);
3795 image->colormap[i].red=red;
3796 image->colormap[i].green=green;
3797 image->colormap[i].blue=blue;
3802#if defined(MAGICKCORE_OPENCL_SUPPORT)
3803 if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3804 percent_saturation,colorspace,exception) != MagickFalse)
3809 image_view=AcquireAuthenticCacheView(image,exception);
3810#if defined(MAGICKCORE_OPENMP_SUPPORT)
3811 #pragma omp parallel for schedule(static) shared(progress,status) \
3812 magick_number_threads(image,image,image->rows,1)
3814 for (y=0; y < (ssize_t) image->rows; y++)
3822 if (status == MagickFalse)
3824 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3825 if (q == (Quantum *) NULL)
3830 for (x=0; x < (ssize_t) image->columns; x++)
3837 red=(double) GetPixelRed(image,q);
3838 green=(double) GetPixelGreen(image,q);
3839 blue=(double) GetPixelBlue(image,q);
3844 ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3848 case HCLpColorspace:
3850 ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3856 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3862 ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3869 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3875 ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3881 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3886 case LCHabColorspace:
3888 ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3889 illuminant,&red,&green,&blue);
3892 case LCHuvColorspace:
3894 ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3895 illuminant,&red,&green,&blue);
3899 SetPixelRed(image,ClampToQuantum(red),q);
3900 SetPixelGreen(image,ClampToQuantum(green),q);
3901 SetPixelBlue(image,ClampToQuantum(blue),q);
3902 q+=(ptrdiff_t) GetPixelChannels(image);
3904 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3906 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3911#if defined(MAGICKCORE_OPENMP_SUPPORT)
3915 proceed=SetImageProgress(image,ModulateImageTag,progress,image->rows);
3916 if (proceed == MagickFalse)
3920 image_view=DestroyCacheView(image_view);
3952MagickExport MagickBooleanType NegateImage(Image *image,
3953 const MagickBooleanType grayscale,ExceptionInfo *exception)
3955#define NegateImageTag "Negate/Image"
3972 assert(image != (Image *) NULL);
3973 assert(image->signature == MagickCoreSignature);
3974 if (IsEventLogging() != MagickFalse)
3975 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3976 if (image->storage_class == PseudoClass)
3977 for (i=0; i < (ssize_t) image->colors; i++)
3982 if (grayscale != MagickFalse)
3983 if ((image->colormap[i].red != image->colormap[i].green) ||
3984 (image->colormap[i].green != image->colormap[i].blue))
3986 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3987 image->colormap[i].red=(double) QuantumRange-image->colormap[i].red;
3988 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3989 image->colormap[i].green=(double) QuantumRange-image->colormap[i].green;
3990 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3991 image->colormap[i].blue=(double) QuantumRange-image->colormap[i].blue;
3998 image_view=AcquireAuthenticCacheView(image,exception);
3999 if( grayscale != MagickFalse )
4001 for (y=0; y < (ssize_t) image->rows; y++)
4012 if (status == MagickFalse)
4014 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4016 if (q == (Quantum *) NULL)
4021 for (x=0; x < (ssize_t) image->columns; x++)
4026 if (IsPixelGray(image,q) == MagickFalse)
4028 q+=(ptrdiff_t) GetPixelChannels(image);
4031 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4033 PixelChannel channel = GetPixelChannelChannel(image,j);
4034 PixelTrait traits = GetPixelChannelTraits(image,channel);
4035 if ((traits & UpdatePixelTrait) == 0)
4037 q[j]=QuantumRange-q[j];
4039 q+=(ptrdiff_t) GetPixelChannels(image);
4041 sync=SyncCacheViewAuthenticPixels(image_view,exception);
4042 if (sync == MagickFalse)
4044 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4050 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4051 if (proceed == MagickFalse)
4055 image_view=DestroyCacheView(image_view);
4061#if defined(MAGICKCORE_OPENMP_SUPPORT)
4062 #pragma omp parallel for schedule(static) shared(progress,status) \
4063 magick_number_threads(image,image,image->rows,1)
4065 for (y=0; y < (ssize_t) image->rows; y++)
4073 if (status == MagickFalse)
4075 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4076 if (q == (Quantum *) NULL)
4081 for (x=0; x < (ssize_t) image->columns; x++)
4086 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
4088 PixelChannel channel = GetPixelChannelChannel(image,j);
4089 PixelTrait traits = GetPixelChannelTraits(image,channel);
4090 if ((traits & UpdatePixelTrait) == 0)
4092 q[j]=QuantumRange-q[j];
4094 q+=(ptrdiff_t) GetPixelChannels(image);
4096 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4098 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4103#if defined(MAGICKCORE_OPENMP_SUPPORT)
4107 proceed=SetImageProgress(image,NegateImageTag,progress,image->rows);
4108 if (proceed == MagickFalse)
4112 image_view=DestroyCacheView(image_view);
4142MagickExport MagickBooleanType NormalizeImage(Image *image,
4143 ExceptionInfo *exception)
4149 black_point=0.02*image->columns*image->rows;
4150 white_point=0.99*image->columns*image->rows;
4151 return(ContrastStretchImage(image,black_point,white_point,exception));
4218#if defined(MAGICKCORE_HAVE_ATANH)
4219#define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
4221#define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
4240#define ScaledSigmoidal(a,b,x) ( \
4241 (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
4242 (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
4252static inline double InverseScaledSigmoidal(
const double a,
const double b,
4255 const double sig0=Sigmoidal(a,b,0.0);
4256 const double sig1=Sigmoidal(a,b,1.0);
4257 const double argument=(sig1-sig0)*x+sig0;
4258 const double clamped=
4260#if defined(MAGICKCORE_HAVE_ATANH)
4261 argument < -1+MagickEpsilon
4265 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4267 return(b+(2.0/a)*atanh(clamped));
4269 argument < MagickEpsilon
4273 ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
4275 return(b-log(1.0/clamped-1.0)/a);
4279MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
4280 const MagickBooleanType sharpen,
const double contrast,
const double midpoint,
4281 ExceptionInfo *exception)
4283#define SigmoidalContrastImageTag "SigmoidalContrast/Image"
4284#define ScaledSig(x) (ClampToQuantum((double) QuantumRange* \
4285 ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*((double) x))) )
4286#define InverseScaledSig(x) (ClampToQuantum((double) QuantumRange* \
4287 InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale* \
4305 assert(image != (Image *) NULL);
4306 assert(image->signature == MagickCoreSignature);
4307 if (IsEventLogging() != MagickFalse)
4308 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4313 if (contrast < MagickEpsilon)
4318 if (image->storage_class == PseudoClass)
4323 if( sharpen != MagickFalse )
4324 for (i=0; i < (ssize_t) image->colors; i++)
4326 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4327 image->colormap[i].red=(MagickRealType) ScaledSig(
4328 image->colormap[i].red);
4329 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4330 image->colormap[i].green=(MagickRealType) ScaledSig(
4331 image->colormap[i].green);
4332 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4333 image->colormap[i].blue=(MagickRealType) ScaledSig(
4334 image->colormap[i].blue);
4335 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4336 image->colormap[i].alpha=(MagickRealType) ScaledSig(
4337 image->colormap[i].alpha);
4340 for (i=0; i < (ssize_t) image->colors; i++)
4342 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
4343 image->colormap[i].red=(MagickRealType) InverseScaledSig(
4344 image->colormap[i].red);
4345 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
4346 image->colormap[i].green=(MagickRealType) InverseScaledSig(
4347 image->colormap[i].green);
4348 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
4349 image->colormap[i].blue=(MagickRealType) InverseScaledSig(
4350 image->colormap[i].blue);
4351 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
4352 image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
4353 image->colormap[i].alpha);
4361 image_view=AcquireAuthenticCacheView(image,exception);
4362#if defined(MAGICKCORE_OPENMP_SUPPORT)
4363 #pragma omp parallel for schedule(static) shared(progress,status) \
4364 magick_number_threads(image,image,image->rows,1)
4366 for (y=0; y < (ssize_t) image->rows; y++)
4374 if (status == MagickFalse)
4376 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4377 if (q == (Quantum *) NULL)
4382 for (x=0; x < (ssize_t) image->columns; x++)
4387 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4389 PixelChannel channel = GetPixelChannelChannel(image,i);
4390 PixelTrait traits = GetPixelChannelTraits(image,channel);
4391 if ((traits & UpdatePixelTrait) == 0)
4393 if( sharpen != MagickFalse )
4394 q[i]=ScaledSig(q[i]);
4396 q[i]=InverseScaledSig(q[i]);
4398 q+=(ptrdiff_t) GetPixelChannels(image);
4400 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4402 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4407#if defined(MAGICKCORE_OPENMP_SUPPORT)
4411 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress,
4413 if (proceed == MagickFalse)
4417 image_view=DestroyCacheView(image_view);
4447MagickExport MagickBooleanType WhiteBalanceImage(Image *image,
4448 ExceptionInfo *exception)
4450#define WhiteBalanceImageTag "WhiteBalance/Image"
4474 assert(image != (Image *) NULL);
4475 assert(image->signature == MagickCoreSignature);
4476 if (IsEventLogging() != MagickFalse)
4477 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4478 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4479 return(MagickFalse);
4480 status=TransformImageColorspace(image,LabColorspace,exception);
4483 image_view=AcquireAuthenticCacheView(image,exception);
4484 for (y=0; y < (ssize_t) image->rows; y++)
4492 if (status == MagickFalse)
4494 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4495 if (p == (Quantum *) NULL)
4500 for (x=0; x < (ssize_t) image->columns; x++)
4502 a_mean+=QuantumScale*(double) GetPixela(image,p)-0.5;
4503 b_mean+=QuantumScale*(double) GetPixelb(image,p)-0.5;
4504 p+=(ptrdiff_t) GetPixelChannels(image);
4507 a_mean/=((double) image->columns*image->rows);
4508 b_mean/=((double) image->columns*image->rows);
4510#if defined(MAGICKCORE_OPENMP_SUPPORT)
4511 #pragma omp parallel for schedule(static) shared(progress,status) \
4512 magick_number_threads(image,image,image->rows,1)
4514 for (y=0; y < (ssize_t) image->rows; y++)
4522 if (status == MagickFalse)
4524 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4525 if (q == (Quantum *) NULL)
4530 for (x=0; x < (ssize_t) image->columns; x++)
4539 a=(double) GetPixela(image,q)-1.1*(double) GetPixelL(image,q)*a_mean;
4540 b=(double) GetPixelb(image,q)-1.1*(double) GetPixelL(image,q)*b_mean;
4541 SetPixela(image,ClampToQuantum(a),q);
4542 SetPixelb(image,ClampToQuantum(b),q);
4543 q+=(ptrdiff_t) GetPixelChannels(image);
4545 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4547 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4552#if defined(MAGICKCORE_OPENMP_SUPPORT)
4556 proceed=SetImageProgress(image,WhiteBalanceImageTag,progress,image->rows);
4557 if (proceed == MagickFalse)
4561 image_view=DestroyCacheView(image_view);
4562 artifact=GetImageArtifact(image,
"white-balance:vibrance");
4563 if (artifact != (
const char *) NULL)
4580 flags=ParseGeometry(artifact,&geometry_info);
4581 if ((flags & RhoValue) != 0)
4582 black_point=geometry_info.rho;
4583 if ((flags & PercentValue) != 0)
4584 black_point*=((double) QuantumRange/100.0);
4585 channel_mask=SetImageChannelMask(image,(ChannelType) (aChannel |
4587 status&=(MagickStatusType) LevelImage(image,black_point,(
double)
4588 QuantumRange-black_point,1.0,exception);
4589 (void) SetImageChannelMask(image,channel_mask);
4591 status&=(MagickStatusType) TransformImageColorspace(image,sRGBColorspace,
4593 return(status != 0 ? MagickTrue : MagickFalse);