2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % TTTTT H H RRRR EEEEE SSSSS H H OOO L DDDD %
7 % T H H R R E SS H H O O L D D %
8 % T HHHHH RRRR EEE SSS HHHHH O O L D D %
9 % T H H R R E SS H H O O L D D %
10 % T H H R R EEEEE SSSSS H H OOO LLLLL DDDD %
13 % MagickCore Image Threshold Methods %
20 % Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % https://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/property.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/configure.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/gem.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/memory-private.h"
68 #include "MagickCore/monitor.h"
69 #include "MagickCore/monitor-private.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/pixel-accessor.h"
73 #include "MagickCore/pixel-private.h"
74 #include "MagickCore/quantize.h"
75 #include "MagickCore/quantum.h"
76 #include "MagickCore/quantum-private.h"
77 #include "MagickCore/random_.h"
78 #include "MagickCore/random-private.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/segment.h"
82 #include "MagickCore/shear.h"
83 #include "MagickCore/signature-private.h"
84 #include "MagickCore/string_.h"
85 #include "MagickCore/string-private.h"
86 #include "MagickCore/thread-private.h"
87 #include "MagickCore/threshold.h"
88 #include "MagickCore/token.h"
89 #include "MagickCore/transform.h"
90 #include "MagickCore/xml-tree.h"
91 #include "MagickCore/xml-tree-private.h"
96 #define ThresholdsFilename "thresholds.xml"
120 *MinimalThresholdMap =
121 "<?xml version=\"1.0\"?>"
123 " <threshold map=\"threshold\" alias=\"1x1\">"
124 " <description>Threshold 1x1 (non-dither)</description>"
125 " <levels width=\"1\" height=\"1\" divisor=\"2\">"
129 " <threshold map=\"checks\" alias=\"2x1\">"
130 " <description>Checkerboard 2x1 (dither)</description>"
131 " <levels width=\"2\" height=\"2\" divisor=\"3\">"
139 Forward declarations.
142 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149 % A d a p t i v e T h r e s h o l d I m a g e %
153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155 % AdaptiveThresholdImage() selects an individual threshold for each pixel
156 % based on the range of intensity values in its local neighborhood. This
157 % allows for thresholding of an image whose global intensity histogram
158 % doesn't contain distinctive peaks.
160 % The format of the AdaptiveThresholdImage method is:
162 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
163 % const size_t height,const double bias,ExceptionInfo *exception)
165 % A description of each parameter follows:
167 % o image: the image.
169 % o width: the width of the local neighborhood.
171 % o height: the height of the local neighborhood.
173 % o bias: the mean bias.
175 % o exception: return any errors or warnings in this structure.
178 MagickExport Image *AdaptiveThresholdImage(const Image *image,
179 const size_t width,const size_t height,const double bias,
180 ExceptionInfo *exception)
182 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
204 Initialize threshold image attributes.
206 assert(image != (Image *) NULL);
207 assert(image->signature == MagickCoreSignature);
208 if (image->debug != MagickFalse)
209 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
210 assert(exception != (ExceptionInfo *) NULL);
211 assert(exception->signature == MagickCoreSignature);
212 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
214 if (threshold_image == (Image *) NULL)
215 return((Image *) NULL);
216 status=SetImageStorageClass(threshold_image,DirectClass,exception);
217 if (status == MagickFalse)
219 threshold_image=DestroyImage(threshold_image);
220 return((Image *) NULL);
227 number_pixels=(MagickSizeType) width*height;
228 image_view=AcquireVirtualCacheView(image,exception);
229 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
230 #if defined(MAGICKCORE_OPENMP_SUPPORT)
231 #pragma omp parallel for schedule(static,4) shared(progress,status) \
232 magick_number_threads(image,threshold_image,image->rows,1)
234 for (y=0; y < (ssize_t) image->rows; y++)
237 channel_bias[MaxPixelChannels],
238 channel_sum[MaxPixelChannels];
240 register const Quantum
242 *magick_restrict pixels;
256 if (status == MagickFalse)
258 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
259 (height/2L),image->columns+width,height,exception);
260 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
262 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
267 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
268 GetPixelChannels(image)*(width/2);
269 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
271 PixelChannel channel = GetPixelChannelChannel(image,i);
272 PixelTrait traits = GetPixelChannelTraits(image,channel);
273 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
275 if ((traits == UndefinedPixelTrait) ||
276 (threshold_traits == UndefinedPixelTrait))
278 if (((threshold_traits & CopyPixelTrait) != 0) ||
279 (GetPixelWriteMask(image,p) <= (QuantumRange/2)))
281 SetPixelChannel(threshold_image,channel,p[center+i],q);
285 channel_bias[channel]=0.0;
286 channel_sum[channel]=0.0;
287 for (v=0; v < (ssize_t) height; v++)
289 for (u=0; u < (ssize_t) width; u++)
291 if (u == (ssize_t) (width-1))
292 channel_bias[channel]+=pixels[i];
293 channel_sum[channel]+=pixels[i];
294 pixels+=GetPixelChannels(image);
296 pixels+=GetPixelChannels(image)*image->columns;
299 for (x=0; x < (ssize_t) image->columns; x++)
301 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
306 PixelChannel channel = GetPixelChannelChannel(image,i);
307 PixelTrait traits = GetPixelChannelTraits(image,channel);
308 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
310 if ((traits == UndefinedPixelTrait) ||
311 (threshold_traits == UndefinedPixelTrait))
313 if (((threshold_traits & CopyPixelTrait) != 0) ||
314 (GetPixelWriteMask(image,p) <= (QuantumRange/2)))
316 SetPixelChannel(threshold_image,channel,p[center+i],q);
319 channel_sum[channel]-=channel_bias[channel];
320 channel_bias[channel]=0.0;
322 for (v=0; v < (ssize_t) height; v++)
324 channel_bias[channel]+=pixels[i];
325 pixels+=(width-1)*GetPixelChannels(image);
326 channel_sum[channel]+=pixels[i];
327 pixels+=GetPixelChannels(image)*(image->columns+1);
329 mean=(double) (channel_sum[channel]/number_pixels+bias);
330 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
331 p[center+i] <= mean ? 0 : QuantumRange),q);
333 p+=GetPixelChannels(image);
334 q+=GetPixelChannels(threshold_image);
336 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
338 if (image->progress_monitor != (MagickProgressMonitor) NULL)
343 #if defined(MAGICKCORE_OPENMP_SUPPORT)
344 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
346 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
348 if (proceed == MagickFalse)
352 threshold_image->type=image->type;
353 threshold_view=DestroyCacheView(threshold_view);
354 image_view=DestroyCacheView(image_view);
355 if (status == MagickFalse)
356 threshold_image=DestroyImage(threshold_image);
357 return(threshold_image);
361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
365 % A u t o T h r e s h o l d I m a g e %
369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371 % AutoThresholdImage() automatically selects a threshold and replaces each
372 % pixel in the image with a black pixel if the image intentsity is less than
373 % the selected threshold otherwise white.
375 % The format of the AutoThresholdImage method is:
377 % MagickBooleanType AutoThresholdImage(Image *image,
378 % const AutoThresholdMethod method,ExceptionInfo *exception)
380 % A description of each parameter follows:
382 % o image: The image to auto-threshold.
384 % o method: choose from Kapur, OTSU, or Triangle.
386 % o exception: return any errors or warnings in this structure.
390 static double KapurThreshold(const Image *image,const double *histogram,
391 ExceptionInfo *exception)
393 #define MaxIntensity 255
397 *cumulative_histogram,
411 Compute optimal threshold from the entopy of the histogram.
413 cumulative_histogram=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
414 sizeof(*cumulative_histogram));
415 black_entropy=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
416 sizeof(*black_entropy));
417 white_entropy=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
418 sizeof(*white_entropy));
419 if ((cumulative_histogram == (double *) NULL) ||
420 (black_entropy == (double *) NULL) || (white_entropy == (double *) NULL))
422 if (white_entropy != (double *) NULL)
423 white_entropy=(double *) RelinquishMagickMemory(white_entropy);
424 if (black_entropy != (double *) NULL)
425 black_entropy=(double *) RelinquishMagickMemory(black_entropy);
426 if (cumulative_histogram != (double *) NULL)
427 cumulative_histogram=(double *)
428 RelinquishMagickMemory(cumulative_histogram);
429 (void) ThrowMagickException(exception,GetMagickModule(),
430 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
434 Entropy for black and white parts of the histogram.
436 cumulative_histogram[0]=histogram[0];
437 for (i=1; i <= MaxIntensity; i++)
438 cumulative_histogram[i]=cumulative_histogram[i-1]+histogram[i];
439 epsilon=MagickMinimumValue;
440 for (j=0; j <= MaxIntensity; j++)
445 black_entropy[j]=0.0;
446 if (cumulative_histogram[j] > epsilon)
449 for (i=0; i <= j; i++)
450 if (histogram[i] > epsilon)
451 entropy-=histogram[i]/cumulative_histogram[j]*
452 log(histogram[i]/cumulative_histogram[j]);
453 black_entropy[j]=entropy;
458 white_entropy[j]=0.0;
459 if ((1.0-cumulative_histogram[j]) > epsilon)
462 for (i=j+1; i <= MaxIntensity; i++)
463 if (histogram[i] > epsilon)
464 entropy-=histogram[i]/(1.0-cumulative_histogram[j])*
465 log(histogram[i]/(1.0-cumulative_histogram[j]));
466 white_entropy[j]=entropy;
470 Find histogram bin with maximum entropy.
472 maximum_entropy=black_entropy[0]+white_entropy[0];
474 for (j=1; j <= MaxIntensity; j++)
475 if ((black_entropy[j]+white_entropy[j]) > maximum_entropy)
477 maximum_entropy=black_entropy[j]+white_entropy[j];
478 threshold=(size_t) j;
483 white_entropy=(double *) RelinquishMagickMemory(white_entropy);
484 black_entropy=(double *) RelinquishMagickMemory(black_entropy);
485 cumulative_histogram=(double *) RelinquishMagickMemory(cumulative_histogram);
486 return(100.0*threshold/MaxIntensity);
489 static double OTSUThreshold(const Image *image,const double *histogram,
490 ExceptionInfo *exception)
504 Compute optimal threshold from maximization of inter-class variance.
506 myu=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*myu));
507 omega=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*omega));
508 probability=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
509 sizeof(*probability));
510 sigma=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*sigma));
511 if ((myu == (double *) NULL) || (omega == (double *) NULL) ||
512 (probability == (double *) NULL) || (sigma == (double *) NULL))
514 if (sigma != (double *) NULL)
515 sigma=(double *) RelinquishMagickMemory(sigma);
516 if (probability != (double *) NULL)
517 probability=(double *) RelinquishMagickMemory(probability);
518 if (omega != (double *) NULL)
519 omega=(double *) RelinquishMagickMemory(omega);
520 if (myu != (double *) NULL)
521 myu=(double *) RelinquishMagickMemory(myu);
522 (void) ThrowMagickException(exception,GetMagickModule(),
523 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
527 Calculate probability density.
529 for (i=0; i <= (ssize_t) MaxIntensity; i++)
530 probability[i]=histogram[i];
532 Generate probability of graylevels and mean value for separation.
534 omega[0]=probability[0];
536 for (i=1; i <= (ssize_t) MaxIntensity; i++)
538 omega[i]=omega[i-1]+probability[i];
539 myu[i]=myu[i-1]+i*probability[i];
542 Sigma maximization: inter-class variance and compute optimal threshold.
546 for (i=0; i < (ssize_t) MaxIntensity; i++)
549 if ((omega[i] != 0.0) && (omega[i] != 1.0))
550 sigma[i]=pow(myu[MaxIntensity]*omega[i]-myu[i],2.0)/(omega[i]*(1.0-
552 if (sigma[i] > max_sigma)
555 threshold=(double) i;
561 myu=(double *) RelinquishMagickMemory(myu);
562 omega=(double *) RelinquishMagickMemory(omega);
563 probability=(double *) RelinquishMagickMemory(probability);
564 sigma=(double *) RelinquishMagickMemory(sigma);
565 return(100.0*threshold/MaxIntensity);
568 static double TriangleThreshold(const Image *image,const double *histogram,
569 ExceptionInfo *exception)
595 Compute optimal threshold with triangle algorithm.
597 start=0; /* find start bin, first bin not zero count */
598 for (i=0; i <= (ssize_t) MaxIntensity; i++)
599 if (histogram[i] > 0.0)
604 end=0; /* find end bin, last bin not zero count */
605 for (i=(ssize_t) MaxIntensity; i >= 0; i--)
606 if (histogram[i] > 0.0)
611 max=0; /* find max bin, bin with largest count */
613 for (i=0; i <= (ssize_t) MaxIntensity; i++)
614 if (histogram[i] > count)
620 Compute threshold at split point.
625 if ((max-start) >= (end-max))
630 c=(-1.0)*(a*x1+b*y1);
631 inverse_ratio=1.0/sqrt(a*a+b*b+c*c);
634 if (x2 == (double) start)
635 for (i=start; i < max; i++)
637 segment=inverse_ratio*(a*i+b*histogram[i]+c);
638 distance=sqrt(segment*segment);
639 if ((distance > max_distance) && (segment > 0.0))
642 max_distance=distance;
646 for (i=end; 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;
656 return(100.0*threshold/MaxIntensity);
659 MagickExport MagickBooleanType AutoThresholdImage(Image *image,
660 const AutoThresholdMethod method,ExceptionInfo *exception)
666 property[MagickPathExtent];
686 assert(image != (Image *) NULL);
687 assert(image->signature == MagickCoreSignature);
688 if (image->debug != MagickFalse)
689 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
690 histogram=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
692 if (histogram == (double *) NULL)
693 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
696 (void) ResetMagickMemory(histogram,0,(MaxIntensity+1UL)*sizeof(*histogram));
697 image_view=AcquireVirtualCacheView(image,exception);
698 for (y=0; y < (ssize_t) image->rows; y++)
700 register const Quantum
706 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
707 if (p == (const Quantum *) NULL)
709 for (x=0; x < (ssize_t) image->columns; x++)
711 double intensity = GetPixelIntensity(image,p);
712 histogram[ScaleQuantumToChar(ClampToQuantum(intensity))]++;
713 p+=GetPixelChannels(image);
716 image_view=DestroyCacheView(image_view);
721 for (i=0; i <= (ssize_t) MaxIntensity; i++)
723 gamma=PerceptibleReciprocal(sum);
724 for (i=0; i <= (ssize_t) MaxIntensity; i++)
725 histogram[i]=gamma*histogram[i];
727 Discover threshold from histogram.
731 case KapurThresholdMethod:
733 threshold=KapurThreshold(image,histogram,exception);
736 case OTSUThresholdMethod:
739 threshold=OTSUThreshold(image,histogram,exception);
742 case TriangleThresholdMethod:
744 threshold=TriangleThreshold(image,histogram,exception);
748 histogram=(double *) RelinquishMagickMemory(histogram);
751 if (status == MagickFalse)
756 (void) FormatLocaleString(property,MagickPathExtent,"%g%%",threshold);
757 (void) SetImageProperty(image,"auto-threshold:threshold",property,exception);
758 return(BilevelImage(image,QuantumRange*threshold/100.0,exception));
762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
766 % B i l e v e l I m a g e %
770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772 % BilevelImage() changes the value of individual pixels based on the
773 % intensity of each pixel channel. The result is a high-contrast image.
775 % More precisely each channel value of the image is 'thresholded' so that if
776 % it is equal to or less than the given value it is set to zero, while any
777 % value greater than that give is set to it maximum or QuantumRange.
779 % This function is what is used to implement the "-threshold" operator for
780 % the command line API.
782 % If the default channel setting is given the image is thresholded using just
783 % the gray 'intensity' of the image, rather than the individual channels.
785 % The format of the BilevelImage method is:
787 % MagickBooleanType BilevelImage(Image *image,const double threshold,
788 % ExceptionInfo *exception)
790 % A description of each parameter follows:
792 % o image: the image.
794 % o threshold: define the threshold values.
796 % o exception: return any errors or warnings in this structure.
798 % Aside: You can get the same results as operator using LevelImages()
799 % with the 'threshold' value for both the black_point and the white_point.
802 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
803 ExceptionInfo *exception)
805 #define ThresholdImageTag "Threshold/Image"
819 assert(image != (Image *) NULL);
820 assert(image->signature == MagickCoreSignature);
821 if (image->debug != MagickFalse)
822 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
823 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
825 if (IsGrayColorspace(image->colorspace) != MagickFalse)
826 (void) SetImageColorspace(image,sRGBColorspace,exception);
828 Bilevel threshold image.
832 image_view=AcquireAuthenticCacheView(image,exception);
833 #if defined(MAGICKCORE_OPENMP_SUPPORT)
834 #pragma omp parallel for schedule(static,4) shared(progress,status) \
835 magick_number_threads(image,image,image->rows,1)
837 for (y=0; y < (ssize_t) image->rows; y++)
845 if (status == MagickFalse)
847 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
848 if (q == (Quantum *) NULL)
853 for (x=0; x < (ssize_t) image->columns; x++)
861 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
863 q+=GetPixelChannels(image);
866 pixel=GetPixelIntensity(image,q);
867 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
869 PixelChannel channel = GetPixelChannelChannel(image,i);
870 PixelTrait traits = GetPixelChannelTraits(image,channel);
871 if ((traits & UpdatePixelTrait) == 0)
873 if (image->channel_mask != DefaultChannels)
875 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
877 q+=GetPixelChannels(image);
879 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
881 if (image->progress_monitor != (MagickProgressMonitor) NULL)
886 #if defined(MAGICKCORE_OPENMP_SUPPORT)
887 #pragma omp critical (MagickCore_BilevelImage)
889 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
891 if (proceed == MagickFalse)
895 image_view=DestroyCacheView(image_view);
900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
904 % B l a c k T h r e s h o l d I m a g e %
908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
910 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
911 % the threshold into black while leaving all pixels at or above the threshold
914 % The format of the BlackThresholdImage method is:
916 % MagickBooleanType BlackThresholdImage(Image *image,
917 % const char *threshold,ExceptionInfo *exception)
919 % A description of each parameter follows:
921 % o image: the image.
923 % o threshold: define the threshold value.
925 % o exception: return any errors or warnings in this structure.
928 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
929 const char *thresholds,ExceptionInfo *exception)
931 #define ThresholdImageTag "Threshold/Image"
954 assert(image != (Image *) NULL);
955 assert(image->signature == MagickCoreSignature);
956 if (image->debug != MagickFalse)
957 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
958 if (thresholds == (const char *) NULL)
960 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
962 if (IsGrayColorspace(image->colorspace) != MagickFalse)
963 (void) SetImageColorspace(image,sRGBColorspace,exception);
964 GetPixelInfo(image,&threshold);
965 flags=ParseGeometry(thresholds,&geometry_info);
966 threshold.red=geometry_info.rho;
967 threshold.green=geometry_info.rho;
968 threshold.blue=geometry_info.rho;
969 threshold.black=geometry_info.rho;
970 threshold.alpha=100.0;
971 if ((flags & SigmaValue) != 0)
972 threshold.green=geometry_info.sigma;
973 if ((flags & XiValue) != 0)
974 threshold.blue=geometry_info.xi;
975 if ((flags & PsiValue) != 0)
976 threshold.alpha=geometry_info.psi;
977 if (threshold.colorspace == CMYKColorspace)
979 if ((flags & PsiValue) != 0)
980 threshold.black=geometry_info.psi;
981 if ((flags & ChiValue) != 0)
982 threshold.alpha=geometry_info.chi;
984 if ((flags & PercentValue) != 0)
986 threshold.red*=(MagickRealType) (QuantumRange/100.0);
987 threshold.green*=(MagickRealType) (QuantumRange/100.0);
988 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
989 threshold.black*=(MagickRealType) (QuantumRange/100.0);
990 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
993 White threshold image.
997 image_view=AcquireAuthenticCacheView(image,exception);
998 #if defined(MAGICKCORE_OPENMP_SUPPORT)
999 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1000 magick_number_threads(image,image,image->rows,1)
1002 for (y=0; y < (ssize_t) image->rows; y++)
1010 if (status == MagickFalse)
1012 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1013 if (q == (Quantum *) NULL)
1018 for (x=0; x < (ssize_t) image->columns; x++)
1026 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1028 q+=GetPixelChannels(image);
1031 pixel=GetPixelIntensity(image,q);
1032 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1034 PixelChannel channel = GetPixelChannelChannel(image,i);
1035 PixelTrait traits = GetPixelChannelTraits(image,channel);
1036 if ((traits & UpdatePixelTrait) == 0)
1038 if (image->channel_mask != DefaultChannels)
1039 pixel=(double) q[i];
1040 if (pixel < GetPixelInfoChannel(&threshold,channel))
1043 q+=GetPixelChannels(image);
1045 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1047 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1052 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1053 #pragma omp critical (MagickCore_BlackThresholdImage)
1055 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1057 if (proceed == MagickFalse)
1061 image_view=DestroyCacheView(image_view);
1066 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1070 % C l a m p I m a g e %
1074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1076 % ClampImage() set each pixel whose value is below zero to zero and any the
1077 % pixel whose value is above the quantum range to the quantum range (e.g.
1078 % 65535) otherwise the pixel value remains unchanged.
1080 % The format of the ClampImage method is:
1082 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
1084 % A description of each parameter follows:
1086 % o image: the image.
1088 % o exception: return any errors or warnings in this structure.
1092 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
1094 #define ClampImageTag "Clamp/Image"
1108 assert(image != (Image *) NULL);
1109 assert(image->signature == MagickCoreSignature);
1110 if (image->debug != MagickFalse)
1111 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1112 if (image->storage_class == PseudoClass)
1121 for (i=0; i < (ssize_t) image->colors; i++)
1123 q->red=(double) ClampPixel(q->red);
1124 q->green=(double) ClampPixel(q->green);
1125 q->blue=(double) ClampPixel(q->blue);
1126 q->alpha=(double) ClampPixel(q->alpha);
1129 return(SyncImage(image,exception));
1136 image_view=AcquireAuthenticCacheView(image,exception);
1137 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1138 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1139 magick_number_threads(image,image,image->rows,1)
1141 for (y=0; y < (ssize_t) image->rows; y++)
1149 if (status == MagickFalse)
1151 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1152 if (q == (Quantum *) NULL)
1157 for (x=0; x < (ssize_t) image->columns; x++)
1162 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1164 q+=GetPixelChannels(image);
1167 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1169 PixelChannel channel = GetPixelChannelChannel(image,i);
1170 PixelTrait traits = GetPixelChannelTraits(image,channel);
1171 if ((traits & UpdatePixelTrait) == 0)
1173 q[i]=ClampPixel((MagickRealType) q[i]);
1175 q+=GetPixelChannels(image);
1177 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1179 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1184 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1185 #pragma omp critical (MagickCore_ClampImage)
1187 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
1188 if (proceed == MagickFalse)
1192 image_view=DestroyCacheView(image_view);
1197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201 % D e s t r o y T h r e s h o l d M a p %
1205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1207 % DestroyThresholdMap() de-allocate the given ThresholdMap
1209 % The format of the ListThresholdMaps method is:
1211 % ThresholdMap *DestroyThresholdMap(Threshold *map)
1213 % A description of each parameter follows.
1215 % o map: Pointer to the Threshold map to destroy
1218 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
1220 assert(map != (ThresholdMap *) NULL);
1221 if (map->map_id != (char *) NULL)
1222 map->map_id=DestroyString(map->map_id);
1223 if (map->description != (char *) NULL)
1224 map->description=DestroyString(map->description);
1225 if (map->levels != (ssize_t *) NULL)
1226 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1227 map=(ThresholdMap *) RelinquishMagickMemory(map);
1232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1236 % G e t T h r e s h o l d M a p %
1240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242 % GetThresholdMap() loads and searches one or more threshold map files for the
1243 % map matching the given name or alias.
1245 % The format of the GetThresholdMap method is:
1247 % ThresholdMap *GetThresholdMap(const char *map_id,
1248 % ExceptionInfo *exception)
1250 % A description of each parameter follows.
1252 % o map_id: ID of the map to look for.
1254 % o exception: return any errors or warnings in this structure.
1257 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1258 ExceptionInfo *exception)
1263 map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
1264 if (map != (ThresholdMap *) NULL)
1266 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
1274 options=GetConfigureOptions(ThresholdsFilename,exception);
1275 option=(const StringInfo *) GetNextValueInLinkedList(options);
1276 while (option != (const StringInfo *) NULL)
1278 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1279 GetStringInfoPath(option),map_id,exception);
1280 if (map != (ThresholdMap *) NULL)
1282 option=(const StringInfo *) GetNextValueInLinkedList(options);
1284 options=DestroyConfigureOptions(options);
1291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1295 + G e t T h r e s h o l d M a p F i l e %
1299 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1301 % GetThresholdMapFile() look for a given threshold map name or alias in the
1302 % given XML file data, and return the allocated the map when found.
1304 % The format of the ListThresholdMaps method is:
1306 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
1307 % const char *map_id,ExceptionInfo *exception)
1309 % A description of each parameter follows.
1311 % o xml: The threshold map list in XML format.
1313 % o filename: The threshold map XML filename.
1315 % o map_id: ID of the map to look for in XML list.
1317 % o exception: return any errors or warnings in this structure.
1320 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
1321 const char *map_id,ExceptionInfo *exception)
1345 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1346 "Loading threshold map file \"%s\" ...",filename);
1347 map=(ThresholdMap *) NULL;
1348 thresholds=NewXMLTree(xml,exception);
1349 if (thresholds == (XMLTreeInfo *) NULL)
1351 for (threshold=GetXMLTreeChild(thresholds,"threshold");
1352 threshold != (XMLTreeInfo *) NULL;
1353 threshold=GetNextXMLTreeTag(threshold))
1355 attribute=GetXMLTreeAttribute(threshold,"map");
1356 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1358 attribute=GetXMLTreeAttribute(threshold,"alias");
1359 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1362 if (threshold == (XMLTreeInfo *) NULL)
1364 thresholds=DestroyXMLTree(thresholds);
1367 description=GetXMLTreeChild(threshold,"description");
1368 if (description == (XMLTreeInfo *) NULL)
1370 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1371 "XmlMissingElement", "<description>, map \"%s\"",map_id);
1372 thresholds=DestroyXMLTree(thresholds);
1375 levels=GetXMLTreeChild(threshold,"levels");
1376 if (levels == (XMLTreeInfo *) NULL)
1378 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1379 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
1380 thresholds=DestroyXMLTree(thresholds);
1383 map=(ThresholdMap *) AcquireCriticalMemory(sizeof(*map));
1384 map->map_id=(char *) NULL;
1385 map->description=(char *) NULL;
1386 map->levels=(ssize_t *) NULL;
1387 attribute=GetXMLTreeAttribute(threshold,"map");
1388 if (attribute != (char *) NULL)
1389 map->map_id=ConstantString(attribute);
1390 content=GetXMLTreeContent(description);
1391 if (content != (char *) NULL)
1392 map->description=ConstantString(content);
1393 attribute=GetXMLTreeAttribute(levels,"width");
1394 if (attribute == (char *) NULL)
1396 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1397 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1398 thresholds=DestroyXMLTree(thresholds);
1399 map=DestroyThresholdMap(map);
1402 map->width=StringToUnsignedLong(attribute);
1403 if (map->width == 0)
1405 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1406 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1407 thresholds=DestroyXMLTree(thresholds);
1408 map=DestroyThresholdMap(map);
1411 attribute=GetXMLTreeAttribute(levels,"height");
1412 if (attribute == (char *) NULL)
1414 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1415 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1416 thresholds=DestroyXMLTree(thresholds);
1417 map=DestroyThresholdMap(map);
1420 map->height=StringToUnsignedLong(attribute);
1421 if (map->height == 0)
1423 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1424 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1425 thresholds=DestroyXMLTree(thresholds);
1426 map=DestroyThresholdMap(map);
1429 attribute=GetXMLTreeAttribute(levels,"divisor");
1430 if (attribute == (char *) NULL)
1432 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1433 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1434 thresholds=DestroyXMLTree(thresholds);
1435 map=DestroyThresholdMap(map);
1438 map->divisor=(ssize_t) StringToLong(attribute);
1439 if (map->divisor < 2)
1441 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1442 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1443 thresholds=DestroyXMLTree(thresholds);
1444 map=DestroyThresholdMap(map);
1447 content=GetXMLTreeContent(levels);
1448 if (content == (char *) NULL)
1450 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1451 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1452 thresholds=DestroyXMLTree(thresholds);
1453 map=DestroyThresholdMap(map);
1456 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1457 sizeof(*map->levels));
1458 if (map->levels == (ssize_t *) NULL)
1459 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1460 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1462 map->levels[i]=(ssize_t) strtol(content,&p,10);
1465 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1466 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1467 thresholds=DestroyXMLTree(thresholds);
1468 map=DestroyThresholdMap(map);
1471 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1473 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1474 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1475 (double) map->levels[i],map_id);
1476 thresholds=DestroyXMLTree(thresholds);
1477 map=DestroyThresholdMap(map);
1482 value=(double) strtol(content,&p,10);
1486 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1487 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1488 thresholds=DestroyXMLTree(thresholds);
1489 map=DestroyThresholdMap(map);
1492 thresholds=DestroyXMLTree(thresholds);
1497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1501 + L i s t T h r e s h o l d M a p F i l e %
1505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1507 % ListThresholdMapFile() lists the threshold maps and their descriptions
1508 % in the given XML file data.
1510 % The format of the ListThresholdMaps method is:
1512 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1513 % const char *filename,ExceptionInfo *exception)
1515 % A description of each parameter follows.
1517 % o file: An pointer to the output FILE.
1519 % o xml: The threshold map list in XML format.
1521 % o filename: The threshold map XML filename.
1523 % o exception: return any errors or warnings in this structure.
1526 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1527 const char *filename,ExceptionInfo *exception)
1539 assert( xml != (char *) NULL );
1540 assert( file != (FILE *) NULL );
1541 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1542 "Loading threshold map file \"%s\" ...",filename);
1543 thresholds=NewXMLTree(xml,exception);
1544 if ( thresholds == (XMLTreeInfo *) NULL )
1545 return(MagickFalse);
1546 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1547 (void) FormatLocaleFile(file,
1548 "----------------------------------------------------\n");
1549 threshold=GetXMLTreeChild(thresholds,"threshold");
1550 for ( ; threshold != (XMLTreeInfo *) NULL;
1551 threshold=GetNextXMLTreeTag(threshold))
1553 map=GetXMLTreeAttribute(threshold,"map");
1554 if (map == (char *) NULL)
1556 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1557 "XmlMissingAttribute", "<map>");
1558 thresholds=DestroyXMLTree(thresholds);
1559 return(MagickFalse);
1561 alias=GetXMLTreeAttribute(threshold,"alias");
1562 description=GetXMLTreeChild(threshold,"description");
1563 if (description == (XMLTreeInfo *) NULL)
1565 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1566 "XmlMissingElement", "<description>, map \"%s\"",map);
1567 thresholds=DestroyXMLTree(thresholds);
1568 return(MagickFalse);
1570 content=GetXMLTreeContent(description);
1571 if (content == (char *) NULL)
1573 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1574 "XmlMissingContent", "<description>, map \"%s\"", map);
1575 thresholds=DestroyXMLTree(thresholds);
1576 return(MagickFalse);
1578 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1581 thresholds=DestroyXMLTree(thresholds);
1586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1590 % L i s t T h r e s h o l d M a p s %
1594 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1596 % ListThresholdMaps() lists the threshold maps and their descriptions
1597 % as defined by "threshold.xml" to a file.
1599 % The format of the ListThresholdMaps method is:
1601 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1603 % A description of each parameter follows.
1605 % o file: An pointer to the output FILE.
1607 % o exception: return any errors or warnings in this structure.
1610 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1611 ExceptionInfo *exception)
1623 if (file == (FILE *) NULL)
1625 options=GetConfigureOptions(ThresholdsFilename,exception);
1626 (void) FormatLocaleFile(file,
1627 "\n Threshold Maps for Ordered Dither Operations\n");
1628 option=(const StringInfo *) GetNextValueInLinkedList(options);
1629 while (option != (const StringInfo *) NULL)
1631 (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1632 status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1633 GetStringInfoPath(option),exception);
1634 option=(const StringInfo *) GetNextValueInLinkedList(options);
1636 options=DestroyConfigureOptions(options);
1637 return(status != 0 ? MagickTrue : MagickFalse);
1641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1645 % O r d e r e d D i t h e r I m a g e %
1649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1651 % OrderedDitherImage() will perform a ordered dither based on a number
1652 % of pre-defined dithering threshold maps, but over multiple intensity
1653 % levels, which can be different for different channels, according to the
1656 % The format of the OrderedDitherImage method is:
1658 % MagickBooleanType OrderedDitherImage(Image *image,
1659 % const char *threshold_map,ExceptionInfo *exception)
1661 % A description of each parameter follows:
1663 % o image: the image.
1665 % o threshold_map: A string containing the name of the threshold dither
1666 % map to use, followed by zero or more numbers representing the number
1667 % of color levels tho dither between.
1669 % Any level number less than 2 will be equivalent to 2, and means only
1670 % binary dithering will be applied to each color channel.
1672 % No numbers also means a 2 level (bitmap) dither will be applied to all
1673 % channels, while a single number is the number of levels applied to each
1674 % channel in sequence. More numbers will be applied in turn to each of
1675 % the color channels.
1677 % For example: "o3x3,6" will generate a 6 level posterization of the
1678 % image with a ordered 3x3 diffused pixel dither being applied between
1679 % each level. While checker,8,8,4 will produce a 332 colormaped image
1680 % with only a single checkerboard hash pattern (50% grey) between each
1681 % color level, to basically double the number of color levels with
1682 % a bare minimim of dithering.
1684 % o exception: return any errors or warnings in this structure.
1687 MagickExport MagickBooleanType OrderedDitherImage(Image *image,
1688 const char *threshold_map,ExceptionInfo *exception)
1690 #define DitherImageTag "Dither/Image"
1696 token[MagickPathExtent];
1702 levels[CompositePixelChannel];
1719 assert(image != (Image *) NULL);
1720 assert(image->signature == MagickCoreSignature);
1721 if (image->debug != MagickFalse)
1722 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1723 assert(exception != (ExceptionInfo *) NULL);
1724 assert(exception->signature == MagickCoreSignature);
1725 if (threshold_map == (const char *) NULL)
1727 p=(char *) threshold_map;
1728 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1732 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1735 if ((p-threshold_map) >= (MagickPathExtent-1))
1737 token[p-threshold_map]=(*p);
1740 token[p-threshold_map]='\0';
1741 map=GetThresholdMap(token,exception);
1742 if (map == (ThresholdMap *) NULL)
1744 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1745 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1746 return(MagickFalse);
1748 for (i=0; i < MaxPixelChannels; i++)
1750 p=strchr((char *) threshold_map,',');
1751 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1753 GetNextToken(p,&p,MagickPathExtent,token);
1754 for (i=0; (i < MaxPixelChannels); i++)
1755 levels[i]=StringToDouble(token,(char **) NULL);
1756 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1758 GetNextToken(p,&p,MagickPathExtent,token);
1760 GetNextToken(p,&p,MagickPathExtent,token);
1761 levels[i]=StringToDouble(token,(char **) NULL);
1764 for (i=0; i < MaxPixelChannels; i++)
1765 if (fabs(levels[i]) >= 1)
1767 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1768 return(MagickFalse);
1771 image_view=AcquireAuthenticCacheView(image,exception);
1772 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1773 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1774 magick_number_threads(image,image,image->rows,1)
1776 for (y=0; y < (ssize_t) image->rows; y++)
1784 if (status == MagickFalse)
1786 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1787 if (q == (Quantum *) NULL)
1792 for (x=0; x < (ssize_t) image->columns; x++)
1801 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1803 q+=GetPixelChannels(image);
1806 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1812 PixelChannel channel = GetPixelChannelChannel(image,i);
1813 PixelTrait traits = GetPixelChannelTraits(image,channel);
1814 if ((traits & UpdatePixelTrait) == 0)
1816 if (fabs(levels[n]) < MagickEpsilon)
1821 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1822 level=threshold/(map->divisor-1);
1823 threshold-=level*(map->divisor-1);
1824 q[i]=ClampToQuantum((double) (level+(threshold >=
1825 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1826 QuantumRange/levels[n]);
1829 q+=GetPixelChannels(image);
1831 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1833 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1838 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1839 #pragma omp critical (MagickCore_OrderedDitherImage)
1841 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1842 if (proceed == MagickFalse)
1846 image_view=DestroyCacheView(image_view);
1847 map=DestroyThresholdMap(map);
1852 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1856 % P e r c e p t i b l e I m a g e %
1860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1862 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1863 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1866 % The format of the PerceptibleImage method is:
1868 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1869 % ExceptionInfo *exception)
1871 % A description of each parameter follows:
1873 % o image: the image.
1875 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1877 % o exception: return any errors or warnings in this structure.
1881 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1882 const double epsilon)
1887 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1888 if ((sign*quantum) >= epsilon)
1890 return((Quantum) (sign*epsilon));
1893 MagickExport MagickBooleanType PerceptibleImage(Image *image,
1894 const double epsilon,ExceptionInfo *exception)
1896 #define PerceptibleImageTag "Perceptible/Image"
1910 assert(image != (Image *) NULL);
1911 assert(image->signature == MagickCoreSignature);
1912 if (image->debug != MagickFalse)
1913 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1914 if (image->storage_class == PseudoClass)
1923 for (i=0; i < (ssize_t) image->colors; i++)
1925 q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1927 q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1929 q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1931 q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1935 return(SyncImage(image,exception));
1942 image_view=AcquireAuthenticCacheView(image,exception);
1943 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1944 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1945 magick_number_threads(image,image,image->rows,1)
1947 for (y=0; y < (ssize_t) image->rows; y++)
1955 if (status == MagickFalse)
1957 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1958 if (q == (Quantum *) NULL)
1963 for (x=0; x < (ssize_t) image->columns; x++)
1968 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1970 q+=GetPixelChannels(image);
1973 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1975 PixelChannel channel = GetPixelChannelChannel(image,i);
1976 PixelTrait traits = GetPixelChannelTraits(image,channel);
1977 if (traits == UndefinedPixelTrait)
1979 q[i]=PerceptibleThreshold(q[i],epsilon);
1981 q+=GetPixelChannels(image);
1983 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1985 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1990 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1991 #pragma omp critical (MagickCore_PerceptibleImage)
1993 proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1994 if (proceed == MagickFalse)
1998 image_view=DestroyCacheView(image_view);
2003 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2007 % R a n d o m T h r e s h o l d I m a g e %
2011 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2013 % RandomThresholdImage() changes the value of individual pixels based on the
2014 % intensity of each pixel compared to a random threshold. The result is a
2015 % low-contrast, two color image.
2017 % The format of the RandomThresholdImage method is:
2019 % MagickBooleanType RandomThresholdImage(Image *image,
2020 % const char *thresholds,ExceptionInfo *exception)
2022 % A description of each parameter follows:
2024 % o image: the image.
2026 % o low,high: Specify the high and low thresholds. These values range from
2027 % 0 to QuantumRange.
2029 % o exception: return any errors or warnings in this structure.
2032 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
2033 const double min_threshold, const double max_threshold,ExceptionInfo *exception)
2035 #define ThresholdImageTag "Threshold/Image"
2050 **magick_restrict random_info;
2055 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2060 assert(image != (Image *) NULL);
2061 assert(image->signature == MagickCoreSignature);
2062 if (image->debug != MagickFalse)
2063 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2064 assert(exception != (ExceptionInfo *) NULL);
2065 assert(exception->signature == MagickCoreSignature);
2066 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2067 return(MagickFalse);
2068 GetPixelInfo(image,&threshold);
2070 Random threshold image.
2074 random_info=AcquireRandomInfoThreadSet();
2075 image_view=AcquireAuthenticCacheView(image,exception);
2076 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2077 key=GetRandomSecretKey(random_info[0]);
2078 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2079 magick_number_threads(image,image,image->rows,key == ~0UL)
2081 for (y=0; y < (ssize_t) image->rows; y++)
2084 id = GetOpenMPThreadId();
2092 if (status == MagickFalse)
2094 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2095 if (q == (Quantum *) NULL)
2100 for (x=0; x < (ssize_t) image->columns; x++)
2105 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2107 q+=GetPixelChannels(image);
2110 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2115 PixelChannel channel = GetPixelChannelChannel(image,i);
2116 PixelTrait traits = GetPixelChannelTraits(image,channel);
2117 if ((traits & UpdatePixelTrait) == 0)
2119 if ((double) q[i] < min_threshold)
2120 threshold=min_threshold;
2122 if ((double) q[i] > max_threshold)
2123 threshold=max_threshold;
2125 threshold=(double) (QuantumRange*
2126 GetPseudoRandomValue(random_info[id]));
2127 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
2129 q+=GetPixelChannels(image);
2131 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2133 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2138 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2139 #pragma omp critical (MagickCore_RandomThresholdImage)
2141 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2143 if (proceed == MagickFalse)
2147 image_view=DestroyCacheView(image_view);
2148 random_info=DestroyRandomInfoThreadSet(random_info);
2153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2157 % W h i t e T h r e s h o l d I m a g e %
2161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2163 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
2164 % the threshold into white while leaving all pixels at or below the threshold
2167 % The format of the WhiteThresholdImage method is:
2169 % MagickBooleanType WhiteThresholdImage(Image *image,
2170 % const char *threshold,ExceptionInfo *exception)
2172 % A description of each parameter follows:
2174 % o image: the image.
2176 % o threshold: Define the threshold value.
2178 % o exception: return any errors or warnings in this structure.
2181 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
2182 const char *thresholds,ExceptionInfo *exception)
2184 #define ThresholdImageTag "Threshold/Image"
2207 assert(image != (Image *) NULL);
2208 assert(image->signature == MagickCoreSignature);
2209 if (image->debug != MagickFalse)
2210 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2211 if (thresholds == (const char *) NULL)
2213 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2214 return(MagickFalse);
2215 if (IsGrayColorspace(image->colorspace) != MagickFalse)
2216 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2217 GetPixelInfo(image,&threshold);
2218 flags=ParseGeometry(thresholds,&geometry_info);
2219 threshold.red=geometry_info.rho;
2220 threshold.green=geometry_info.rho;
2221 threshold.blue=geometry_info.rho;
2222 threshold.black=geometry_info.rho;
2223 threshold.alpha=100.0;
2224 if ((flags & SigmaValue) != 0)
2225 threshold.green=geometry_info.sigma;
2226 if ((flags & XiValue) != 0)
2227 threshold.blue=geometry_info.xi;
2228 if ((flags & PsiValue) != 0)
2229 threshold.alpha=geometry_info.psi;
2230 if (threshold.colorspace == CMYKColorspace)
2232 if ((flags & PsiValue) != 0)
2233 threshold.black=geometry_info.psi;
2234 if ((flags & ChiValue) != 0)
2235 threshold.alpha=geometry_info.chi;
2237 if ((flags & PercentValue) != 0)
2239 threshold.red*=(MagickRealType) (QuantumRange/100.0);
2240 threshold.green*=(MagickRealType) (QuantumRange/100.0);
2241 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
2242 threshold.black*=(MagickRealType) (QuantumRange/100.0);
2243 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
2246 White threshold image.
2250 image_view=AcquireAuthenticCacheView(image,exception);
2251 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2252 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2253 magick_number_threads(image,image,image->rows,1)
2255 for (y=0; y < (ssize_t) image->rows; y++)
2263 if (status == MagickFalse)
2265 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2266 if (q == (Quantum *) NULL)
2271 for (x=0; x < (ssize_t) image->columns; x++)
2279 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2281 q+=GetPixelChannels(image);
2284 pixel=GetPixelIntensity(image,q);
2285 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2287 PixelChannel channel = GetPixelChannelChannel(image,i);
2288 PixelTrait traits = GetPixelChannelTraits(image,channel);
2289 if ((traits & UpdatePixelTrait) == 0)
2291 if (image->channel_mask != DefaultChannels)
2292 pixel=(double) q[i];
2293 if (pixel > GetPixelInfoChannel(&threshold,channel))
2296 q+=GetPixelChannels(image);
2298 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2300 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2305 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2306 #pragma omp critical (MagickCore_WhiteThresholdImage)
2308 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2310 if (proceed == MagickFalse)
2314 image_view=DestroyCacheView(image_view);