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"
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\">"
173MagickExport
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+=(MagickRealType) r[u].red;
283 channel_bias.green+=(MagickRealType) r[u].green;
284 channel_bias.blue+=(MagickRealType) r[u].blue;
285 channel_bias.opacity+=(MagickRealType) r[u].opacity;
286 if (image->colorspace == CMYKColorspace)
287 channel_bias.index=(MagickRealType)
288 GetPixelIndex(indexes+(r-p)+u);
290 channel_sum.red+=(MagickRealType) r[u].red;
291 channel_sum.green+=(MagickRealType) r[u].green;
292 channel_sum.blue+=(MagickRealType) r[u].blue;
293 channel_sum.opacity+=(MagickRealType) r[u].opacity;
294 if (image->colorspace == CMYKColorspace)
295 channel_sum.index=(MagickRealType) GetPixelIndex(indexes+(r-p)+u);
297 r+=(ptrdiff_t) 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+=(MagickRealType) r[0].red;
315 channel_bias.green+=(MagickRealType) r[0].green;
316 channel_bias.blue+=(MagickRealType) r[0].blue;
317 channel_bias.opacity+=(MagickRealType) r[0].opacity;
318 if (image->colorspace == CMYKColorspace)
319 channel_bias.index=(MagickRealType) GetPixelIndex(indexes+x+(r-p)+0);
320 channel_sum.red+=(MagickRealType) r[width-1].red;
321 channel_sum.green+=(MagickRealType) r[width-1].green;
322 channel_sum.blue+=(MagickRealType) r[width-1].blue;
323 channel_sum.opacity+=(MagickRealType) r[width-1].opacity;
324 if (image->colorspace == CMYKColorspace)
325 channel_sum.index=(MagickRealType) GetPixelIndex(indexes+x+(r-p)+
327 r+=(ptrdiff_t) 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);
402static 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);
501static 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);
580static 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);
670MagickExport 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,(MagickRealType) QuantumRange*threshold/100.0));
821MagickExport MagickBooleanType BilevelImage(
Image *image,
const double threshold)
826 status=BilevelImageChannel(image,DefaultChannels,threshold);
830MagickExport 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,2)
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);
980MagickExport MagickBooleanType BlackThresholdImage(
Image *image,
981 const char *threshold)
986 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
991MagickExport 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);
1150MagickExport MagickBooleanType ClampImage(
Image *image)
1155 status=ClampImageChannel(image,DefaultChannels);
1159MagickExport 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,2)
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);
1334MagickExport
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);
1552MagickExport
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);
1611MagickBooleanType 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);
1689MagickExport 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);
1753MagickExport MagickBooleanType OrderedDitherImage(
Image *image)
1758 status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
1762MagickExport MagickBooleanType OrderedDitherImageChannel(
Image *image,
1771 status=OrderedPosterizeImageChannel(image,channel,
"o8x8",exception);
1827MagickExport MagickBooleanType OrderedPosterizeImage(
Image *image,
1833 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1838MagickExport 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++);
1946printf(
"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*(MagickRealType) GetPixelRed(q)*
2024 SetPixelRed(q,ClampToQuantum((MagickRealType)
2025 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)));
2028 t = (ssize_t) (QuantumScale*(MagickRealType) 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*(MagickRealType) 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*(MagickRealType) 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*(MagickRealType) 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);
2111static inline Quantum PerceptibleThreshold(
const Quantum quantum,
2112 const double epsilon)
2117 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
2118 if ((sign*(
double) quantum) >= epsilon)
2120 return((Quantum) (sign*epsilon));
2123MagickExport MagickBooleanType PerceptibleImage(
Image *image,
2124 const double epsilon)
2129 status=PerceptibleImageChannel(image,DefaultChannels,epsilon);
2133MagickExport 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);
2281MagickExport MagickBooleanType RandomThresholdImage(
Image *image,
2287 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
2292MagickExport 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*=0.01*(MagickRealType) QuantumRange;
2349 min_threshold*=0.01*(MagickRealType) 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 (MagickRealType) 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) ((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) ((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) ((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) ((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) ((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);
2613MagickExport MagickBooleanType WhiteThresholdImage(
Image *image,
2614 const char *threshold)
2619 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
2624MagickExport 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,2)
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);