43 #include "magick/studio.h"
44 #include "magick/artifact.h"
45 #include "magick/blob.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colormap.h"
50 #include "magick/colorspace.h"
51 #include "magick/colorspace-private.h"
52 #include "magick/configure.h"
53 #include "magick/constitute.h"
54 #include "magick/decorate.h"
55 #include "magick/draw.h"
56 #include "magick/enhance.h"
57 #include "magick/exception.h"
58 #include "magick/exception-private.h"
59 #include "magick/effect.h"
60 #include "magick/fx.h"
61 #include "magick/gem.h"
62 #include "magick/geometry.h"
63 #include "magick/image-private.h"
64 #include "magick/list.h"
65 #include "magick/log.h"
66 #include "magick/memory_.h"
67 #include "magick/monitor.h"
68 #include "magick/monitor-private.h"
69 #include "magick/montage.h"
70 #include "magick/option.h"
71 #include "magick/pixel-private.h"
72 #include "magick/property.h"
73 #include "magick/quantize.h"
74 #include "magick/quantum.h"
75 #include "magick/random_.h"
76 #include "magick/random-private.h"
77 #include "magick/resize.h"
78 #include "magick/resource_.h"
79 #include "magick/segment.h"
80 #include "magick/shear.h"
81 #include "magick/signature-private.h"
82 #include "magick/string_.h"
83 #include "magick/string-private.h"
84 #include "magick/thread-private.h"
85 #include "magick/threshold.h"
86 #include "magick/transform.h"
87 #include "magick/xml-tree.h"
92 #define ThresholdsFilename "thresholds.xml"
115 #if MAGICKCORE_ZERO_CONFIGURATION_SUPPORT
116 #include "magick/threshold-map.h"
120 "<?xml version=\"1.0\"?>"
122 " <threshold map=\"threshold\" alias=\"1x1\">"
123 " <description>Threshold 1x1 (non-dither)</description>"
124 " <levels width=\"1\" height=\"1\" divisor=\"2\">"
128 " <threshold map=\"checks\" alias=\"2x1\">"
129 " <description>Checkerboard 2x1 (dither)</description>"
130 " <levels width=\"2\" height=\"2\" divisor=\"3\">"
173 MagickExport
Image *AdaptiveThresholdImage(
const Image *image,
174 const size_t width,
const size_t height,
const ssize_t offset,
177 #define ThresholdImageTag "Threshold/Image"
201 assert(image != (
const Image *) NULL);
202 assert(image->signature == MagickCoreSignature);
203 if (IsEventLogging() != MagickFalse)
204 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
206 assert(exception->signature == MagickCoreSignature);
207 threshold_image=CloneImage(image,0,0,MagickTrue,exception);
208 if (threshold_image == (
Image *) NULL)
209 return((
Image *) NULL);
210 if ((width == 0) || (height == 0))
211 return(threshold_image);
212 if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
214 InheritException(exception,&threshold_image->exception);
215 threshold_image=DestroyImage(threshold_image);
216 return((
Image *) NULL);
223 GetMagickPixelPacket(image,&zero);
224 number_pixels=(MagickRealType) (width*height);
225 image_view=AcquireVirtualCacheView(image,exception);
226 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
227 #if defined(MAGICKCORE_OPENMP_SUPPORT)
228 #pragma omp parallel for schedule(static) shared(progress,status) \
229 magick_number_threads(image,threshold_image,image->rows,1)
231 for (y=0; y < (ssize_t) image->rows; y++)
241 *magick_restrict indexes;
248 *magick_restrict threshold_indexes;
260 if (status == MagickFalse)
262 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
263 height/2L,image->columns+width,height,exception);
264 q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
271 indexes=GetCacheViewVirtualIndexQueue(image_view);
272 threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
276 for (v=0; v < (ssize_t) height; v++)
278 for (u=0; u < (ssize_t) width; u++)
280 if (u == (ssize_t) (width-1))
282 channel_bias.red+=r[u].red;
283 channel_bias.green+=r[u].green;
284 channel_bias.blue+=r[u].blue;
285 channel_bias.opacity+=r[u].opacity;
286 if (image->colorspace == CMYKColorspace)
287 channel_bias.index=(MagickRealType)
288 GetPixelIndex(indexes+(r-p)+u);
290 channel_sum.red+=r[u].red;
291 channel_sum.green+=r[u].green;
292 channel_sum.blue+=r[u].blue;
293 channel_sum.opacity+=r[u].opacity;
294 if (image->colorspace == CMYKColorspace)
295 channel_sum.index=(MagickRealType) GetPixelIndex(indexes+(r-p)+u);
297 r+=image->columns+width;
299 for (x=0; x < (ssize_t) image->columns; x++)
306 channel_sum.red-=channel_bias.red;
307 channel_sum.green-=channel_bias.green;
308 channel_sum.blue-=channel_bias.blue;
309 channel_sum.opacity-=channel_bias.opacity;
310 channel_sum.index-=channel_bias.index;
312 for (v=0; v < (ssize_t) height; v++)
314 channel_bias.red+=r[0].red;
315 channel_bias.green+=r[0].green;
316 channel_bias.blue+=r[0].blue;
317 channel_bias.opacity+=r[0].opacity;
318 if (image->colorspace == CMYKColorspace)
319 channel_bias.index=(MagickRealType) GetPixelIndex(indexes+x+(r-p)+0);
320 channel_sum.red+=r[width-1].red;
321 channel_sum.green+=r[width-1].green;
322 channel_sum.blue+=r[width-1].blue;
323 channel_sum.opacity+=r[width-1].opacity;
324 if (image->colorspace == CMYKColorspace)
325 channel_sum.index=(MagickRealType) GetPixelIndex(indexes+x+(r-p)+
327 r+=image->columns+width;
329 mean.red=(MagickRealType) (channel_sum.red/number_pixels+offset);
330 mean.green=(MagickRealType) (channel_sum.green/number_pixels+offset);
331 mean.blue=(MagickRealType) (channel_sum.blue/number_pixels+offset);
332 mean.opacity=(MagickRealType) (channel_sum.opacity/number_pixels+offset);
333 if (image->colorspace == CMYKColorspace)
334 mean.index=(MagickRealType) (channel_sum.index/number_pixels+offset);
335 SetPixelRed(q,((MagickRealType) GetPixelRed(q) <= mean.red) ?
337 SetPixelGreen(q,((MagickRealType) GetPixelGreen(q) <= mean.green) ?
339 SetPixelBlue(q,((MagickRealType) GetPixelBlue(q) <= mean.blue) ?
341 SetPixelOpacity(q,((MagickRealType) GetPixelOpacity(q) <= mean.opacity) ?
343 if (image->colorspace == CMYKColorspace)
344 SetPixelIndex(threshold_indexes+x,(((MagickRealType) GetPixelIndex(
345 threshold_indexes+x) <= mean.index) ? 0 : QuantumRange));
349 sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
350 if (sync == MagickFalse)
352 if (image->progress_monitor != (MagickProgressMonitor) NULL)
357 #if defined(MAGICKCORE_OPENMP_SUPPORT)
361 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
362 if (proceed == MagickFalse)
366 threshold_view=DestroyCacheView(threshold_view);
367 image_view=DestroyCacheView(image_view);
368 if (status == MagickFalse)
369 threshold_image=DestroyImage(threshold_image);
370 return(threshold_image);
402 static double KapurThreshold(
const Image *image,
const double *histogram,
405 #define MaxIntensity 255
409 *cumulative_histogram,
425 cumulative_histogram=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
426 sizeof(*cumulative_histogram));
427 black_entropy=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
428 sizeof(*black_entropy));
429 white_entropy=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
430 sizeof(*white_entropy));
431 if ((cumulative_histogram == (
double *) NULL) ||
432 (black_entropy == (
double *) NULL) || (white_entropy == (
double *) NULL))
434 if (white_entropy != (
double *) NULL)
435 white_entropy=(
double *) RelinquishMagickMemory(white_entropy);
436 if (black_entropy != (
double *) NULL)
437 black_entropy=(
double *) RelinquishMagickMemory(black_entropy);
438 if (cumulative_histogram != (
double *) NULL)
439 cumulative_histogram=(
double *)
440 RelinquishMagickMemory(cumulative_histogram);
441 (void) ThrowMagickException(exception,GetMagickModule(),
442 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
448 cumulative_histogram[0]=histogram[0];
449 for (i=1; i <= MaxIntensity; i++)
450 cumulative_histogram[i]=cumulative_histogram[i-1]+histogram[i];
451 epsilon=MagickMinimumValue;
452 for (j=0; j <= MaxIntensity; j++)
457 black_entropy[j]=0.0;
458 if (cumulative_histogram[j] > epsilon)
461 for (i=0; i <= j; i++)
462 if (histogram[i] > epsilon)
463 entropy-=histogram[i]/cumulative_histogram[j]*
464 log(histogram[i]/cumulative_histogram[j]);
465 black_entropy[j]=entropy;
470 white_entropy[j]=0.0;
471 if ((1.0-cumulative_histogram[j]) > epsilon)
474 for (i=j+1; i <= MaxIntensity; i++)
475 if (histogram[i] > epsilon)
476 entropy-=histogram[i]/(1.0-cumulative_histogram[j])*
477 log(histogram[i]/(1.0-cumulative_histogram[j]));
478 white_entropy[j]=entropy;
484 maximum_entropy=black_entropy[0]+white_entropy[0];
486 for (j=1; j <= MaxIntensity; j++)
487 if ((black_entropy[j]+white_entropy[j]) > maximum_entropy)
489 maximum_entropy=black_entropy[j]+white_entropy[j];
490 threshold=(size_t) j;
495 white_entropy=(
double *) RelinquishMagickMemory(white_entropy);
496 black_entropy=(
double *) RelinquishMagickMemory(black_entropy);
497 cumulative_histogram=(
double *) RelinquishMagickMemory(cumulative_histogram);
498 return(100.0*threshold/MaxIntensity);
501 static double OTSUThreshold(
const Image *image,
const double *histogram,
518 myu=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
sizeof(*myu));
519 omega=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
sizeof(*omega));
520 probability=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
521 sizeof(*probability));
522 sigma=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
sizeof(*sigma));
523 if ((myu == (
double *) NULL) || (omega == (
double *) NULL) ||
524 (probability == (
double *) NULL) || (sigma == (
double *) NULL))
526 if (sigma != (
double *) NULL)
527 sigma=(
double *) RelinquishMagickMemory(sigma);
528 if (probability != (
double *) NULL)
529 probability=(
double *) RelinquishMagickMemory(probability);
530 if (omega != (
double *) NULL)
531 omega=(
double *) RelinquishMagickMemory(omega);
532 if (myu != (
double *) NULL)
533 myu=(
double *) RelinquishMagickMemory(myu);
534 (void) ThrowMagickException(exception,GetMagickModule(),
535 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
541 for (i=0; i <= (ssize_t) MaxIntensity; i++)
542 probability[i]=histogram[i];
546 omega[0]=probability[0];
548 for (i=1; i <= (ssize_t) MaxIntensity; i++)
550 omega[i]=omega[i-1]+probability[i];
551 myu[i]=myu[i-1]+i*probability[i];
558 for (i=0; i < (ssize_t) MaxIntensity; i++)
561 if ((omega[i] != 0.0) && (omega[i] != 1.0))
562 sigma[i]=pow(myu[MaxIntensity]*omega[i]-myu[i],2.0)/(omega[i]*(1.0-
564 if (sigma[i] > max_sigma)
567 threshold=(double) i;
573 myu=(
double *) RelinquishMagickMemory(myu);
574 omega=(
double *) RelinquishMagickMemory(omega);
575 probability=(
double *) RelinquishMagickMemory(probability);
576 sigma=(
double *) RelinquishMagickMemory(sigma);
577 return(100.0*threshold/MaxIntensity);
580 static double TriangleThreshold(
const double *histogram)
609 for (i=0; i <= (ssize_t) MaxIntensity; i++)
610 if (histogram[i] > 0.0)
616 for (i=(ssize_t) MaxIntensity; i >= 0; i--)
617 if (histogram[i] > 0.0)
624 for (i=0; i <= (ssize_t) MaxIntensity; i++)
625 if (histogram[i] > count)
636 if ((max-start) >= (end-max))
641 c=(-1.0)*(a*x1+b*y1);
642 inverse_ratio=1.0/sqrt(a*a+b*b+c*c);
645 if (x2 == (
double) start)
646 for (i=start; i < max; i++)
648 segment=inverse_ratio*(a*i+b*histogram[i]+c);
649 distance=sqrt(segment*segment);
650 if ((distance > max_distance) && (segment > 0.0))
653 max_distance=distance;
657 for (i=end; i > max; i--)
659 segment=inverse_ratio*(a*i+b*histogram[i]+c);
660 distance=sqrt(segment*segment);
661 if ((distance > max_distance) && (segment < 0.0))
664 max_distance=distance;
667 return(100.0*threshold/MaxIntensity);
670 MagickExport MagickBooleanType AutoThresholdImage(
Image *image,
677 property[MagickPathExtent];
700 assert(image != (
Image *) NULL);
701 assert(image->signature == MagickCoreSignature);
702 if (IsEventLogging() != MagickFalse)
703 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
704 histogram=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
706 if (histogram == (
double *) NULL)
707 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
710 (void) memset(histogram,0,(MaxIntensity+1UL)*
sizeof(*histogram));
711 image_view=AcquireVirtualCacheView(image,exception);
712 for (y=0; y < (ssize_t) image->rows; y++)
720 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
723 for (x=0; x < (ssize_t) image->columns; x++)
725 double intensity = GetPixelIntensity(image,p);
726 histogram[ScaleQuantumToChar(ClampToQuantum(intensity))]++;
730 image_view=DestroyCacheView(image_view);
735 for (i=0; i <= (ssize_t) MaxIntensity; i++)
737 gamma=PerceptibleReciprocal(sum);
738 for (i=0; i <= (ssize_t) MaxIntensity; i++)
739 histogram[i]=gamma*histogram[i];
745 case KapurThresholdMethod:
747 threshold=KapurThreshold(image,histogram,exception);
750 case OTSUThresholdMethod:
753 threshold=OTSUThreshold(image,histogram,exception);
756 case TriangleThresholdMethod:
758 threshold=TriangleThreshold(histogram);
762 histogram=(
double *) RelinquishMagickMemory(histogram);
765 if (status == MagickFalse)
770 (void) FormatLocaleString(property,MagickPathExtent,
"%g%%",threshold);
771 (void) SetImageProperty(image,
"auto-threshold:threshold",property);
772 artifact=GetImageArtifact(image,
"threshold:verbose");
773 if (IsStringTrue(artifact) != MagickFalse)
774 (void) FormatLocaleFile(stdout,
"%.*g%%\n",GetMagickPrecision(),threshold);
775 return(BilevelImage(image,QuantumRange*threshold/100.0));
821 MagickExport MagickBooleanType BilevelImage(
Image *image,
const double threshold)
826 status=BilevelImageChannel(image,DefaultChannels,threshold);
830 MagickExport MagickBooleanType BilevelImageChannel(
Image *image,
831 const ChannelType channel,
const double threshold)
833 #define ThresholdImageTag "Threshold/Image"
850 assert(image != (
Image *) NULL);
851 assert(image->signature == MagickCoreSignature);
852 if (IsEventLogging() != MagickFalse)
853 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
854 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
856 if (IsGrayColorspace(image->colorspace) == MagickFalse)
857 (
void) SetImageColorspace(image,sRGBColorspace);
863 exception=(&image->exception);
864 image_view=AcquireAuthenticCacheView(image,exception);
865 #if defined(MAGICKCORE_OPENMP_SUPPORT)
866 #pragma omp parallel for schedule(static) shared(progress,status) \
867 magick_number_threads(image,image,image->rows,1)
869 for (y=0; y < (ssize_t) image->rows; y++)
872 *magick_restrict indexes;
880 if (status == MagickFalse)
882 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
888 indexes=GetCacheViewAuthenticIndexQueue(image_view);
889 if ((channel & SyncChannels) != 0)
891 for (x=0; x < (ssize_t) image->columns; x++)
893 SetPixelRed(q,GetPixelIntensity(image,q) <= threshold ? 0 :
895 SetPixelGreen(q,GetPixelRed(q));
896 SetPixelBlue(q,GetPixelRed(q));
901 for (x=0; x < (ssize_t) image->columns; x++)
903 if ((channel & RedChannel) != 0)
904 SetPixelRed(q,(MagickRealType) GetPixelRed(q) <= threshold ? 0 :
906 if ((channel & GreenChannel) != 0)
907 SetPixelGreen(q,(MagickRealType) GetPixelGreen(q) <= threshold ? 0 :
909 if ((channel & BlueChannel) != 0)
910 SetPixelBlue(q,(MagickRealType) GetPixelBlue(q) <= threshold ? 0 :
912 if ((channel & OpacityChannel) != 0)
914 if (image->matte == MagickFalse)
915 SetPixelOpacity(q,(MagickRealType) GetPixelOpacity(q) <=
916 threshold ? 0 : QuantumRange);
918 SetPixelAlpha(q,(MagickRealType) GetPixelAlpha(q) <= threshold ?
919 OpaqueOpacity : TransparentOpacity);
921 if (((channel & IndexChannel) != 0) &&
922 (image->colorspace == CMYKColorspace))
923 SetPixelIndex(indexes+x,(MagickRealType) GetPixelIndex(indexes+x) <=
924 threshold ? 0 : QuantumRange);
927 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
929 if (image->progress_monitor != (MagickProgressMonitor) NULL)
934 #if defined(MAGICKCORE_OPENMP_SUPPORT)
938 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
939 if (proceed == MagickFalse)
943 image_view=DestroyCacheView(image_view);
980 MagickExport MagickBooleanType BlackThresholdImage(
Image *image,
981 const char *threshold)
986 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
991 MagickExport MagickBooleanType BlackThresholdImageChannel(
Image *image,
992 const ChannelType channel,
const char *thresholds,
ExceptionInfo *exception)
994 #define ThresholdImageTag "Threshold/Image"
1017 assert(image != (
Image *) NULL);
1018 assert(image->signature == MagickCoreSignature);
1019 if (IsEventLogging() != MagickFalse)
1020 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1021 if (thresholds == (
const char *) NULL)
1023 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1024 return(MagickFalse);
1025 GetMagickPixelPacket(image,&threshold);
1026 flags=ParseGeometry(thresholds,&geometry_info);
1027 threshold.red=geometry_info.rho;
1028 threshold.green=geometry_info.sigma;
1029 if ((flags & SigmaValue) == 0)
1030 threshold.green=threshold.red;
1031 threshold.blue=geometry_info.xi;
1032 if ((flags & XiValue) == 0)
1033 threshold.blue=threshold.red;
1034 threshold.opacity=geometry_info.psi;
1035 if ((flags & PsiValue) == 0)
1036 threshold.opacity=threshold.red;
1037 threshold.index=geometry_info.chi;
1038 if ((flags & ChiValue) == 0)
1039 threshold.index=threshold.red;
1040 if ((flags & PercentValue) != 0)
1042 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1043 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1044 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1045 threshold.opacity*=(MagickRealType) (QuantumRange/100.0);
1046 threshold.index*=(MagickRealType) (QuantumRange/100.0);
1048 if ((IsMagickGray(&threshold) == MagickFalse) &&
1049 (IsGrayColorspace(image->colorspace) != MagickFalse))
1050 (void) SetImageColorspace(image,sRGBColorspace);
1056 image_view=AcquireAuthenticCacheView(image,exception);
1057 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1058 #pragma omp parallel for schedule(static) shared(progress,status) \
1059 magick_number_threads(image,image,image->rows,1)
1061 for (y=0; y < (ssize_t) image->rows; y++)
1064 *magick_restrict indexes;
1072 if (status == MagickFalse)
1074 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1080 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1081 for (x=0; x < (ssize_t) image->columns; x++)
1083 if (((channel & RedChannel) != 0) &&
1084 ((MagickRealType) GetPixelRed(q) < threshold.red))
1086 if (((channel & GreenChannel) != 0) &&
1087 ((MagickRealType) GetPixelGreen(q) < threshold.green))
1089 if (((channel & BlueChannel) != 0) &&
1090 ((MagickRealType) GetPixelBlue(q) < threshold.blue))
1092 if (((channel & OpacityChannel) != 0) &&
1093 ((MagickRealType) GetPixelOpacity(q) < threshold.opacity))
1094 SetPixelOpacity(q,0);
1095 if (((channel & IndexChannel) != 0) &&
1096 (image->colorspace == CMYKColorspace) &&
1097 ((MagickRealType) GetPixelIndex(indexes+x) < threshold.index))
1098 SetPixelIndex(indexes+x,0);
1101 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1103 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1108 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1112 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
1113 if (proceed == MagickFalse)
1117 image_view=DestroyCacheView(image_view);
1150 MagickExport MagickBooleanType ClampImage(
Image *image)
1155 status=ClampImageChannel(image,DefaultChannels);
1159 MagickExport MagickBooleanType ClampImageChannel(
Image *image,
1160 const ChannelType channel)
1162 #define ClampImageTag "Clamp/Image"
1179 assert(image != (
Image *) NULL);
1180 assert(image->signature == MagickCoreSignature);
1181 if (IsEventLogging() != MagickFalse)
1182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1183 if (image->storage_class == PseudoClass)
1192 for (i=0; i < (ssize_t) image->colors; i++)
1194 SetPixelRed(q,ClampPixel((MagickRealType) GetPixelRed(q)));
1195 SetPixelGreen(q,ClampPixel((MagickRealType) GetPixelGreen(q)));
1196 SetPixelBlue(q,ClampPixel((MagickRealType) GetPixelBlue(q)));
1197 SetPixelOpacity(q,ClampPixel((MagickRealType) GetPixelOpacity(q)));
1200 return(SyncImage(image));
1207 exception=(&image->exception);
1208 image_view=AcquireAuthenticCacheView(image,exception);
1209 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1210 #pragma omp parallel for schedule(static) shared(progress,status) \
1211 magick_number_threads(image,image,image->rows,1)
1213 for (y=0; y < (ssize_t) image->rows; y++)
1216 *magick_restrict indexes;
1224 if (status == MagickFalse)
1226 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1232 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1233 for (x=0; x < (ssize_t) image->columns; x++)
1235 if ((channel & RedChannel) != 0)
1236 SetPixelRed(q,ClampPixel((MagickRealType) GetPixelRed(q)));
1237 if ((channel & GreenChannel) != 0)
1238 SetPixelGreen(q,ClampPixel((MagickRealType) GetPixelGreen(q)));
1239 if ((channel & BlueChannel) != 0)
1240 SetPixelBlue(q,ClampPixel((MagickRealType) GetPixelBlue(q)));
1241 if ((channel & OpacityChannel) != 0)
1242 SetPixelOpacity(q,ClampPixel((MagickRealType) GetPixelOpacity(q)));
1243 if (((channel & IndexChannel) != 0) &&
1244 (image->colorspace == CMYKColorspace))
1245 SetPixelIndex(indexes+x,ClampPixel((MagickRealType) GetPixelIndex(
1249 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1251 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1256 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1260 proceed=SetImageProgress(image,ClampImageTag,progress,image->rows);
1261 if (proceed == MagickFalse)
1265 image_view=DestroyCacheView(image_view);
1294 if (map->map_id != (
char *) NULL)
1295 map->map_id=DestroyString(map->map_id);
1296 if (map->description != (
char *) NULL)
1297 map->description=DestroyString(map->description);
1298 if (map->levels != (ssize_t *) NULL)
1299 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1334 MagickExport
ThresholdMap *GetThresholdMapFile(
const char *xml,
1335 const char *filename,
const char *map_id,
ExceptionInfo *exception)
1354 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1355 "Loading threshold map file \"%s\" ...",filename);
1356 thresholds=NewXMLTree(xml,exception);
1359 for (threshold = GetXMLTreeChild(thresholds,
"threshold");
1361 threshold = GetNextXMLTreeTag(threshold) )
1363 attribute=GetXMLTreeAttribute(threshold,
"map");
1364 if ((attribute != (
char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1366 attribute=GetXMLTreeAttribute(threshold,
"alias");
1367 if ((attribute != (
char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1372 thresholds=DestroyXMLTree(thresholds);
1375 description=GetXMLTreeChild(threshold,
"description");
1378 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1379 "XmlMissingElement",
"<description>, map \"%s\"", map_id);
1380 thresholds=DestroyXMLTree(thresholds);
1383 levels=GetXMLTreeChild(threshold,
"levels");
1386 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1387 "XmlMissingElement",
"<levels>, map \"%s\"", map_id);
1388 thresholds=DestroyXMLTree(thresholds);
1396 ThrowFatalException(ResourceLimitFatalError,
"UnableToAcquireThresholdMap");
1397 map->map_id=(
char *) NULL;
1398 map->description=(
char *) NULL;
1399 map->levels=(ssize_t *) NULL;
1403 attribute=GetXMLTreeAttribute(threshold,
"map");
1404 if (attribute != (
char *) NULL)
1405 map->map_id=ConstantString(attribute);
1406 content=GetXMLTreeContent(description);
1407 if (content != (
char *) NULL)
1408 map->description=ConstantString(content);
1409 attribute=GetXMLTreeAttribute(levels,
"width");
1410 if (attribute == (
char *) NULL)
1412 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1413 "XmlMissingAttribute",
"<levels width>, map \"%s\"",map_id);
1414 thresholds=DestroyXMLTree(thresholds);
1415 map=DestroyThresholdMap(map);
1418 map->width=StringToUnsignedLong(attribute);
1419 if (map->width == 0)
1421 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1422 "XmlInvalidAttribute",
"<levels width>, map \"%s\"", map_id);
1423 thresholds=DestroyXMLTree(thresholds);
1424 map=DestroyThresholdMap(map);
1427 attribute=GetXMLTreeAttribute(levels,
"height");
1428 if (attribute == (
char *) NULL)
1430 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1431 "XmlMissingAttribute",
"<levels height>, map \"%s\"", map_id);
1432 thresholds=DestroyXMLTree(thresholds);
1433 map=DestroyThresholdMap(map);
1436 map->height=StringToUnsignedLong(attribute);
1437 if (map->height == 0)
1439 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1440 "XmlInvalidAttribute",
"<levels height>, map \"%s\"", map_id);
1441 thresholds=DestroyXMLTree(thresholds);
1442 map=DestroyThresholdMap(map);
1445 attribute=GetXMLTreeAttribute(levels,
"divisor");
1446 if (attribute == (
char *) NULL)
1448 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1449 "XmlMissingAttribute",
"<levels divisor>, map \"%s\"", map_id);
1450 thresholds=DestroyXMLTree(thresholds);
1451 map=DestroyThresholdMap(map);
1454 map->divisor=(ssize_t) StringToLong(attribute);
1455 if (map->divisor < 2)
1457 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1458 "XmlInvalidAttribute",
"<levels divisor>, map \"%s\"", map_id);
1459 thresholds=DestroyXMLTree(thresholds);
1460 map=DestroyThresholdMap(map);
1466 content=GetXMLTreeContent(levels);
1467 if (content == (
char *) NULL)
1469 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1470 "XmlMissingContent",
"<levels>, map \"%s\"", map_id);
1471 thresholds=DestroyXMLTree(thresholds);
1472 map=DestroyThresholdMap(map);
1475 map->levels=(ssize_t *) AcquireQuantumMemory((
size_t) map->width,map->height*
1476 sizeof(*map->levels));
1477 if (map->levels == (ssize_t *) NULL)
1478 ThrowFatalException(ResourceLimitFatalError,
"UnableToAcquireThresholdMap");
1489 for (i=0; i< (ssize_t) (map->width*map->height); i++)
1491 map->levels[i]=(ssize_t) strtol(content,&p,10);
1494 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1495 "XmlInvalidContent",
"<level> too few values, map \"%s\"", map_id);
1496 thresholds=DestroyXMLTree(thresholds);
1497 map=DestroyThresholdMap(map);
1500 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1502 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1503 "XmlInvalidContent",
"<level> %.20g out of range, map \"%s\"",
1504 (double) map->levels[i],map_id);
1505 thresholds=DestroyXMLTree(thresholds);
1506 map=DestroyThresholdMap(map);
1511 value=(double) strtol(content,&p,10);
1515 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1516 "XmlInvalidContent",
"<level> too many values, map \"%s\"", map_id);
1517 thresholds=DestroyXMLTree(thresholds);
1518 map=DestroyThresholdMap(map);
1522 thresholds=DestroyXMLTree(thresholds);
1552 MagickExport
ThresholdMap *GetThresholdMap(
const char *map_id,
1564 map=GetThresholdMapFile(BuiltinMap,
"built-in",map_id,exception);
1567 options=GetConfigureOptions(ThresholdsFilename,exception);
1568 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1571 map=GetThresholdMapFile((
const char *) GetStringInfoDatum(option),
1572 GetStringInfoPath(option),map_id,exception);
1575 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1577 options=DestroyConfigureOptions(options);
1611 MagickBooleanType ListThresholdMapFile(FILE *file,
const char *xml,
1615 const char *map,*alias,*content;
1617 assert( xml != (
char *) NULL );
1618 assert( file != (FILE *) NULL );
1620 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1621 "Loading threshold map file \"%s\" ...",filename);
1622 thresholds=NewXMLTree(xml,exception);
1624 return(MagickFalse);
1626 (void) FormatLocaleFile(file,
"%-16s %-12s %s\n",
"Map",
"Alias",
"Description");
1627 (void) FormatLocaleFile(file,
1628 "----------------------------------------------------\n");
1630 for( threshold = GetXMLTreeChild(thresholds,
"threshold");
1632 threshold = GetNextXMLTreeTag(threshold) )
1634 map = GetXMLTreeAttribute(threshold,
"map");
1635 if (map == (
char *) NULL) {
1636 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1637 "XmlMissingAttribute",
"<map>");
1638 thresholds=DestroyXMLTree(thresholds);
1639 return(MagickFalse);
1641 alias = GetXMLTreeAttribute(threshold,
"alias");
1643 description=GetXMLTreeChild(threshold,
"description");
1645 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1646 "XmlMissingElement",
"<description>, map \"%s\"", map);
1647 thresholds=DestroyXMLTree(thresholds);
1648 return(MagickFalse);
1650 content=GetXMLTreeContent(description);
1651 if ( content == (
char *) NULL ) {
1652 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1653 "XmlMissingContent",
"<description>, map \"%s\"", map);
1654 thresholds=DestroyXMLTree(thresholds);
1655 return(MagickFalse);
1657 (void) FormatLocaleFile(file,
"%-16s %-12s %s\n",map,alias ? alias :
"",
1660 thresholds=DestroyXMLTree(thresholds);
1689 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1702 if (file == (FILE *) NULL)
1704 options=GetConfigureOptions(ThresholdsFilename,exception);
1705 (void) FormatLocaleFile(file,
1706 "\n Threshold Maps for Ordered Dither Operations\n");
1707 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1710 (void) FormatLocaleFile(file,
"\nPath: %s\n\n",GetStringInfoPath(option));
1711 status&=ListThresholdMapFile(file,(
const char *) GetStringInfoDatum(option),
1712 GetStringInfoPath(option),exception);
1713 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1715 options=DestroyConfigureOptions(options);
1716 return(status != 0 ? MagickTrue : MagickFalse);
1753 MagickExport MagickBooleanType OrderedDitherImage(
Image *image)
1758 status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
1762 MagickExport MagickBooleanType OrderedDitherImageChannel(
Image *image,
1771 status=OrderedPosterizeImageChannel(image,channel,
"o8x8",exception);
1827 MagickExport MagickBooleanType OrderedPosterizeImage(
Image *image,
1833 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1838 MagickExport MagickBooleanType OrderedPosterizeImageChannel(
Image *image,
1839 const ChannelType channel,
const char *threshold_map,
ExceptionInfo *exception)
1841 #define DitherImageTag "Dither/Image"
1861 assert(image != (
Image *) NULL);
1862 assert(image->signature == MagickCoreSignature);
1864 assert(exception->signature == MagickCoreSignature);
1865 if (IsEventLogging() != MagickFalse)
1866 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1867 if (threshold_map == (
const char *) NULL)
1871 token[MaxTextExtent];
1876 p=(
char *)threshold_map;
1877 while (((isspace((
int) ((
unsigned char) *p)) != 0) || (*p ==
',')) &&
1881 while (((isspace((
int) ((
unsigned char) *p)) == 0) && (*p !=
',')) &&
1883 if ((p-threshold_map) >= (MaxTextExtent-1))
1885 token[p-threshold_map] = *p;
1888 token[p-threshold_map] =
'\0';
1889 map = GetThresholdMap(token, exception);
1891 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1892 "InvalidArgument",
"%s : '%s'",
"ordered-dither",threshold_map);
1893 return(MagickFalse);
1903 p = strchr((
char *) threshold_map,
',');
1904 if ( p != (
char *) NULL && isdigit((
int) ((
unsigned char) *(++p))) )
1905 levels.index = (
unsigned int) strtoul(p, &p, 10);
1909 levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
1910 levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
1911 levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
1912 levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
1913 levels.index = ((channel & IndexChannel) != 0
1914 && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
1917 if ( p != (
char *) NULL && *p ==
',' ) {
1918 p=strchr((
char *) threshold_map,
',');
1920 if ((channel & RedChannel) != 0)
1921 levels.red = (
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1922 if ((channel & GreenChannel) != 0)
1923 levels.green = (
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1924 if ((channel & BlueChannel) != 0)
1925 levels.blue = (
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1926 if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
1927 levels.index=(
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1928 if ((channel & OpacityChannel) != 0)
1929 levels.opacity = (
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1946 printf(
"DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1947 levels.red, levels.green, levels.blue, levels.opacity, levels.index);
1958 levels.red = levels.red ? levels.red-1 : 0;
1959 levels.green = levels.green ? levels.green-1 : 0;
1960 levels.blue = levels.blue ? levels.blue-1 : 0;
1961 levels.opacity = levels.opacity ? levels.opacity-1 : 0;
1962 levels.index = levels.index ? levels.index-1 : 0;
1964 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1966 InheritException(exception,&image->exception);
1967 return(MagickFalse);
1971 image_view=AcquireAuthenticCacheView(image,exception);
1972 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1973 #pragma omp parallel for schedule(static) shared(progress,status) \
1974 magick_number_threads(image,image,image->rows,1)
1976 for (y=0; y < (ssize_t) image->rows; y++)
1979 *magick_restrict indexes;
1987 if (status == MagickFalse)
1989 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1995 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1996 for (x=0; x < (ssize_t) image->columns; x++)
2007 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
2021 t = (ssize_t) (QuantumScale*GetPixelRed(q)*(levels.red*d+1));
2023 SetPixelRed(q,ClampToQuantum((MagickRealType)
2024 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)));
2027 t = (ssize_t) (QuantumScale*GetPixelGreen(q)*
2028 (levels.green*d+1));
2030 SetPixelGreen(q,ClampToQuantum((MagickRealType)
2031 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)));
2034 t = (ssize_t) (QuantumScale*GetPixelBlue(q)*
2037 SetPixelBlue(q,ClampToQuantum((MagickRealType)
2038 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)));
2040 if (levels.opacity) {
2041 t = (ssize_t) ((1.0-QuantumScale*GetPixelOpacity(q))*
2042 (levels.opacity*d+1));
2044 SetPixelOpacity(q,ClampToQuantum((MagickRealType)
2045 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
2049 t = (ssize_t) (QuantumScale*GetPixelIndex(indexes+x)*
2050 (levels.index*d+1));
2052 SetPixelIndex(indexes+x,ClampToQuantum((MagickRealType) ((l+
2053 (t>=threshold))*(MagickRealType) QuantumRange/levels.index)));
2057 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2059 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2064 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2068 proceed=SetImageProgress(image,DitherImageTag,progress,image->rows);
2069 if (proceed == MagickFalse)
2073 image_view=DestroyCacheView(image_view);
2075 map=DestroyThresholdMap(map);
2110 static inline Quantum PerceptibleThreshold(
const Quantum quantum,
2111 const double epsilon)
2116 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
2117 if ((sign*quantum) >= epsilon)
2119 return((Quantum) (sign*epsilon));
2122 MagickExport MagickBooleanType PerceptibleImage(
Image *image,
2123 const double epsilon)
2128 status=PerceptibleImageChannel(image,DefaultChannels,epsilon);
2132 MagickExport MagickBooleanType PerceptibleImageChannel(
Image *image,
2133 const ChannelType channel,
const double epsilon)
2135 #define PerceptibleImageTag "Perceptible/Image"
2152 assert(image != (
Image *) NULL);
2153 assert(image->signature == MagickCoreSignature);
2154 if (IsEventLogging() != MagickFalse)
2155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2156 if (image->storage_class == PseudoClass)
2165 for (i=0; i < (ssize_t) image->colors; i++)
2167 SetPixelRed(q,PerceptibleThreshold(GetPixelRed(q),epsilon));
2168 SetPixelGreen(q,PerceptibleThreshold(GetPixelGreen(q),epsilon));
2169 SetPixelBlue(q,PerceptibleThreshold(GetPixelBlue(q),epsilon));
2170 SetPixelOpacity(q,PerceptibleThreshold(GetPixelOpacity(q),epsilon));
2173 return(SyncImage(image));
2180 exception=(&image->exception);
2181 image_view=AcquireAuthenticCacheView(image,exception);
2182 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2183 #pragma omp parallel for schedule(static) shared(progress,status) \
2184 magick_number_threads(image,image,image->rows,1)
2186 for (y=0; y < (ssize_t) image->rows; y++)
2189 *magick_restrict indexes;
2197 if (status == MagickFalse)
2199 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2205 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2206 for (x=0; x < (ssize_t) image->columns; x++)
2208 if ((channel & RedChannel) != 0)
2209 SetPixelRed(q,PerceptibleThreshold(GetPixelRed(q),epsilon));
2210 if ((channel & GreenChannel) != 0)
2211 SetPixelGreen(q,PerceptibleThreshold(GetPixelGreen(q),epsilon));
2212 if ((channel & BlueChannel) != 0)
2213 SetPixelBlue(q,PerceptibleThreshold(GetPixelBlue(q),epsilon));
2214 if ((channel & OpacityChannel) != 0)
2215 SetPixelOpacity(q,PerceptibleThreshold(GetPixelOpacity(q),epsilon));
2216 if (((channel & IndexChannel) != 0) &&
2217 (image->colorspace == CMYKColorspace))
2218 SetPixelIndex(indexes+x,PerceptibleThreshold(GetPixelIndex(indexes+x),
2222 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2224 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2229 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2233 proceed=SetImageProgress(image,PerceptibleImageTag,progress,
2235 if (proceed == MagickFalse)
2239 image_view=DestroyCacheView(image_view);
2280 MagickExport MagickBooleanType RandomThresholdImage(
Image *image,
2286 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
2291 MagickExport MagickBooleanType RandomThresholdImageChannel(
Image *image,
2292 const ChannelType channel,
const char *thresholds,
ExceptionInfo *exception)
2294 #define ThresholdImageTag "Threshold/Image"
2319 **magick_restrict random_info;
2324 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2329 assert(image != (
Image *) NULL);
2330 assert(image->signature == MagickCoreSignature);
2332 assert(exception->signature == MagickCoreSignature);
2333 if (IsEventLogging() != MagickFalse)
2334 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2335 if (thresholds == (
const char *) NULL)
2337 GetMagickPixelPacket(image,&threshold);
2339 max_threshold=(MagickRealType) QuantumRange;
2340 flags=ParseGeometry(thresholds,&geometry_info);
2341 min_threshold=geometry_info.rho;
2342 max_threshold=geometry_info.sigma;
2343 if ((flags & SigmaValue) == 0)
2344 max_threshold=min_threshold;
2345 if (strchr(thresholds,
'%') != (
char *) NULL)
2347 max_threshold*=(MagickRealType) (0.01*QuantumRange);
2348 min_threshold*=(MagickRealType) (0.01*QuantumRange);
2351 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
2352 (min_threshold <= 8))
2357 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
2365 if (channel == CompositeChannels)
2367 if (AcquireImageColormap(image,2) == MagickFalse)
2368 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2370 random_info=AcquireRandomInfoTLS();
2371 image_view=AcquireAuthenticCacheView(image,exception);
2372 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2373 key=GetRandomSecretKey(random_info[0]);
2374 #pragma omp parallel for schedule(static) shared(progress,status) \
2375 magick_number_threads(image,image,image->rows,key == ~0UL)
2377 for (y=0; y < (ssize_t) image->rows; y++)
2380 id = GetOpenMPThreadId();
2386 *magick_restrict indexes;
2394 if (status == MagickFalse)
2396 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2403 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2404 for (x=0; x < (ssize_t) image->columns; x++)
2412 intensity=GetPixelIntensity(image,q);
2413 if (intensity < min_threshold)
2414 threshold.index=min_threshold;
2415 else if (intensity > max_threshold)
2416 threshold.index=max_threshold;
2418 threshold.index=(MagickRealType)(QuantumRange*
2419 GetPseudoRandomValue(random_info[
id]));
2420 index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
2421 SetPixelIndex(indexes+x,index);
2422 SetPixelRGBO(q,image->colormap+(ssize_t) index);
2425 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2426 if (sync == MagickFalse)
2428 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2433 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2437 proceed=SetImageProgress(image,ThresholdImageTag,progress,
2439 if (proceed == MagickFalse)
2443 image_view=DestroyCacheView(image_view);
2444 random_info=DestroyRandomInfoTLS(random_info);
2447 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2449 InheritException(exception,&image->exception);
2450 return(MagickFalse);
2452 random_info=AcquireRandomInfoTLS();
2453 image_view=AcquireAuthenticCacheView(image,exception);
2454 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2455 key=GetRandomSecretKey(random_info[0]);
2456 #pragma omp parallel for schedule(static) shared(progress,status) \
2457 magick_number_threads(image,image,image->rows,key == ~0UL)
2459 for (y=0; y < (ssize_t) image->rows; y++)
2462 id = GetOpenMPThreadId();
2465 *magick_restrict indexes;
2473 if (status == MagickFalse)
2475 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2481 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2482 for (x=0; x < (ssize_t) image->columns; x++)
2484 if ((channel & RedChannel) != 0)
2486 if ((MagickRealType) GetPixelRed(q) < min_threshold)
2487 threshold.red=min_threshold;
2489 if ((MagickRealType) GetPixelRed(q) > max_threshold)
2490 threshold.red=max_threshold;
2492 threshold.red=(MagickRealType) (QuantumRange*
2493 GetPseudoRandomValue(random_info[
id]));
2495 if ((channel & GreenChannel) != 0)
2497 if ((MagickRealType) GetPixelGreen(q) < min_threshold)
2498 threshold.green=min_threshold;
2500 if ((MagickRealType) GetPixelGreen(q) > max_threshold)
2501 threshold.green=max_threshold;
2503 threshold.green=(MagickRealType) (QuantumRange*
2504 GetPseudoRandomValue(random_info[
id]));
2506 if ((channel & BlueChannel) != 0)
2508 if ((MagickRealType) GetPixelBlue(q) < min_threshold)
2509 threshold.blue=min_threshold;
2511 if ((MagickRealType) GetPixelBlue(q) > max_threshold)
2512 threshold.blue=max_threshold;
2514 threshold.blue=(MagickRealType) (QuantumRange*
2515 GetPseudoRandomValue(random_info[
id]));
2517 if ((channel & OpacityChannel) != 0)
2519 if ((MagickRealType) GetPixelOpacity(q) < min_threshold)
2520 threshold.opacity=min_threshold;
2522 if ((MagickRealType) GetPixelOpacity(q) > max_threshold)
2523 threshold.opacity=max_threshold;
2525 threshold.opacity=(MagickRealType) (QuantumRange*
2526 GetPseudoRandomValue(random_info[
id]));
2528 if (((channel & IndexChannel) != 0) &&
2529 (image->colorspace == CMYKColorspace))
2531 if ((MagickRealType) GetPixelIndex(indexes+x) < min_threshold)
2532 threshold.index=min_threshold;
2534 if ((MagickRealType) GetPixelIndex(indexes+x) > max_threshold)
2535 threshold.index=max_threshold;
2537 threshold.index=(MagickRealType) (QuantumRange*
2538 GetPseudoRandomValue(random_info[
id]));
2540 if ((channel & RedChannel) != 0)
2541 SetPixelRed(q,(MagickRealType) GetPixelRed(q) <= threshold.red ?
2543 if ((channel & GreenChannel) != 0)
2544 SetPixelGreen(q,(MagickRealType) GetPixelGreen(q) <= threshold.green ?
2546 if ((channel & BlueChannel) != 0)
2547 SetPixelBlue(q,(MagickRealType) GetPixelBlue(q) <= threshold.blue ?
2549 if ((channel & OpacityChannel) != 0)
2550 SetPixelOpacity(q,(MagickRealType) GetPixelOpacity(q) <=
2551 threshold.opacity ? 0 : QuantumRange);
2552 if (((channel & IndexChannel) != 0) &&
2553 (image->colorspace == CMYKColorspace))
2554 SetPixelIndex(indexes+x,(MagickRealType) GetPixelIndex(indexes+x) <=
2555 threshold.index ? 0 : QuantumRange);
2558 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2560 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2565 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2569 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
2570 if (proceed == MagickFalse)
2574 image_view=DestroyCacheView(image_view);
2575 random_info=DestroyRandomInfoTLS(random_info);
2612 MagickExport MagickBooleanType WhiteThresholdImage(
Image *image,
2613 const char *threshold)
2618 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
2623 MagickExport MagickBooleanType WhiteThresholdImageChannel(
Image *image,
2624 const ChannelType channel,
const char *thresholds,
ExceptionInfo *exception)
2626 #define ThresholdImageTag "Threshold/Image"
2649 assert(image != (
Image *) NULL);
2650 assert(image->signature == MagickCoreSignature);
2651 if (IsEventLogging() != MagickFalse)
2652 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2653 if (thresholds == (
const char *) NULL)
2655 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2656 return(MagickFalse);
2657 flags=ParseGeometry(thresholds,&geometry_info);
2658 GetMagickPixelPacket(image,&threshold);
2659 threshold.red=geometry_info.rho;
2660 threshold.green=geometry_info.sigma;
2661 if ((flags & SigmaValue) == 0)
2662 threshold.green=threshold.red;
2663 threshold.blue=geometry_info.xi;
2664 if ((flags & XiValue) == 0)
2665 threshold.blue=threshold.red;
2666 threshold.opacity=geometry_info.psi;
2667 if ((flags & PsiValue) == 0)
2668 threshold.opacity=threshold.red;
2669 threshold.index=geometry_info.chi;
2670 if ((flags & ChiValue) == 0)
2671 threshold.index=threshold.red;
2672 if ((flags & PercentValue) != 0)
2674 threshold.red*=(MagickRealType) (QuantumRange/100.0);
2675 threshold.green*=(MagickRealType) (QuantumRange/100.0);
2676 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
2677 threshold.opacity*=(MagickRealType) (QuantumRange/100.0);
2678 threshold.index*=(MagickRealType) (QuantumRange/100.0);
2680 if ((IsMagickGray(&threshold) == MagickFalse) &&
2681 (IsGrayColorspace(image->colorspace) != MagickFalse))
2682 (void) SetImageColorspace(image,sRGBColorspace);
2688 image_view=AcquireAuthenticCacheView(image,exception);
2689 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2690 #pragma omp parallel for schedule(static) shared(progress,status) \
2691 magick_number_threads(image,image,image->rows,1)
2693 for (y=0; y < (ssize_t) image->rows; y++)
2696 *magick_restrict indexes;
2704 if (status == MagickFalse)
2706 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2712 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2713 for (x=0; x < (ssize_t) image->columns; x++)
2715 if (((channel & RedChannel) != 0) &&
2716 ((MagickRealType) GetPixelRed(q) > threshold.red))
2717 SetPixelRed(q,QuantumRange);
2718 if (((channel & GreenChannel) != 0) &&
2719 ((MagickRealType) GetPixelGreen(q) > threshold.green))
2720 SetPixelGreen(q,QuantumRange);
2721 if (((channel & BlueChannel) != 0) &&
2722 ((MagickRealType) GetPixelBlue(q) > threshold.blue))
2723 SetPixelBlue(q,QuantumRange);
2724 if (((channel & OpacityChannel) != 0) &&
2725 ((MagickRealType) GetPixelOpacity(q) > threshold.opacity))
2726 SetPixelOpacity(q,QuantumRange);
2727 if (((channel & IndexChannel) != 0) &&
2728 (image->colorspace == CMYKColorspace) &&
2729 ((MagickRealType) GetPixelIndex(indexes+x)) > threshold.index)
2730 SetPixelIndex(indexes+x,QuantumRange);
2733 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2735 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2740 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2744 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
2745 if (proceed == MagickFalse)
2749 image_view=DestroyCacheView(image_view);