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 Image *image,
const double *histogram)
608 magick_unreferenced(image);
610 for (i=0; i <= (ssize_t) MaxIntensity; i++)
611 if (histogram[i] > 0.0)
617 for (i=(ssize_t) MaxIntensity; i >= 0; i--)
618 if (histogram[i] > 0.0)
625 for (i=0; i <= (ssize_t) MaxIntensity; i++)
626 if (histogram[i] > count)
637 if ((max-start) >= (end-max))
642 c=(-1.0)*(a*x1+b*y1);
643 inverse_ratio=1.0/sqrt(a*a+b*b+c*c);
646 if (x2 == (
double) start)
647 for (i=start; 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;
658 for (i=end; i > max; i--)
660 segment=inverse_ratio*(a*i+b*histogram[i]+c);
661 distance=sqrt(segment*segment);
662 if ((distance > max_distance) && (segment < 0.0))
665 max_distance=distance;
668 return(100.0*threshold/MaxIntensity);
671 MagickExport MagickBooleanType AutoThresholdImage(
Image *image,
678 property[MagickPathExtent];
701 assert(image != (
Image *) NULL);
702 assert(image->signature == MagickCoreSignature);
703 if (IsEventLogging() != MagickFalse)
704 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
705 histogram=(
double *) AcquireQuantumMemory(MaxIntensity+1UL,
707 if (histogram == (
double *) NULL)
708 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
711 (void) memset(histogram,0,(MaxIntensity+1UL)*
sizeof(*histogram));
712 image_view=AcquireVirtualCacheView(image,exception);
713 for (y=0; y < (ssize_t) image->rows; y++)
721 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
724 for (x=0; x < (ssize_t) image->columns; x++)
726 double intensity = GetPixelIntensity(image,p);
727 histogram[ScaleQuantumToChar(ClampToQuantum(intensity))]++;
731 image_view=DestroyCacheView(image_view);
736 for (i=0; i <= (ssize_t) MaxIntensity; i++)
738 gamma=PerceptibleReciprocal(sum);
739 for (i=0; i <= (ssize_t) MaxIntensity; i++)
740 histogram[i]=gamma*histogram[i];
746 case KapurThresholdMethod:
748 threshold=KapurThreshold(image,histogram,exception);
751 case OTSUThresholdMethod:
754 threshold=OTSUThreshold(image,histogram,exception);
757 case TriangleThresholdMethod:
759 threshold=TriangleThreshold(image,histogram);
763 histogram=(
double *) RelinquishMagickMemory(histogram);
766 if (status == MagickFalse)
771 (void) FormatLocaleString(property,MagickPathExtent,
"%g%%",threshold);
772 (void) SetImageProperty(image,
"auto-threshold:threshold",property);
773 artifact=GetImageArtifact(image,
"threshold:verbose");
774 if (IsStringTrue(artifact) != MagickFalse)
775 (void) FormatLocaleFile(stdout,
"%.*g%%\n",GetMagickPrecision(),threshold);
776 return(BilevelImage(image,QuantumRange*threshold/100.0));
822 MagickExport MagickBooleanType BilevelImage(
Image *image,
const double threshold)
827 status=BilevelImageChannel(image,DefaultChannels,threshold);
831 MagickExport MagickBooleanType BilevelImageChannel(
Image *image,
832 const ChannelType channel,
const double threshold)
834 #define ThresholdImageTag "Threshold/Image"
851 assert(image != (
Image *) NULL);
852 assert(image->signature == MagickCoreSignature);
853 if (IsEventLogging() != MagickFalse)
854 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
855 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
857 if (IsGrayColorspace(image->colorspace) == MagickFalse)
858 (
void) SetImageColorspace(image,sRGBColorspace);
864 exception=(&image->exception);
865 image_view=AcquireAuthenticCacheView(image,exception);
866 #if defined(MAGICKCORE_OPENMP_SUPPORT)
867 #pragma omp parallel for schedule(static) shared(progress,status) \
868 magick_number_threads(image,image,image->rows,1)
870 for (y=0; y < (ssize_t) image->rows; y++)
873 *magick_restrict indexes;
881 if (status == MagickFalse)
883 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
889 indexes=GetCacheViewAuthenticIndexQueue(image_view);
890 if ((channel & SyncChannels) != 0)
892 for (x=0; x < (ssize_t) image->columns; x++)
894 SetPixelRed(q,GetPixelIntensity(image,q) <= threshold ? 0 :
896 SetPixelGreen(q,GetPixelRed(q));
897 SetPixelBlue(q,GetPixelRed(q));
902 for (x=0; x < (ssize_t) image->columns; x++)
904 if ((channel & RedChannel) != 0)
905 SetPixelRed(q,(MagickRealType) GetPixelRed(q) <= threshold ? 0 :
907 if ((channel & GreenChannel) != 0)
908 SetPixelGreen(q,(MagickRealType) GetPixelGreen(q) <= threshold ? 0 :
910 if ((channel & BlueChannel) != 0)
911 SetPixelBlue(q,(MagickRealType) GetPixelBlue(q) <= threshold ? 0 :
913 if ((channel & OpacityChannel) != 0)
915 if (image->matte == MagickFalse)
916 SetPixelOpacity(q,(MagickRealType) GetPixelOpacity(q) <=
917 threshold ? 0 : QuantumRange);
919 SetPixelAlpha(q,(MagickRealType) GetPixelAlpha(q) <= threshold ?
920 OpaqueOpacity : TransparentOpacity);
922 if (((channel & IndexChannel) != 0) &&
923 (image->colorspace == CMYKColorspace))
924 SetPixelIndex(indexes+x,(MagickRealType) GetPixelIndex(indexes+x) <=
925 threshold ? 0 : QuantumRange);
928 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
930 if (image->progress_monitor != (MagickProgressMonitor) NULL)
935 #if defined(MAGICKCORE_OPENMP_SUPPORT)
939 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
940 if (proceed == MagickFalse)
944 image_view=DestroyCacheView(image_view);
981 MagickExport MagickBooleanType BlackThresholdImage(
Image *image,
982 const char *threshold)
987 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
992 MagickExport MagickBooleanType BlackThresholdImageChannel(
Image *image,
993 const ChannelType channel,
const char *thresholds,
ExceptionInfo *exception)
995 #define ThresholdImageTag "Threshold/Image"
1018 assert(image != (
Image *) NULL);
1019 assert(image->signature == MagickCoreSignature);
1020 if (IsEventLogging() != MagickFalse)
1021 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1022 if (thresholds == (
const char *) NULL)
1024 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1025 return(MagickFalse);
1026 GetMagickPixelPacket(image,&threshold);
1027 flags=ParseGeometry(thresholds,&geometry_info);
1028 threshold.red=geometry_info.rho;
1029 threshold.green=geometry_info.sigma;
1030 if ((flags & SigmaValue) == 0)
1031 threshold.green=threshold.red;
1032 threshold.blue=geometry_info.xi;
1033 if ((flags & XiValue) == 0)
1034 threshold.blue=threshold.red;
1035 threshold.opacity=geometry_info.psi;
1036 if ((flags & PsiValue) == 0)
1037 threshold.opacity=threshold.red;
1038 threshold.index=geometry_info.chi;
1039 if ((flags & ChiValue) == 0)
1040 threshold.index=threshold.red;
1041 if ((flags & PercentValue) != 0)
1043 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1044 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1045 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1046 threshold.opacity*=(MagickRealType) (QuantumRange/100.0);
1047 threshold.index*=(MagickRealType) (QuantumRange/100.0);
1049 if ((IsMagickGray(&threshold) == MagickFalse) &&
1050 (IsGrayColorspace(image->colorspace) != MagickFalse))
1051 (void) SetImageColorspace(image,sRGBColorspace);
1057 image_view=AcquireAuthenticCacheView(image,exception);
1058 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1059 #pragma omp parallel for schedule(static) shared(progress,status) \
1060 magick_number_threads(image,image,image->rows,1)
1062 for (y=0; y < (ssize_t) image->rows; y++)
1065 *magick_restrict indexes;
1073 if (status == MagickFalse)
1075 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1081 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1082 for (x=0; x < (ssize_t) image->columns; x++)
1084 if (((channel & RedChannel) != 0) &&
1085 ((MagickRealType) GetPixelRed(q) < threshold.red))
1087 if (((channel & GreenChannel) != 0) &&
1088 ((MagickRealType) GetPixelGreen(q) < threshold.green))
1090 if (((channel & BlueChannel) != 0) &&
1091 ((MagickRealType) GetPixelBlue(q) < threshold.blue))
1093 if (((channel & OpacityChannel) != 0) &&
1094 ((MagickRealType) GetPixelOpacity(q) < threshold.opacity))
1095 SetPixelOpacity(q,0);
1096 if (((channel & IndexChannel) != 0) &&
1097 (image->colorspace == CMYKColorspace) &&
1098 ((MagickRealType) GetPixelIndex(indexes+x) < threshold.index))
1099 SetPixelIndex(indexes+x,0);
1102 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1104 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1109 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1113 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
1114 if (proceed == MagickFalse)
1118 image_view=DestroyCacheView(image_view);
1151 MagickExport MagickBooleanType ClampImage(
Image *image)
1156 status=ClampImageChannel(image,DefaultChannels);
1160 MagickExport MagickBooleanType ClampImageChannel(
Image *image,
1161 const ChannelType channel)
1163 #define ClampImageTag "Clamp/Image"
1180 assert(image != (
Image *) NULL);
1181 assert(image->signature == MagickCoreSignature);
1182 if (IsEventLogging() != MagickFalse)
1183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1184 if (image->storage_class == PseudoClass)
1193 for (i=0; i < (ssize_t) image->colors; i++)
1195 SetPixelRed(q,ClampPixel((MagickRealType) GetPixelRed(q)));
1196 SetPixelGreen(q,ClampPixel((MagickRealType) GetPixelGreen(q)));
1197 SetPixelBlue(q,ClampPixel((MagickRealType) GetPixelBlue(q)));
1198 SetPixelOpacity(q,ClampPixel((MagickRealType) GetPixelOpacity(q)));
1201 return(SyncImage(image));
1208 exception=(&image->exception);
1209 image_view=AcquireAuthenticCacheView(image,exception);
1210 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1211 #pragma omp parallel for schedule(static) shared(progress,status) \
1212 magick_number_threads(image,image,image->rows,1)
1214 for (y=0; y < (ssize_t) image->rows; y++)
1217 *magick_restrict indexes;
1225 if (status == MagickFalse)
1227 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1233 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1234 for (x=0; x < (ssize_t) image->columns; x++)
1236 if ((channel & RedChannel) != 0)
1237 SetPixelRed(q,ClampPixel((MagickRealType) GetPixelRed(q)));
1238 if ((channel & GreenChannel) != 0)
1239 SetPixelGreen(q,ClampPixel((MagickRealType) GetPixelGreen(q)));
1240 if ((channel & BlueChannel) != 0)
1241 SetPixelBlue(q,ClampPixel((MagickRealType) GetPixelBlue(q)));
1242 if ((channel & OpacityChannel) != 0)
1243 SetPixelOpacity(q,ClampPixel((MagickRealType) GetPixelOpacity(q)));
1244 if (((channel & IndexChannel) != 0) &&
1245 (image->colorspace == CMYKColorspace))
1246 SetPixelIndex(indexes+x,ClampPixel((MagickRealType) GetPixelIndex(
1250 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1252 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1257 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1261 proceed=SetImageProgress(image,ClampImageTag,progress,image->rows);
1262 if (proceed == MagickFalse)
1266 image_view=DestroyCacheView(image_view);
1295 if (map->map_id != (
char *) NULL)
1296 map->map_id=DestroyString(map->map_id);
1297 if (map->description != (
char *) NULL)
1298 map->description=DestroyString(map->description);
1299 if (map->levels != (ssize_t *) NULL)
1300 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1335 MagickExport
ThresholdMap *GetThresholdMapFile(
const char *xml,
1336 const char *filename,
const char *map_id,
ExceptionInfo *exception)
1355 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1356 "Loading threshold map file \"%s\" ...",filename);
1357 thresholds=NewXMLTree(xml,exception);
1360 for (threshold = GetXMLTreeChild(thresholds,
"threshold");
1362 threshold = GetNextXMLTreeTag(threshold) )
1364 attribute=GetXMLTreeAttribute(threshold,
"map");
1365 if ((attribute != (
char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1367 attribute=GetXMLTreeAttribute(threshold,
"alias");
1368 if ((attribute != (
char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1373 thresholds=DestroyXMLTree(thresholds);
1376 description=GetXMLTreeChild(threshold,
"description");
1379 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1380 "XmlMissingElement",
"<description>, map \"%s\"", map_id);
1381 thresholds=DestroyXMLTree(thresholds);
1384 levels=GetXMLTreeChild(threshold,
"levels");
1387 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1388 "XmlMissingElement",
"<levels>, map \"%s\"", map_id);
1389 thresholds=DestroyXMLTree(thresholds);
1397 ThrowFatalException(ResourceLimitFatalError,
"UnableToAcquireThresholdMap");
1398 map->map_id=(
char *) NULL;
1399 map->description=(
char *) NULL;
1400 map->levels=(ssize_t *) NULL;
1404 attribute=GetXMLTreeAttribute(threshold,
"map");
1405 if (attribute != (
char *) NULL)
1406 map->map_id=ConstantString(attribute);
1407 content=GetXMLTreeContent(description);
1408 if (content != (
char *) NULL)
1409 map->description=ConstantString(content);
1410 attribute=GetXMLTreeAttribute(levels,
"width");
1411 if (attribute == (
char *) NULL)
1413 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1414 "XmlMissingAttribute",
"<levels width>, map \"%s\"",map_id);
1415 thresholds=DestroyXMLTree(thresholds);
1416 map=DestroyThresholdMap(map);
1419 map->width=StringToUnsignedLong(attribute);
1420 if (map->width == 0)
1422 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1423 "XmlInvalidAttribute",
"<levels width>, map \"%s\"", map_id);
1424 thresholds=DestroyXMLTree(thresholds);
1425 map=DestroyThresholdMap(map);
1428 attribute=GetXMLTreeAttribute(levels,
"height");
1429 if (attribute == (
char *) NULL)
1431 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1432 "XmlMissingAttribute",
"<levels height>, map \"%s\"", map_id);
1433 thresholds=DestroyXMLTree(thresholds);
1434 map=DestroyThresholdMap(map);
1437 map->height=StringToUnsignedLong(attribute);
1438 if (map->height == 0)
1440 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1441 "XmlInvalidAttribute",
"<levels height>, map \"%s\"", map_id);
1442 thresholds=DestroyXMLTree(thresholds);
1443 map=DestroyThresholdMap(map);
1446 attribute=GetXMLTreeAttribute(levels,
"divisor");
1447 if (attribute == (
char *) NULL)
1449 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1450 "XmlMissingAttribute",
"<levels divisor>, map \"%s\"", map_id);
1451 thresholds=DestroyXMLTree(thresholds);
1452 map=DestroyThresholdMap(map);
1455 map->divisor=(ssize_t) StringToLong(attribute);
1456 if (map->divisor < 2)
1458 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1459 "XmlInvalidAttribute",
"<levels divisor>, map \"%s\"", map_id);
1460 thresholds=DestroyXMLTree(thresholds);
1461 map=DestroyThresholdMap(map);
1467 content=GetXMLTreeContent(levels);
1468 if (content == (
char *) NULL)
1470 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1471 "XmlMissingContent",
"<levels>, map \"%s\"", map_id);
1472 thresholds=DestroyXMLTree(thresholds);
1473 map=DestroyThresholdMap(map);
1476 map->levels=(ssize_t *) AcquireQuantumMemory((
size_t) map->width,map->height*
1477 sizeof(*map->levels));
1478 if (map->levels == (ssize_t *) NULL)
1479 ThrowFatalException(ResourceLimitFatalError,
"UnableToAcquireThresholdMap");
1490 for (i=0; i< (ssize_t) (map->width*map->height); i++)
1492 map->levels[i]=(ssize_t) strtol(content,&p,10);
1495 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1496 "XmlInvalidContent",
"<level> too few values, map \"%s\"", map_id);
1497 thresholds=DestroyXMLTree(thresholds);
1498 map=DestroyThresholdMap(map);
1501 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1503 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1504 "XmlInvalidContent",
"<level> %.20g out of range, map \"%s\"",
1505 (double) map->levels[i],map_id);
1506 thresholds=DestroyXMLTree(thresholds);
1507 map=DestroyThresholdMap(map);
1512 value=(double) strtol(content,&p,10);
1516 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1517 "XmlInvalidContent",
"<level> too many values, map \"%s\"", map_id);
1518 thresholds=DestroyXMLTree(thresholds);
1519 map=DestroyThresholdMap(map);
1523 thresholds=DestroyXMLTree(thresholds);
1553 MagickExport
ThresholdMap *GetThresholdMap(
const char *map_id,
1565 map=GetThresholdMapFile(BuiltinMap,
"built-in",map_id,exception);
1568 options=GetConfigureOptions(ThresholdsFilename,exception);
1569 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1572 map=GetThresholdMapFile((
const char *) GetStringInfoDatum(option),
1573 GetStringInfoPath(option),map_id,exception);
1576 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1578 options=DestroyConfigureOptions(options);
1612 MagickBooleanType ListThresholdMapFile(FILE *file,
const char *xml,
1616 const char *map,*alias,*content;
1618 assert( xml != (
char *) NULL );
1619 assert( file != (FILE *) NULL );
1621 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1622 "Loading threshold map file \"%s\" ...",filename);
1623 thresholds=NewXMLTree(xml,exception);
1625 return(MagickFalse);
1627 (void) FormatLocaleFile(file,
"%-16s %-12s %s\n",
"Map",
"Alias",
"Description");
1628 (void) FormatLocaleFile(file,
1629 "----------------------------------------------------\n");
1631 for( threshold = GetXMLTreeChild(thresholds,
"threshold");
1633 threshold = GetNextXMLTreeTag(threshold) )
1635 map = GetXMLTreeAttribute(threshold,
"map");
1636 if (map == (
char *) NULL) {
1637 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1638 "XmlMissingAttribute",
"<map>");
1639 thresholds=DestroyXMLTree(thresholds);
1640 return(MagickFalse);
1642 alias = GetXMLTreeAttribute(threshold,
"alias");
1644 description=GetXMLTreeChild(threshold,
"description");
1646 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1647 "XmlMissingElement",
"<description>, map \"%s\"", map);
1648 thresholds=DestroyXMLTree(thresholds);
1649 return(MagickFalse);
1651 content=GetXMLTreeContent(description);
1652 if ( content == (
char *) NULL ) {
1653 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1654 "XmlMissingContent",
"<description>, map \"%s\"", map);
1655 thresholds=DestroyXMLTree(thresholds);
1656 return(MagickFalse);
1658 (void) FormatLocaleFile(file,
"%-16s %-12s %s\n",map,alias ? alias :
"",
1661 thresholds=DestroyXMLTree(thresholds);
1690 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1703 if (file == (FILE *) NULL)
1705 options=GetConfigureOptions(ThresholdsFilename,exception);
1706 (void) FormatLocaleFile(file,
1707 "\n Threshold Maps for Ordered Dither Operations\n");
1708 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1711 (void) FormatLocaleFile(file,
"\nPath: %s\n\n",GetStringInfoPath(option));
1712 status&=ListThresholdMapFile(file,(
const char *) GetStringInfoDatum(option),
1713 GetStringInfoPath(option),exception);
1714 option=(
const StringInfo *) GetNextValueInLinkedList(options);
1716 options=DestroyConfigureOptions(options);
1717 return(status != 0 ? MagickTrue : MagickFalse);
1754 MagickExport MagickBooleanType OrderedDitherImage(
Image *image)
1759 status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
1763 MagickExport MagickBooleanType OrderedDitherImageChannel(
Image *image,
1772 status=OrderedPosterizeImageChannel(image,channel,
"o8x8",exception);
1828 MagickExport MagickBooleanType OrderedPosterizeImage(
Image *image,
1834 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1839 MagickExport MagickBooleanType OrderedPosterizeImageChannel(
Image *image,
1840 const ChannelType channel,
const char *threshold_map,
ExceptionInfo *exception)
1842 #define DitherImageTag "Dither/Image"
1862 assert(image != (
Image *) NULL);
1863 assert(image->signature == MagickCoreSignature);
1865 assert(exception->signature == MagickCoreSignature);
1866 if (IsEventLogging() != MagickFalse)
1867 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1868 if (threshold_map == (
const char *) NULL)
1872 token[MaxTextExtent];
1877 p=(
char *)threshold_map;
1878 while (((isspace((
int) ((
unsigned char) *p)) != 0) || (*p ==
',')) &&
1882 while (((isspace((
int) ((
unsigned char) *p)) == 0) && (*p !=
',')) &&
1884 if ((p-threshold_map) >= (MaxTextExtent-1))
1886 token[p-threshold_map] = *p;
1889 token[p-threshold_map] =
'\0';
1890 map = GetThresholdMap(token, exception);
1892 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1893 "InvalidArgument",
"%s : '%s'",
"ordered-dither",threshold_map);
1894 return(MagickFalse);
1904 p = strchr((
char *) threshold_map,
',');
1905 if ( p != (
char *) NULL && isdigit((
int) ((
unsigned char) *(++p))) )
1906 levels.index = (
unsigned int) strtoul(p, &p, 10);
1910 levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
1911 levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
1912 levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
1913 levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
1914 levels.index = ((channel & IndexChannel) != 0
1915 && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
1918 if ( p != (
char *) NULL && *p ==
',' ) {
1919 p=strchr((
char *) threshold_map,
',');
1921 if ((channel & RedChannel) != 0)
1922 levels.red = (
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1923 if ((channel & GreenChannel) != 0)
1924 levels.green = (
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1925 if ((channel & BlueChannel) != 0)
1926 levels.blue = (
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1927 if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
1928 levels.index=(
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1929 if ((channel & OpacityChannel) != 0)
1930 levels.opacity = (
unsigned int) strtoul(p, &p, 10), (void)(*p ==
',' && p++);
1947 printf(
"DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1948 levels.red, levels.green, levels.blue, levels.opacity, levels.index);
1959 levels.red = levels.red ? levels.red-1 : 0;
1960 levels.green = levels.green ? levels.green-1 : 0;
1961 levels.blue = levels.blue ? levels.blue-1 : 0;
1962 levels.opacity = levels.opacity ? levels.opacity-1 : 0;
1963 levels.index = levels.index ? levels.index-1 : 0;
1965 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1967 InheritException(exception,&image->exception);
1968 return(MagickFalse);
1972 image_view=AcquireAuthenticCacheView(image,exception);
1973 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1974 #pragma omp parallel for schedule(static) shared(progress,status) \
1975 magick_number_threads(image,image,image->rows,1)
1977 for (y=0; y < (ssize_t) image->rows; y++)
1980 *magick_restrict indexes;
1988 if (status == MagickFalse)
1990 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1996 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1997 for (x=0; x < (ssize_t) image->columns; x++)
2008 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
2022 t = (ssize_t) (QuantumScale*GetPixelRed(q)*(levels.red*d+1));
2024 SetPixelRed(q,ClampToQuantum((MagickRealType)
2025 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)));
2028 t = (ssize_t) (QuantumScale*GetPixelGreen(q)*
2029 (levels.green*d+1));
2031 SetPixelGreen(q,ClampToQuantum((MagickRealType)
2032 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)));
2035 t = (ssize_t) (QuantumScale*GetPixelBlue(q)*
2038 SetPixelBlue(q,ClampToQuantum((MagickRealType)
2039 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)));
2041 if (levels.opacity) {
2042 t = (ssize_t) ((1.0-QuantumScale*GetPixelOpacity(q))*
2043 (levels.opacity*d+1));
2045 SetPixelOpacity(q,ClampToQuantum((MagickRealType)
2046 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
2050 t = (ssize_t) (QuantumScale*GetPixelIndex(indexes+x)*
2051 (levels.index*d+1));
2053 SetPixelIndex(indexes+x,ClampToQuantum((MagickRealType) ((l+
2054 (t>=threshold))*(MagickRealType) QuantumRange/levels.index)));
2058 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2060 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2065 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2069 proceed=SetImageProgress(image,DitherImageTag,progress,image->rows);
2070 if (proceed == MagickFalse)
2074 image_view=DestroyCacheView(image_view);
2076 map=DestroyThresholdMap(map);
2111 static inline Quantum PerceptibleThreshold(
const Quantum quantum,
2112 const double epsilon)
2117 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
2118 if ((sign*quantum) >= epsilon)
2120 return((Quantum) (sign*epsilon));
2123 MagickExport MagickBooleanType PerceptibleImage(
Image *image,
2124 const double epsilon)
2129 status=PerceptibleImageChannel(image,DefaultChannels,epsilon);
2133 MagickExport MagickBooleanType PerceptibleImageChannel(
Image *image,
2134 const ChannelType channel,
const double epsilon)
2136 #define PerceptibleImageTag "Perceptible/Image"
2153 assert(image != (
Image *) NULL);
2154 assert(image->signature == MagickCoreSignature);
2155 if (IsEventLogging() != MagickFalse)
2156 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2157 if (image->storage_class == PseudoClass)
2166 for (i=0; i < (ssize_t) image->colors; i++)
2168 SetPixelRed(q,PerceptibleThreshold(GetPixelRed(q),epsilon));
2169 SetPixelGreen(q,PerceptibleThreshold(GetPixelGreen(q),epsilon));
2170 SetPixelBlue(q,PerceptibleThreshold(GetPixelBlue(q),epsilon));
2171 SetPixelOpacity(q,PerceptibleThreshold(GetPixelOpacity(q),epsilon));
2174 return(SyncImage(image));
2181 exception=(&image->exception);
2182 image_view=AcquireAuthenticCacheView(image,exception);
2183 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2184 #pragma omp parallel for schedule(static) shared(progress,status) \
2185 magick_number_threads(image,image,image->rows,1)
2187 for (y=0; y < (ssize_t) image->rows; y++)
2190 *magick_restrict indexes;
2198 if (status == MagickFalse)
2200 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2206 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2207 for (x=0; x < (ssize_t) image->columns; x++)
2209 if ((channel & RedChannel) != 0)
2210 SetPixelRed(q,PerceptibleThreshold(GetPixelRed(q),epsilon));
2211 if ((channel & GreenChannel) != 0)
2212 SetPixelGreen(q,PerceptibleThreshold(GetPixelGreen(q),epsilon));
2213 if ((channel & BlueChannel) != 0)
2214 SetPixelBlue(q,PerceptibleThreshold(GetPixelBlue(q),epsilon));
2215 if ((channel & OpacityChannel) != 0)
2216 SetPixelOpacity(q,PerceptibleThreshold(GetPixelOpacity(q),epsilon));
2217 if (((channel & IndexChannel) != 0) &&
2218 (image->colorspace == CMYKColorspace))
2219 SetPixelIndex(indexes+x,PerceptibleThreshold(GetPixelIndex(indexes+x),
2223 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2225 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2230 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2234 proceed=SetImageProgress(image,PerceptibleImageTag,progress,
2236 if (proceed == MagickFalse)
2240 image_view=DestroyCacheView(image_view);
2281 MagickExport MagickBooleanType RandomThresholdImage(
Image *image,
2287 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
2292 MagickExport MagickBooleanType RandomThresholdImageChannel(
Image *image,
2293 const ChannelType channel,
const char *thresholds,
ExceptionInfo *exception)
2295 #define ThresholdImageTag "Threshold/Image"
2320 **magick_restrict random_info;
2325 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2330 assert(image != (
Image *) NULL);
2331 assert(image->signature == MagickCoreSignature);
2333 assert(exception->signature == MagickCoreSignature);
2334 if (IsEventLogging() != MagickFalse)
2335 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2336 if (thresholds == (
const char *) NULL)
2338 GetMagickPixelPacket(image,&threshold);
2340 max_threshold=(MagickRealType) QuantumRange;
2341 flags=ParseGeometry(thresholds,&geometry_info);
2342 min_threshold=geometry_info.rho;
2343 max_threshold=geometry_info.sigma;
2344 if ((flags & SigmaValue) == 0)
2345 max_threshold=min_threshold;
2346 if (strchr(thresholds,
'%') != (
char *) NULL)
2348 max_threshold*=(MagickRealType) (0.01*QuantumRange);
2349 min_threshold*=(MagickRealType) (0.01*QuantumRange);
2352 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
2353 (min_threshold <= 8))
2358 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
2366 if (channel == CompositeChannels)
2368 if (AcquireImageColormap(image,2) == MagickFalse)
2369 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2371 random_info=AcquireRandomInfoTLS();
2372 image_view=AcquireAuthenticCacheView(image,exception);
2373 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2374 key=GetRandomSecretKey(random_info[0]);
2375 #pragma omp parallel for schedule(static) shared(progress,status) \
2376 magick_number_threads(image,image,image->rows,key == ~0UL)
2378 for (y=0; y < (ssize_t) image->rows; y++)
2381 id = GetOpenMPThreadId();
2387 *magick_restrict indexes;
2395 if (status == MagickFalse)
2397 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2404 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2405 for (x=0; x < (ssize_t) image->columns; x++)
2413 intensity=GetPixelIntensity(image,q);
2414 if (intensity < min_threshold)
2415 threshold.index=min_threshold;
2416 else if (intensity > max_threshold)
2417 threshold.index=max_threshold;
2419 threshold.index=(MagickRealType)(QuantumRange*
2420 GetPseudoRandomValue(random_info[
id]));
2421 index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
2422 SetPixelIndex(indexes+x,index);
2423 SetPixelRGBO(q,image->colormap+(ssize_t) index);
2426 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2427 if (sync == MagickFalse)
2429 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2434 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2438 proceed=SetImageProgress(image,ThresholdImageTag,progress,
2440 if (proceed == MagickFalse)
2444 image_view=DestroyCacheView(image_view);
2445 random_info=DestroyRandomInfoTLS(random_info);
2448 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2450 InheritException(exception,&image->exception);
2451 return(MagickFalse);
2453 random_info=AcquireRandomInfoTLS();
2454 image_view=AcquireAuthenticCacheView(image,exception);
2455 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2456 key=GetRandomSecretKey(random_info[0]);
2457 #pragma omp parallel for schedule(static) shared(progress,status) \
2458 magick_number_threads(image,image,image->rows,key == ~0UL)
2460 for (y=0; y < (ssize_t) image->rows; y++)
2463 id = GetOpenMPThreadId();
2466 *magick_restrict indexes;
2474 if (status == MagickFalse)
2476 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2482 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2483 for (x=0; x < (ssize_t) image->columns; x++)
2485 if ((channel & RedChannel) != 0)
2487 if ((MagickRealType) GetPixelRed(q) < min_threshold)
2488 threshold.red=min_threshold;
2490 if ((MagickRealType) GetPixelRed(q) > max_threshold)
2491 threshold.red=max_threshold;
2493 threshold.red=(MagickRealType) (QuantumRange*
2494 GetPseudoRandomValue(random_info[
id]));
2496 if ((channel & GreenChannel) != 0)
2498 if ((MagickRealType) GetPixelGreen(q) < min_threshold)
2499 threshold.green=min_threshold;
2501 if ((MagickRealType) GetPixelGreen(q) > max_threshold)
2502 threshold.green=max_threshold;
2504 threshold.green=(MagickRealType) (QuantumRange*
2505 GetPseudoRandomValue(random_info[
id]));
2507 if ((channel & BlueChannel) != 0)
2509 if ((MagickRealType) GetPixelBlue(q) < min_threshold)
2510 threshold.blue=min_threshold;
2512 if ((MagickRealType) GetPixelBlue(q) > max_threshold)
2513 threshold.blue=max_threshold;
2515 threshold.blue=(MagickRealType) (QuantumRange*
2516 GetPseudoRandomValue(random_info[
id]));
2518 if ((channel & OpacityChannel) != 0)
2520 if ((MagickRealType) GetPixelOpacity(q) < min_threshold)
2521 threshold.opacity=min_threshold;
2523 if ((MagickRealType) GetPixelOpacity(q) > max_threshold)
2524 threshold.opacity=max_threshold;
2526 threshold.opacity=(MagickRealType) (QuantumRange*
2527 GetPseudoRandomValue(random_info[
id]));
2529 if (((channel & IndexChannel) != 0) &&
2530 (image->colorspace == CMYKColorspace))
2532 if ((MagickRealType) GetPixelIndex(indexes+x) < min_threshold)
2533 threshold.index=min_threshold;
2535 if ((MagickRealType) GetPixelIndex(indexes+x) > max_threshold)
2536 threshold.index=max_threshold;
2538 threshold.index=(MagickRealType) (QuantumRange*
2539 GetPseudoRandomValue(random_info[
id]));
2541 if ((channel & RedChannel) != 0)
2542 SetPixelRed(q,(MagickRealType) GetPixelRed(q) <= threshold.red ?
2544 if ((channel & GreenChannel) != 0)
2545 SetPixelGreen(q,(MagickRealType) GetPixelGreen(q) <= threshold.green ?
2547 if ((channel & BlueChannel) != 0)
2548 SetPixelBlue(q,(MagickRealType) GetPixelBlue(q) <= threshold.blue ?
2550 if ((channel & OpacityChannel) != 0)
2551 SetPixelOpacity(q,(MagickRealType) GetPixelOpacity(q) <=
2552 threshold.opacity ? 0 : QuantumRange);
2553 if (((channel & IndexChannel) != 0) &&
2554 (image->colorspace == CMYKColorspace))
2555 SetPixelIndex(indexes+x,(MagickRealType) GetPixelIndex(indexes+x) <=
2556 threshold.index ? 0 : QuantumRange);
2559 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2561 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2566 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2570 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
2571 if (proceed == MagickFalse)
2575 image_view=DestroyCacheView(image_view);
2576 random_info=DestroyRandomInfoTLS(random_info);
2613 MagickExport MagickBooleanType WhiteThresholdImage(
Image *image,
2614 const char *threshold)
2619 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
2624 MagickExport MagickBooleanType WhiteThresholdImageChannel(
Image *image,
2625 const ChannelType channel,
const char *thresholds,
ExceptionInfo *exception)
2627 #define ThresholdImageTag "Threshold/Image"
2650 assert(image != (
Image *) NULL);
2651 assert(image->signature == MagickCoreSignature);
2652 if (IsEventLogging() != MagickFalse)
2653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2654 if (thresholds == (
const char *) NULL)
2656 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2657 return(MagickFalse);
2658 flags=ParseGeometry(thresholds,&geometry_info);
2659 GetMagickPixelPacket(image,&threshold);
2660 threshold.red=geometry_info.rho;
2661 threshold.green=geometry_info.sigma;
2662 if ((flags & SigmaValue) == 0)
2663 threshold.green=threshold.red;
2664 threshold.blue=geometry_info.xi;
2665 if ((flags & XiValue) == 0)
2666 threshold.blue=threshold.red;
2667 threshold.opacity=geometry_info.psi;
2668 if ((flags & PsiValue) == 0)
2669 threshold.opacity=threshold.red;
2670 threshold.index=geometry_info.chi;
2671 if ((flags & ChiValue) == 0)
2672 threshold.index=threshold.red;
2673 if ((flags & PercentValue) != 0)
2675 threshold.red*=(MagickRealType) (QuantumRange/100.0);
2676 threshold.green*=(MagickRealType) (QuantumRange/100.0);
2677 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
2678 threshold.opacity*=(MagickRealType) (QuantumRange/100.0);
2679 threshold.index*=(MagickRealType) (QuantumRange/100.0);
2681 if ((IsMagickGray(&threshold) == MagickFalse) &&
2682 (IsGrayColorspace(image->colorspace) != MagickFalse))
2683 (void) SetImageColorspace(image,sRGBColorspace);
2689 image_view=AcquireAuthenticCacheView(image,exception);
2690 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2691 #pragma omp parallel for schedule(static) shared(progress,status) \
2692 magick_number_threads(image,image,image->rows,1)
2694 for (y=0; y < (ssize_t) image->rows; y++)
2697 *magick_restrict indexes;
2705 if (status == MagickFalse)
2707 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2713 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2714 for (x=0; x < (ssize_t) image->columns; x++)
2716 if (((channel & RedChannel) != 0) &&
2717 ((MagickRealType) GetPixelRed(q) > threshold.red))
2718 SetPixelRed(q,QuantumRange);
2719 if (((channel & GreenChannel) != 0) &&
2720 ((MagickRealType) GetPixelGreen(q) > threshold.green))
2721 SetPixelGreen(q,QuantumRange);
2722 if (((channel & BlueChannel) != 0) &&
2723 ((MagickRealType) GetPixelBlue(q) > threshold.blue))
2724 SetPixelBlue(q,QuantumRange);
2725 if (((channel & OpacityChannel) != 0) &&
2726 ((MagickRealType) GetPixelOpacity(q) > threshold.opacity))
2727 SetPixelOpacity(q,QuantumRange);
2728 if (((channel & IndexChannel) != 0) &&
2729 (image->colorspace == CMYKColorspace) &&
2730 ((MagickRealType) GetPixelIndex(indexes+x)) > threshold.index)
2731 SetPixelIndex(indexes+x,QuantumRange);
2734 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2736 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2741 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2745 proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
2746 if (proceed == MagickFalse)
2750 image_view=DestroyCacheView(image_view);