43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colorspace.h"
51#include "MagickCore/colorspace-private.h"
52#include "MagickCore/configure.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/decorate.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/effect.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/gem.h"
62#include "MagickCore/gem-private.h"
63#include "MagickCore/geometry.h"
64#include "MagickCore/image-private.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/montage.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel-accessor.h"
73#include "MagickCore/property.h"
74#include "MagickCore/quantize.h"
75#include "MagickCore/quantum.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/random_.h"
78#include "MagickCore/random-private.h"
79#include "MagickCore/resize.h"
80#include "MagickCore/resource_.h"
81#include "MagickCore/segment.h"
82#include "MagickCore/shear.h"
83#include "MagickCore/signature-private.h"
84#include "MagickCore/string_.h"
85#include "MagickCore/string-private.h"
86#include "MagickCore/thread-private.h"
87#include "MagickCore/threshold.h"
88#include "MagickCore/token.h"
89#include "MagickCore/transform.h"
90#include "MagickCore/xml-tree.h"
91#include "MagickCore/xml-tree-private.h"
96#define ThresholdsFilename "thresholds.xml"
119#if MAGICKCORE_ZERO_CONFIGURATION_SUPPORT
120 #include "MagickCore/threshold-map.h"
122static const char *
const
124 "<?xml version=\"1.0\"?>"
126 " <threshold map=\"threshold\" alias=\"1x1\">"
127 " <description>Threshold 1x1 (non-dither)</description>"
128 " <levels width=\"1\" height=\"1\" divisor=\"2\">"
132 " <threshold map=\"checks\" alias=\"2x1\">"
133 " <description>Checkerboard 2x1 (dither)</description>"
134 " <levels width=\"2\" height=\"2\" divisor=\"3\">"
146 *GetThresholdMapFile(
const char *,
const char *,
const char *,
ExceptionInfo *);
182MagickExport
Image *AdaptiveThresholdImage(
const Image *image,
183 const size_t width,
const size_t height,
const double bias,
186#define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
210 assert(image != (
Image *) NULL);
211 assert(image->signature == MagickCoreSignature);
213 assert(exception->signature == MagickCoreSignature);
214 if (IsEventLogging() != MagickFalse)
215 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
216 threshold_image=CloneImage(image,0,0,MagickTrue,exception);
217 if (threshold_image == (
Image *) NULL)
218 return((
Image *) NULL);
219 if ((width == 0) || (height == 0))
220 return(threshold_image);
221 status=SetImageStorageClass(threshold_image,DirectClass,exception);
222 if (status == MagickFalse)
224 threshold_image=DestroyImage(threshold_image);
225 return((
Image *) NULL);
232 number_pixels=(MagickSizeType) width*height;
233 image_view=AcquireVirtualCacheView(image,exception);
234 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
235#if defined(MAGICKCORE_OPENMP_SUPPORT)
236 #pragma omp parallel for schedule(static) shared(progress,status) \
237 magick_number_threads(image,threshold_image,image->rows,1)
239 for (y=0; y < (ssize_t) image->rows; y++)
242 channel_bias[MaxPixelChannels],
243 channel_sum[MaxPixelChannels];
247 *magick_restrict pixels;
259 if (status == MagickFalse)
261 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
262 (height/2L),image->columns+width,height,exception);
263 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
265 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
270 center=(ssize_t) GetPixelChannels(image)*((ssize_t) image->columns+
271 (ssize_t) width)*((ssize_t) height/2L)+(ssize_t) GetPixelChannels(image)*
273 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
275 PixelChannel channel = GetPixelChannelChannel(image,i);
276 PixelTrait traits = GetPixelChannelTraits(image,channel);
277 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
279 if ((traits == UndefinedPixelTrait) ||
280 (threshold_traits == UndefinedPixelTrait))
282 if ((threshold_traits & CopyPixelTrait) != 0)
284 SetPixelChannel(threshold_image,channel,p[center+i],q);
288 channel_bias[channel]=0.0;
289 channel_sum[channel]=0.0;
290 for (v=0; v < (ssize_t) height; v++)
292 for (u=0; u < (ssize_t) width; u++)
294 if (u == (ssize_t) (width-1))
295 channel_bias[channel]+=(double) pixels[i];
296 channel_sum[channel]+=(double) pixels[i];
297 pixels+=(ptrdiff_t) GetPixelChannels(image);
299 pixels+=(ptrdiff_t) GetPixelChannels(image)*image->columns;
302 for (x=0; x < (ssize_t) image->columns; x++)
304 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
309 PixelChannel channel = GetPixelChannelChannel(image,i);
310 PixelTrait traits = GetPixelChannelTraits(image,channel);
311 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
313 if ((traits == UndefinedPixelTrait) ||
314 (threshold_traits == UndefinedPixelTrait))
316 if ((threshold_traits & CopyPixelTrait) != 0)
318 SetPixelChannel(threshold_image,channel,p[center+i],q);
321 channel_sum[channel]-=channel_bias[channel];
322 channel_bias[channel]=0.0;
324 for (v=0; v < (ssize_t) height; v++)
326 channel_bias[channel]+=(double) pixels[i];
327 pixels+=(width-1)*GetPixelChannels(image);
328 channel_sum[channel]+=(double) pixels[i];
329 pixels+=(ptrdiff_t) GetPixelChannels(image)*(image->columns+1);
331 mean=(double) (channel_sum[channel]/number_pixels+bias);
332 SetPixelChannel(threshold_image,channel,(Quantum) ((
double)
333 p[center+i] <= mean ? 0 : QuantumRange),q);
335 p+=(ptrdiff_t) GetPixelChannels(image);
336 q+=(ptrdiff_t) GetPixelChannels(threshold_image);
338 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
340 if (image->progress_monitor != (MagickProgressMonitor) NULL)
345#if defined(MAGICKCORE_OPENMP_SUPPORT)
349 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress,
351 if (proceed == MagickFalse)
355 threshold_image->type=image->type;
356 threshold_view=DestroyCacheView(threshold_view);
357 image_view=DestroyCacheView(image_view);
358 if (status == MagickFalse)
359 threshold_image=DestroyImage(threshold_image);
360 return(threshold_image);
392static double KapurThreshold(
const Image *image,
const double *histogram,
395#define MaxIntensity 255
399 *cumulative_histogram,
415 cumulative_histogram=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
416 sizeof(*cumulative_histogram));
417 black_entropy=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
418 sizeof(*black_entropy));
419 white_entropy=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
420 sizeof(*white_entropy));
421 if ((cumulative_histogram == (
double *) NULL) ||
422 (black_entropy == (
double *) NULL) || (white_entropy == (
double *) NULL))
424 if (white_entropy != (
double *) NULL)
425 white_entropy=(
double *) RelinquishMagickMemory(white_entropy);
426 if (black_entropy != (
double *) NULL)
427 black_entropy=(
double *) RelinquishMagickMemory(black_entropy);
428 if (cumulative_histogram != (
double *) NULL)
429 cumulative_histogram=(
double *)
430 RelinquishMagickMemory(cumulative_histogram);
431 (void) ThrowMagickException(exception,GetMagickModule(),
432 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
438 cumulative_histogram[0]=histogram[0];
439 for (i=1; i <= MaxIntensity; i++)
440 cumulative_histogram[i]=cumulative_histogram[i-1]+histogram[i];
441 epsilon=MagickMinimumValue;
442 for (j=0; j <= MaxIntensity; j++)
447 black_entropy[j]=0.0;
448 if (cumulative_histogram[j] > epsilon)
451 for (i=0; i <= j; i++)
452 if (histogram[i] > epsilon)
453 entropy-=histogram[i]/cumulative_histogram[j]*
454 log(histogram[i]/cumulative_histogram[j]);
455 black_entropy[j]=entropy;
460 white_entropy[j]=0.0;
461 if ((1.0-cumulative_histogram[j]) > epsilon)
464 for (i=j+1; i <= MaxIntensity; i++)
465 if (histogram[i] > epsilon)
466 entropy-=histogram[i]/(1.0-cumulative_histogram[j])*
467 log(histogram[i]/(1.0-cumulative_histogram[j]));
468 white_entropy[j]=entropy;
474 maximum_entropy=black_entropy[0]+white_entropy[0];
476 for (j=1; j <= MaxIntensity; j++)
477 if ((black_entropy[j]+white_entropy[j]) > maximum_entropy)
479 maximum_entropy=black_entropy[j]+white_entropy[j];
480 threshold=(size_t) j;
485 white_entropy=(
double *) RelinquishMagickMemory(white_entropy);
486 black_entropy=(
double *) RelinquishMagickMemory(black_entropy);
487 cumulative_histogram=(
double *) RelinquishMagickMemory(cumulative_histogram);
488 return(100.0*threshold/MaxIntensity);
491static double OTSUThreshold(
const Image *image,
const double *histogram,
508 myu=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
sizeof(*myu));
509 omega=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
sizeof(*omega));
510 probability=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
511 sizeof(*probability));
512 sigma=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
sizeof(*sigma));
513 if ((myu == (
double *) NULL) || (omega == (
double *) NULL) ||
514 (probability == (
double *) NULL) || (sigma == (
double *) NULL))
516 if (sigma != (
double *) NULL)
517 sigma=(
double *) RelinquishMagickMemory(sigma);
518 if (probability != (
double *) NULL)
519 probability=(
double *) RelinquishMagickMemory(probability);
520 if (omega != (
double *) NULL)
521 omega=(
double *) RelinquishMagickMemory(omega);
522 if (myu != (
double *) NULL)
523 myu=(
double *) RelinquishMagickMemory(myu);
524 (void) ThrowMagickException(exception,GetMagickModule(),
525 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
531 for (i=0; i <= (ssize_t) MaxIntensity; i++)
532 probability[i]=histogram[i];
536 omega[0]=probability[0];
538 for (i=1; i <= (ssize_t) MaxIntensity; i++)
540 omega[i]=omega[i-1]+probability[i];
541 myu[i]=myu[i-1]+i*probability[i];
548 for (i=0; i < (ssize_t) MaxIntensity; i++)
551 if ((omega[i] != 0.0) && (omega[i] != 1.0))
552 sigma[i]=pow(myu[MaxIntensity]*omega[i]-myu[i],2.0)/(omega[i]*(1.0-
554 if (sigma[i] > max_sigma)
557 threshold=(double) i;
563 myu=(
double *) RelinquishMagickMemory(myu);
564 omega=(
double *) RelinquishMagickMemory(omega);
565 probability=(
double *) RelinquishMagickMemory(probability);
566 sigma=(
double *) RelinquishMagickMemory(sigma);
567 return(100.0*threshold/MaxIntensity);
570static double TriangleThreshold(
const double *histogram)
599 for (i=0; i <= (ssize_t) MaxIntensity; i++)
600 if (histogram[i] > 0.0)
606 for (i=(ssize_t) MaxIntensity; i >= 0; i--)
607 if (histogram[i] > 0.0)
614 for (i=0; i <= (ssize_t) MaxIntensity; i++)
615 if (histogram[i] > count)
626 if ((max-start) >= (end-max))
631 c=(-1.0)*(a*x1+b*y1);
632 inverse_ratio=1.0/sqrt(a*a+b*b+c*c);
635 if (x2 == (
double) start)
636 for (i=start; i < max; i++)
638 segment=inverse_ratio*(a*i+b*histogram[i]+c);
639 distance=sqrt(segment*segment);
640 if ((distance > max_distance) && (segment > 0.0))
643 max_distance=distance;
647 for (i=end; i > max; i--)
649 segment=inverse_ratio*(a*i+b*histogram[i]+c);
650 distance=sqrt(segment*segment);
651 if ((distance > max_distance) && (segment < 0.0))
654 max_distance=distance;
657 return(100.0*threshold/MaxIntensity);
660MagickExport MagickBooleanType AutoThresholdImage(
Image *image,
667 property[MagickPathExtent];
687 assert(image != (
Image *) NULL);
688 assert(image->signature == MagickCoreSignature);
689 if (IsEventLogging() != MagickFalse)
690 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
691 histogram=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
693 if (histogram == (
double *) NULL)
694 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
697 (void) memset(histogram,0,(MaxIntensity+1UL)*
sizeof(*histogram));
698 image_view=AcquireVirtualCacheView(image,exception);
699 for (y=0; y < (ssize_t) image->rows; y++)
707 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
708 if (p == (
const Quantum *) NULL)
710 for (x=0; x < (ssize_t) image->columns; x++)
712 double intensity = GetPixelIntensity(image,p);
713 histogram[ScaleQuantumToChar(ClampToQuantum(intensity))]++;
714 p+=(ptrdiff_t) GetPixelChannels(image);
717 image_view=DestroyCacheView(image_view);
722 for (i=0; i <= (ssize_t) MaxIntensity; i++)
724 gamma=PerceptibleReciprocal(sum);
725 for (i=0; i <= (ssize_t) MaxIntensity; i++)
726 histogram[i]=gamma*histogram[i];
732 case KapurThresholdMethod:
734 threshold=KapurThreshold(image,histogram,exception);
737 case OTSUThresholdMethod:
740 threshold=OTSUThreshold(image,histogram,exception);
743 case TriangleThresholdMethod:
745 threshold=TriangleThreshold(histogram);
749 histogram=(
double *) RelinquishMagickMemory(histogram);
752 if (status == MagickFalse)
757 (void) FormatLocaleString(property,MagickPathExtent,
"%g%%",threshold);
758 (void) SetImageProperty(image,
"auto-threshold:threshold",property,exception);
759 if (IsStringTrue(GetImageArtifact(image,
"auto-threshold:verbose")) != MagickFalse)
760 (void) FormatLocaleFile(stdout,
"%.*g%%\n",GetMagickPrecision(),threshold);
761 return(BilevelImage(image,(
double) QuantumRange*threshold/100.0,exception));
805MagickExport MagickBooleanType BilevelImage(
Image *image,
const double threshold,
808#define ThresholdImageTag "Threshold/Image"
822 assert(image != (
Image *) NULL);
823 assert(image->signature == MagickCoreSignature);
824 if (IsEventLogging() != MagickFalse)
825 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
826 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
828 if (IsGrayColorspace(image->colorspace) == MagickFalse)
829 (void) SetImageColorspace(image,sRGBColorspace,exception);
835 image_view=AcquireAuthenticCacheView(image,exception);
836#if defined(MAGICKCORE_OPENMP_SUPPORT)
837 #pragma omp parallel for schedule(static) shared(progress,status) \
838 magick_number_threads(image,image,image->rows,2)
840 for (y=0; y < (ssize_t) image->rows; y++)
848 if (status == MagickFalse)
850 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
851 if (q == (Quantum *) NULL)
856 for (x=0; x < (ssize_t) image->columns; x++)
864 pixel=GetPixelIntensity(image,q);
865 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
867 PixelChannel channel = GetPixelChannelChannel(image,i);
868 PixelTrait traits = GetPixelChannelTraits(image,channel);
869 if ((traits & UpdatePixelTrait) == 0)
871 if (image->channel_mask != AllChannels)
873 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
875 q+=(ptrdiff_t) GetPixelChannels(image);
877 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
879 if (image->progress_monitor != (MagickProgressMonitor) NULL)
884#if defined(MAGICKCORE_OPENMP_SUPPORT)
888 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
890 if (proceed == MagickFalse)
894 image_view=DestroyCacheView(image_view);
927MagickExport MagickBooleanType BlackThresholdImage(
Image *image,
930#define ThresholdImageTag "Threshold/Image"
953 assert(image != (
Image *) NULL);
954 assert(image->signature == MagickCoreSignature);
955 if (IsEventLogging() != MagickFalse)
956 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
957 if (thresholds == (
const char *) NULL)
959 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
961 if (IsGrayColorspace(image->colorspace) != MagickFalse)
962 (void) SetImageColorspace(image,sRGBColorspace,exception);
963 GetPixelInfo(image,&threshold);
964 flags=ParseGeometry(thresholds,&geometry_info);
965 threshold.red=geometry_info.rho;
966 threshold.green=geometry_info.rho;
967 threshold.blue=geometry_info.rho;
968 threshold.black=geometry_info.rho;
969 threshold.alpha=100.0;
970 if ((flags & SigmaValue) != 0)
971 threshold.green=geometry_info.sigma;
972 if ((flags & XiValue) != 0)
973 threshold.blue=geometry_info.xi;
974 if ((flags & PsiValue) != 0)
975 threshold.alpha=geometry_info.psi;
976 if (threshold.colorspace == CMYKColorspace)
978 if ((flags & PsiValue) != 0)
979 threshold.black=geometry_info.psi;
980 if ((flags & ChiValue) != 0)
981 threshold.alpha=geometry_info.chi;
983 if ((flags & PercentValue) != 0)
985 threshold.red*=((MagickRealType) QuantumRange/100.0);
986 threshold.green*=((MagickRealType) QuantumRange/100.0);
987 threshold.blue*=((MagickRealType) QuantumRange/100.0);
988 threshold.black*=((MagickRealType) QuantumRange/100.0);
989 threshold.alpha*=((MagickRealType) QuantumRange/100.0);
996 image_view=AcquireAuthenticCacheView(image,exception);
997#if defined(MAGICKCORE_OPENMP_SUPPORT)
998 #pragma omp parallel for schedule(static) shared(progress,status) \
999 magick_number_threads(image,image,image->rows,2)
1001 for (y=0; y < (ssize_t) image->rows; y++)
1009 if (status == MagickFalse)
1011 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1012 if (q == (Quantum *) NULL)
1017 for (x=0; x < (ssize_t) image->columns; x++)
1025 pixel=GetPixelIntensity(image,q);
1026 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1028 PixelChannel channel = GetPixelChannelChannel(image,i);
1029 PixelTrait traits = GetPixelChannelTraits(image,channel);
1030 if ((traits & UpdatePixelTrait) == 0)
1032 if (image->channel_mask != AllChannels)
1033 pixel=(double) q[i];
1034 if (pixel < GetPixelInfoChannel(&threshold,channel))
1037 q+=(ptrdiff_t) GetPixelChannels(image);
1039 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1041 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1046#if defined(MAGICKCORE_OPENMP_SUPPORT)
1050 proceed=SetImageProgress(image,ThresholdImageTag,progress,
1052 if (proceed == MagickFalse)
1056 image_view=DestroyCacheView(image_view);
1089#define ClampImageTag "Clamp/Image"
1103 assert(image != (
Image *) NULL);
1104 assert(image->signature == MagickCoreSignature);
1105 if (IsEventLogging() != MagickFalse)
1106 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1107 if (image->storage_class == PseudoClass)
1116 for (i=0; i < (ssize_t) image->colors; i++)
1118 q->red=(double) ClampPixel(q->red);
1119 q->green=(double) ClampPixel(q->green);
1120 q->blue=(double) ClampPixel(q->blue);
1121 q->alpha=(double) ClampPixel(q->alpha);
1124 return(SyncImage(image,exception));
1131 image_view=AcquireAuthenticCacheView(image,exception);
1132#if defined(MAGICKCORE_OPENMP_SUPPORT)
1133 #pragma omp parallel for schedule(static) shared(progress,status) \
1134 magick_number_threads(image,image,image->rows,2)
1136 for (y=0; y < (ssize_t) image->rows; y++)
1144 if (status == MagickFalse)
1146 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1147 if (q == (Quantum *) NULL)
1152 for (x=0; x < (ssize_t) image->columns; x++)
1157 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1159 PixelChannel channel = GetPixelChannelChannel(image,i);
1160 PixelTrait traits = GetPixelChannelTraits(image,channel);
1161 if ((traits & UpdatePixelTrait) == 0)
1163 q[i]=ClampPixel((MagickRealType) q[i]);
1165 q+=(ptrdiff_t) GetPixelChannels(image);
1167 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1169 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1174#if defined(MAGICKCORE_OPENMP_SUPPORT)
1178 proceed=SetImageProgress(image,ClampImageTag,progress,image->rows);
1179 if (proceed == MagickFalse)
1183 image_view=DestroyCacheView(image_view);
1217MagickExport MagickBooleanType ColorThresholdImage(
Image *image,
1221#define ThresholdImageTag "Threshold/Image"
1230 illuminant = D65Illuminant;
1248 assert(image != (
Image *) NULL);
1249 assert(image->signature == MagickCoreSignature);
1250 if (IsEventLogging() != MagickFalse)
1251 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1252 status=AcquireImageColormap(image,2,exception);
1253 if (status == MagickFalse)
1255 artifact=GetImageArtifact(image,
"color:illuminant");
1256 if (artifact != (
const char *) NULL)
1261 illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
1263 if (illuminant_type < 0)
1264 illuminant=UndefinedIlluminant;
1266 illuminant=(IlluminantType) illuminant_type;
1268 start=(*start_color);
1270 switch (image->colorspace)
1274 ConvertRGBToHCL(start_color->red,start_color->green,start_color->blue,
1275 &start.red,&start.green,&start.blue);
1276 ConvertRGBToHCL(stop_color->red,stop_color->green,stop_color->blue,
1277 &stop.red,&stop.green,&stop.blue);
1282 ConvertRGBToHSB(start_color->red,start_color->green,start_color->blue,
1283 &start.red,&start.green,&start.blue);
1284 ConvertRGBToHSB(stop_color->red,stop_color->green,stop_color->blue,
1285 &stop.red,&stop.green,&stop.blue);
1290 ConvertRGBToHSL(start_color->red,start_color->green,start_color->blue,
1291 &start.red,&start.green,&start.blue);
1292 ConvertRGBToHSL(stop_color->red,stop_color->green,stop_color->blue,
1293 &stop.red,&stop.green,&stop.blue);
1298 ConvertRGBToHSV(start_color->red,start_color->green,start_color->blue,
1299 &start.red,&start.green,&start.blue);
1300 ConvertRGBToHSV(stop_color->red,stop_color->green,stop_color->blue,
1301 &stop.red,&stop.green,&stop.blue);
1306 ConvertRGBToHWB(start_color->red,start_color->green,start_color->blue,
1307 &start.red,&start.green,&start.blue);
1308 ConvertRGBToHWB(stop_color->red,stop_color->green,stop_color->blue,
1309 &stop.red,&stop.green,&stop.blue);
1314 ConvertRGBToLab(start_color->red,start_color->green,start_color->blue,
1315 illuminant,&start.red,&start.green,&start.blue);
1316 ConvertRGBToLab(stop_color->red,stop_color->green,stop_color->blue,
1317 illuminant,&stop.red,&stop.green,&stop.blue);
1322 start.red*=QuantumScale;
1323 start.green*=QuantumScale;
1324 start.blue*=QuantumScale;
1325 stop.red*=QuantumScale;
1326 stop.green*=QuantumScale;
1327 stop.blue*=QuantumScale;
1331 start.red*=(double) QuantumRange;
1332 start.green*=(double) QuantumRange;
1333 start.blue*=(double) QuantumRange;
1334 stop.red*=(double) QuantumRange;
1335 stop.green*=(double) QuantumRange;
1336 stop.blue*=(double) QuantumRange;
1338 image_view=AcquireAuthenticCacheView(image,exception);
1339#if defined(MAGICKCORE_OPENMP_SUPPORT)
1340 #pragma omp parallel for schedule(static) shared(progress,status) \
1341 magick_number_threads(image,image,image->rows,2)
1343 for (y=0; y < (ssize_t) image->rows; y++)
1351 if (status == MagickFalse)
1353 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1354 if (q == (Quantum *) NULL)
1359 for (x=0; x < (ssize_t) image->columns; x++)
1362 foreground = MagickTrue;
1367 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1369 PixelChannel channel = GetPixelChannelChannel(image,i);
1370 PixelTrait traits = GetPixelChannelTraits(image,channel);
1371 if ((traits & UpdatePixelTrait) == 0)
1373 if (((
double) q[i] < GetPixelInfoChannel(&start,channel)) ||
1374 ((
double) q[i] > GetPixelInfoChannel(&stop,channel)))
1375 foreground=MagickFalse;
1377 SetPixelIndex(image,(Quantum) (foreground != MagickFalse ? 1 : 0),q);
1378 q+=(ptrdiff_t) GetPixelChannels(image);
1380 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1382 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1387#if defined(MAGICKCORE_OPENMP_SUPPORT)
1391 proceed=SetImageProgress(image,ThresholdImageTag,progress,
1393 if (proceed == MagickFalse)
1397 image_view=DestroyCacheView(image_view);
1398 image->colorspace=sRGBColorspace;
1399 return(SyncImage(image,exception));
1427 if (map->map_id != (
char *) NULL)
1428 map->map_id=DestroyString(map->map_id);
1429 if (map->description != (
char *) NULL)
1430 map->description=DestroyString(map->description);
1431 if (map->levels != (ssize_t *) NULL)
1432 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1463MagickExport
ThresholdMap *GetThresholdMap(
const char *map_id,
1469 map=GetThresholdMapFile(BuiltinMap,
"built-in",map_id,exception);
1472#if !MAGICKCORE_ZERO_CONFIGURATION_SUPPORT
1480 options=GetConfigureOptions(ThresholdsFilename,exception);
1481 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1484 map=GetThresholdMapFile((
const char *) GetStringInfoDatum(option),
1485 GetStringInfoPath(option),map_id,exception);
1488 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1490 options=DestroyConfigureOptions(options);
1526static ThresholdMap *GetThresholdMapFile(
const char *xml,
const char *filename,
1551 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1552 "Loading threshold map file \"%s\" ...",filename);
1554 thresholds=NewXMLTree(xml,exception);
1557 for (threshold=GetXMLTreeChild(thresholds,
"threshold");
1559 threshold=GetNextXMLTreeTag(threshold))
1561 attribute=GetXMLTreeAttribute(threshold,
"map");
1562 if ((attribute != (
char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1564 attribute=GetXMLTreeAttribute(threshold,
"alias");
1565 if ((attribute != (
char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1570 thresholds=DestroyXMLTree(thresholds);
1573 description=GetXMLTreeChild(threshold,
"description");
1576 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1577 "XmlMissingElement",
"<description>, map \"%s\"",map_id);
1578 thresholds=DestroyXMLTree(thresholds);
1581 levels=GetXMLTreeChild(threshold,
"levels");
1584 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1585 "XmlMissingElement",
"<levels>, map \"%s\"", map_id);
1586 thresholds=DestroyXMLTree(thresholds);
1589 map=(
ThresholdMap *) AcquireCriticalMemory(
sizeof(*map));
1590 map->map_id=(
char *) NULL;
1591 map->description=(
char *) NULL;
1592 map->levels=(ssize_t *) NULL;
1593 attribute=GetXMLTreeAttribute(threshold,
"map");
1594 if (attribute != (
char *) NULL)
1595 map->map_id=ConstantString(attribute);
1596 content=GetXMLTreeContent(description);
1597 if (content != (
char *) NULL)
1598 map->description=ConstantString(content);
1599 attribute=GetXMLTreeAttribute(levels,
"width");
1600 if (attribute == (
char *) NULL)
1602 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1603 "XmlMissingAttribute",
"<levels width>, map \"%s\"",map_id);
1604 thresholds=DestroyXMLTree(thresholds);
1605 map=DestroyThresholdMap(map);
1608 map->width=StringToUnsignedLong(attribute);
1609 if (map->width == 0)
1611 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1612 "XmlInvalidAttribute",
"<levels width>, map \"%s\"",map_id);
1613 thresholds=DestroyXMLTree(thresholds);
1614 map=DestroyThresholdMap(map);
1617 attribute=GetXMLTreeAttribute(levels,
"height");
1618 if (attribute == (
char *) NULL)
1620 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1621 "XmlMissingAttribute",
"<levels height>, map \"%s\"",map_id);
1622 thresholds=DestroyXMLTree(thresholds);
1623 map=DestroyThresholdMap(map);
1626 map->height=StringToUnsignedLong(attribute);
1627 if (map->height == 0)
1629 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1630 "XmlInvalidAttribute",
"<levels height>, map \"%s\"",map_id);
1631 thresholds=DestroyXMLTree(thresholds);
1632 map=DestroyThresholdMap(map);
1635 attribute=GetXMLTreeAttribute(levels,
"divisor");
1636 if (attribute == (
char *) NULL)
1638 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1639 "XmlMissingAttribute",
"<levels divisor>, map \"%s\"",map_id);
1640 thresholds=DestroyXMLTree(thresholds);
1641 map=DestroyThresholdMap(map);
1644 map->divisor=(ssize_t) StringToLong(attribute);
1645 if (map->divisor < 2)
1647 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1648 "XmlInvalidAttribute",
"<levels divisor>, map \"%s\"",map_id);
1649 thresholds=DestroyXMLTree(thresholds);
1650 map=DestroyThresholdMap(map);
1653 content=GetXMLTreeContent(levels);
1654 if (content == (
char *) NULL)
1656 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1657 "XmlMissingContent",
"<levels>, map \"%s\"",map_id);
1658 thresholds=DestroyXMLTree(thresholds);
1659 map=DestroyThresholdMap(map);
1662 map->levels=(ssize_t *) AcquireQuantumMemory((
size_t) map->width,map->height*
1663 sizeof(*map->levels));
1664 if (map->levels == (ssize_t *) NULL)
1665 ThrowFatalException(ResourceLimitFatalError,
"UnableToAcquireThresholdMap");
1666 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1668 map->levels[i]=(ssize_t) strtol(content,&p,10);
1671 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1672 "XmlInvalidContent",
"<level> too few values, map \"%s\"",map_id);
1673 thresholds=DestroyXMLTree(thresholds);
1674 map=DestroyThresholdMap(map);
1677 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1679 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1680 "XmlInvalidContent",
"<level> %.20g out of range, map \"%s\"",
1681 (
double) map->levels[i],map_id);
1682 thresholds=DestroyXMLTree(thresholds);
1683 map=DestroyThresholdMap(map);
1688 value=(double) strtol(content,&p,10);
1692 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1693 "XmlInvalidContent",
"<level> too many values, map \"%s\"",map_id);
1694 thresholds=DestroyXMLTree(thresholds);
1695 map=DestroyThresholdMap(map);
1698 thresholds=DestroyXMLTree(thresholds);
1732MagickBooleanType ListThresholdMapFile(FILE *file,
const char *xml,
1745 assert( xml != (
char *) NULL );
1746 assert( file != (FILE *) NULL );
1747 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1748 "Loading threshold map file \"%s\" ...",filename);
1749 thresholds=NewXMLTree(xml,exception);
1751 return(MagickFalse);
1752 (void) FormatLocaleFile(file,
"%-16s %-12s %s\n",
"Map",
"Alias",
"Description");
1753 (void) FormatLocaleFile(file,
1754 "----------------------------------------------------\n");
1755 threshold=GetXMLTreeChild(thresholds,
"threshold");
1757 threshold=GetNextXMLTreeTag(threshold))
1759 map=GetXMLTreeAttribute(threshold,
"map");
1760 if (map == (
char *) NULL)
1762 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1763 "XmlMissingAttribute",
"<map>");
1764 thresholds=DestroyXMLTree(thresholds);
1765 return(MagickFalse);
1767 alias=GetXMLTreeAttribute(threshold,
"alias");
1768 description=GetXMLTreeChild(threshold,
"description");
1771 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1772 "XmlMissingElement",
"<description>, map \"%s\"",map);
1773 thresholds=DestroyXMLTree(thresholds);
1774 return(MagickFalse);
1776 content=GetXMLTreeContent(description);
1777 if (content == (
char *) NULL)
1779 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1780 "XmlMissingContent",
"<description>, map \"%s\"", map);
1781 thresholds=DestroyXMLTree(thresholds);
1782 return(MagickFalse);
1784 (void) FormatLocaleFile(file,
"%-16s %-12s %s\n",map,alias ? alias :
"",
1787 thresholds=DestroyXMLTree(thresholds);
1816MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1829 if (file == (FILE *) NULL)
1831 options=GetConfigureOptions(ThresholdsFilename,exception);
1832 (void) FormatLocaleFile(file,
1833 "\n Threshold Maps for Ordered Dither Operations\n");
1834 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1837 (void) FormatLocaleFile(file,
"\nPath: %s\n\n",GetStringInfoPath(option));
1838 status&=(MagickStatusType) ListThresholdMapFile(file,(
const char *)
1839 GetStringInfoDatum(option),GetStringInfoPath(option),exception);
1840 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1842 options=DestroyConfigureOptions(options);
1843 return(status != 0 ? MagickTrue : MagickFalse);
1893MagickExport MagickBooleanType OrderedDitherImage(
Image *image,
1896#define DitherImageTag "Dither/Image"
1902 token[MagickPathExtent];
1908 levels[CompositePixelChannel];
1923 assert(image != (
Image *) NULL);
1924 assert(image->signature == MagickCoreSignature);
1926 assert(exception->signature == MagickCoreSignature);
1927 if (IsEventLogging() != MagickFalse)
1928 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1929 if (threshold_map == (
const char *) NULL)
1931 p=(
char *) threshold_map;
1932 while (((isspace((
int) ((
unsigned char) *p)) != 0) || (*p ==
',')) &&
1936 while (((isspace((
int) ((
unsigned char) *p)) == 0) && (*p !=
',')) &&
1939 if ((p-threshold_map) >= (MagickPathExtent-1))
1941 token[p-threshold_map]=(*p);
1944 token[p-threshold_map]=
'\0';
1945 map=GetThresholdMap(token,exception);
1948 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1949 "InvalidArgument",
"%s : '%s'",
"ordered-dither",threshold_map);
1950 return(MagickFalse);
1952 for (i=0; i < MaxPixelChannels; i++)
1954 p=strchr((
char *) threshold_map,
',');
1955 if ((p != (
char *) NULL) && (isdigit((
int) ((
unsigned char) *(++p))) != 0))
1957 (void) GetNextToken(p,&p,MagickPathExtent,token);
1958 for (i=0; (i < MaxPixelChannels); i++)
1959 levels[i]=StringToDouble(token,(
char **) NULL);
1960 for (i=0; (*p !=
'\0') && (i < MaxPixelChannels); i++)
1962 (void) GetNextToken(p,&p,MagickPathExtent,token);
1964 (void) GetNextToken(p,&p,MagickPathExtent,token);
1965 levels[i]=StringToDouble(token,(
char **) NULL);
1968 for (i=0; i < MaxPixelChannels; i++)
1969 if (fabs(levels[i]) >= 1)
1971 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1972 return(MagickFalse);
1975 image_view=AcquireAuthenticCacheView(image,exception);
1976#if defined(MAGICKCORE_OPENMP_SUPPORT)
1977 #pragma omp parallel for schedule(static) shared(progress,status) \
1978 magick_number_threads(image,image,image->rows,2)
1980 for (y=0; y < (ssize_t) image->rows; y++)
1988 if (status == MagickFalse)
1990 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1991 if (q == (Quantum *) NULL)
1996 for (x=0; x < (ssize_t) image->columns; x++)
2003 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2009 PixelChannel channel = GetPixelChannelChannel(image,j);
2010 PixelTrait traits = GetPixelChannelTraits(image,channel);
2011 if ((traits & UpdatePixelTrait) == 0)
2013 if (fabs(levels[n]) < MagickEpsilon)
2018 threshold=(ssize_t) (QuantumScale*(
double) q[j]*(levels[n]*
2019 (map->divisor-1)+1));
2020 level=threshold/(map->divisor-1);
2021 threshold-=level*(map->divisor-1);
2022 q[j]=ClampToQuantum((
double) (level+(threshold >=
2023 map->levels[(x % (ssize_t) map->width)+(ssize_t) map->width*
2024 (y % (ssize_t) map->height)]))*(
double) QuantumRange/levels[n]);
2027 q+=(ptrdiff_t) GetPixelChannels(image);
2029 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2031 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2036#if defined(MAGICKCORE_OPENMP_SUPPORT)
2040 proceed=SetImageProgress(image,DitherImageTag,progress,image->rows);
2041 if (proceed == MagickFalse)
2045 image_view=DestroyCacheView(image_view);
2046 map=DestroyThresholdMap(map);
2080static inline Quantum PerceptibleThreshold(
const Quantum quantum,
2081 const double epsilon)
2086 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
2087 if ((sign*(
double) quantum) >= epsilon)
2089 return((Quantum) (sign*epsilon));
2092MagickExport MagickBooleanType PerceptibleImage(
Image *image,
2095#define PerceptibleImageTag "Perceptible/Image"
2109 assert(image != (
Image *) NULL);
2110 assert(image->signature == MagickCoreSignature);
2111 if (IsEventLogging() != MagickFalse)
2112 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2113 if (image->storage_class == PseudoClass)
2122 for (i=0; i < (ssize_t) image->colors; i++)
2124 if ((GetPixelChannelTraits(image,RedPixelChannel) & UpdatePixelTrait) != 0)
2125 q->red=(MagickRealType) PerceptibleThreshold(ClampToQuantum(q->red),
2127 if ((GetPixelChannelTraits(image,GreenPixelChannel) & UpdatePixelTrait) != 0)
2128 q->green=(MagickRealType) PerceptibleThreshold(ClampToQuantum(q->green),
2130 if ((GetPixelChannelTraits(image,BluePixelChannel) & UpdatePixelTrait) != 0)
2131 q->blue=(MagickRealType) PerceptibleThreshold(ClampToQuantum(q->blue),
2133 if ((GetPixelChannelTraits(image,AlphaPixelChannel) & UpdatePixelTrait) != 0)
2134 q->alpha=(MagickRealType) PerceptibleThreshold(ClampToQuantum(q->alpha),
2138 return(SyncImage(image,exception));
2145 image_view=AcquireAuthenticCacheView(image,exception);
2146#if defined(MAGICKCORE_OPENMP_SUPPORT)
2147 #pragma omp parallel for schedule(static) shared(progress,status) \
2148 magick_number_threads(image,image,image->rows,2)
2150 for (y=0; y < (ssize_t) image->rows; y++)
2158 if (status == MagickFalse)
2160 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2161 if (q == (Quantum *) NULL)
2166 for (x=0; x < (ssize_t) image->columns; x++)
2171 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2173 PixelChannel channel = GetPixelChannelChannel(image,i);
2174 PixelTrait traits = GetPixelChannelTraits(image,channel);
2175 if ((traits & UpdatePixelTrait) != 0)
2176 q[i]=PerceptibleThreshold(q[i],epsilon);
2178 q+=(ptrdiff_t) GetPixelChannels(image);
2180 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2182 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2187#if defined(MAGICKCORE_OPENMP_SUPPORT)
2191 proceed=SetImageProgress(image,PerceptibleImageTag,progress,
2193 if (proceed == MagickFalse)
2197 image_view=DestroyCacheView(image_view);
2231MagickExport MagickBooleanType RandomThresholdImage(
Image *image,
2232 const double min_threshold,
const double max_threshold,
ExceptionInfo *exception)
2234#define ThresholdImageTag "Threshold/Image"
2246 **magick_restrict random_info;
2251#if defined(MAGICKCORE_OPENMP_SUPPORT)
2256 assert(image != (
Image *) NULL);
2257 assert(image->signature == MagickCoreSignature);
2259 assert(exception->signature == MagickCoreSignature);
2260 if (IsEventLogging() != MagickFalse)
2261 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2262 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2263 return(MagickFalse);
2269 random_info=AcquireRandomInfoTLS();
2270 image_view=AcquireAuthenticCacheView(image,exception);
2271#if defined(MAGICKCORE_OPENMP_SUPPORT)
2272 key=GetRandomSecretKey(random_info[0]);
2273 #pragma omp parallel for schedule(static) shared(progress,status) \
2274 magick_number_threads(image,image,image->rows,key == ~0UL ? 0 : 1)
2276 for (y=0; y < (ssize_t) image->rows; y++)
2279 id = GetOpenMPThreadId();
2287 if (status == MagickFalse)
2289 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2290 if (q == (Quantum *) NULL)
2295 for (x=0; x < (ssize_t) image->columns; x++)
2300 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2305 PixelChannel channel = GetPixelChannelChannel(image,i);
2306 PixelTrait traits = GetPixelChannelTraits(image,channel);
2307 if ((traits & UpdatePixelTrait) == 0)
2309 if ((
double) q[i] < min_threshold)
2310 threshold=min_threshold;
2312 if ((
double) q[i] > max_threshold)
2313 threshold=max_threshold;
2315 threshold=((double) QuantumRange*
2316 GetPseudoRandomValue(random_info[
id]));
2317 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
2319 q+=(ptrdiff_t) GetPixelChannels(image);
2321 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2323 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2328#if defined(MAGICKCORE_OPENMP_SUPPORT)
2332 proceed=SetImageProgress(image,ThresholdImageTag,progress,
2334 if (proceed == MagickFalse)
2338 image_view=DestroyCacheView(image_view);
2339 random_info=DestroyRandomInfoTLS(random_info);
2377MagickExport MagickBooleanType RangeThresholdImage(
Image *image,
2378 const double low_black,
const double low_white,
const double high_white,
2381#define ThresholdImageTag "Threshold/Image"
2395 assert(image != (
Image *) NULL);
2396 assert(image->signature == MagickCoreSignature);
2397 if (IsEventLogging() != MagickFalse)
2398 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2399 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2400 return(MagickFalse);
2401 if (IsGrayColorspace(image->colorspace) != MagickFalse)
2402 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2408 image_view=AcquireAuthenticCacheView(image,exception);
2409#if defined(MAGICKCORE_OPENMP_SUPPORT)
2410 #pragma omp parallel for schedule(static) shared(progress,status) \
2411 magick_number_threads(image,image,image->rows,2)
2413 for (y=0; y < (ssize_t) image->rows; y++)
2421 if (status == MagickFalse)
2423 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2424 if (q == (Quantum *) NULL)
2429 for (x=0; x < (ssize_t) image->columns; x++)
2437 pixel=GetPixelIntensity(image,q);
2438 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2440 PixelChannel channel = GetPixelChannelChannel(image,i);
2441 PixelTrait traits = GetPixelChannelTraits(image,channel);
2442 if ((traits & UpdatePixelTrait) == 0)
2444 if (image->channel_mask != AllChannels)
2445 pixel=(double) q[i];
2446 if (pixel < low_black)
2449 if ((pixel >= low_black) && (pixel < low_white))
2450 q[i]=ClampToQuantum((
double) QuantumRange*
2451 PerceptibleReciprocal(low_white-low_black)*(pixel-low_black));
2453 if ((pixel >= low_white) && (pixel <= high_white))
2456 if ((pixel > high_white) && (pixel <= high_black))
2457 q[i]=ClampToQuantum((
double) QuantumRange*(
double)
2458 PerceptibleReciprocal(high_black-high_white)*
2459 (high_black-pixel));
2461 if (pixel > high_black)
2466 q+=(ptrdiff_t) GetPixelChannels(image);
2468 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2470 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2475#if defined(MAGICKCORE_OPENMP_SUPPORT)
2479 proceed=SetImageProgress(image,ThresholdImageTag,progress,
2481 if (proceed == MagickFalse)
2485 image_view=DestroyCacheView(image_view);
2518MagickExport MagickBooleanType WhiteThresholdImage(
Image *image,
2521#define ThresholdImageTag "Threshold/Image"
2544 assert(image != (
Image *) NULL);
2545 assert(image->signature == MagickCoreSignature);
2546 if (IsEventLogging() != MagickFalse)
2547 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2548 if (thresholds == (
const char *) NULL)
2550 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2551 return(MagickFalse);
2552 if (IsGrayColorspace(image->colorspace) != MagickFalse)
2553 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2554 GetPixelInfo(image,&threshold);
2555 flags=ParseGeometry(thresholds,&geometry_info);
2556 threshold.red=geometry_info.rho;
2557 threshold.green=geometry_info.rho;
2558 threshold.blue=geometry_info.rho;
2559 threshold.black=geometry_info.rho;
2560 threshold.alpha=100.0;
2561 if ((flags & SigmaValue) != 0)
2562 threshold.green=geometry_info.sigma;
2563 if ((flags & XiValue) != 0)
2564 threshold.blue=geometry_info.xi;
2565 if ((flags & PsiValue) != 0)
2566 threshold.alpha=geometry_info.psi;
2567 if (threshold.colorspace == CMYKColorspace)
2569 if ((flags & PsiValue) != 0)
2570 threshold.black=geometry_info.psi;
2571 if ((flags & ChiValue) != 0)
2572 threshold.alpha=geometry_info.chi;
2574 if ((flags & PercentValue) != 0)
2576 threshold.red*=((MagickRealType) QuantumRange/100.0);
2577 threshold.green*=((MagickRealType) QuantumRange/100.0);
2578 threshold.blue*=((MagickRealType) QuantumRange/100.0);
2579 threshold.black*=((MagickRealType) QuantumRange/100.0);
2580 threshold.alpha*=((MagickRealType) QuantumRange/100.0);
2587 image_view=AcquireAuthenticCacheView(image,exception);
2588#if defined(MAGICKCORE_OPENMP_SUPPORT)
2589 #pragma omp parallel for schedule(static) shared(progress,status) \
2590 magick_number_threads(image,image,image->rows,2)
2592 for (y=0; y < (ssize_t) image->rows; y++)
2600 if (status == MagickFalse)
2602 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2603 if (q == (Quantum *) NULL)
2608 for (x=0; x < (ssize_t) image->columns; x++)
2616 pixel=GetPixelIntensity(image,q);
2617 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2619 PixelChannel channel = GetPixelChannelChannel(image,i);
2620 PixelTrait traits = GetPixelChannelTraits(image,channel);
2621 if ((traits & UpdatePixelTrait) == 0)
2623 if (image->channel_mask != AllChannels)
2624 pixel=(double) q[i];
2625 if (pixel > GetPixelInfoChannel(&threshold,channel))
2628 q+=(ptrdiff_t) GetPixelChannels(image);
2630 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2632 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2637#if defined(MAGICKCORE_OPENMP_SUPPORT)
2641 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
2642 if (proceed == MagickFalse)
2646 image_view=DestroyCacheView(image_view);