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/monitor.h"
68 #include "MagickCore/monitor-private.h"
69 #include "MagickCore/montage.h"
70 #include "MagickCore/option.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/pixel-private.h"
73 #include "MagickCore/quantize.h"
74 #include "MagickCore/quantum.h"
75 #include "MagickCore/quantum-private.h"
76 #include "MagickCore/random_.h"
77 #include "MagickCore/random-private.h"
78 #include "MagickCore/resize.h"
79 #include "MagickCore/resource_.h"
80 #include "MagickCore/segment.h"
81 #include "MagickCore/shear.h"
82 #include "MagickCore/signature-private.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/thread-private.h"
86 #include "MagickCore/threshold.h"
87 #include "MagickCore/token.h"
88 #include "MagickCore/transform.h"
89 #include "MagickCore/xml-tree.h"
90 #include "MagickCore/xml-tree-private.h"
95 #define ThresholdsFilename "thresholds.xml"
119 *MinimalThresholdMap =
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\">"
138 Forward declarations.
141 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 % A d a p t i v e T h r e s h o l d I m a g e %
152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 % AdaptiveThresholdImage() selects an individual threshold for each pixel
155 % based on the range of intensity values in its local neighborhood. This
156 % allows for thresholding of an image whose global intensity histogram
157 % doesn't contain distinctive peaks.
159 % The format of the AdaptiveThresholdImage method is:
161 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
162 % const size_t height,const double bias,ExceptionInfo *exception)
164 % A description of each parameter follows:
166 % o image: the image.
168 % o width: the width of the local neighborhood.
170 % o height: the height of the local neighborhood.
172 % o bias: the mean bias.
174 % o exception: return any errors or warnings in this structure.
177 MagickExport Image *AdaptiveThresholdImage(const Image *image,
178 const size_t width,const size_t height,const double bias,
179 ExceptionInfo *exception)
181 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
203 Initialize threshold image attributes.
205 assert(image != (Image *) NULL);
206 assert(image->signature == MagickCoreSignature);
207 if (image->debug != MagickFalse)
208 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
209 assert(exception != (ExceptionInfo *) NULL);
210 assert(exception->signature == MagickCoreSignature);
211 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
213 if (threshold_image == (Image *) NULL)
214 return((Image *) NULL);
215 status=SetImageStorageClass(threshold_image,DirectClass,exception);
216 if (status == MagickFalse)
218 threshold_image=DestroyImage(threshold_image);
219 return((Image *) NULL);
226 number_pixels=(MagickSizeType) width*height;
227 image_view=AcquireVirtualCacheView(image,exception);
228 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
229 #if defined(MAGICKCORE_OPENMP_SUPPORT)
230 #pragma omp parallel for schedule(static,4) shared(progress,status) \
231 magick_threads(image,threshold_image,image->rows,1)
233 for (y=0; y < (ssize_t) image->rows; y++)
236 channel_bias[MaxPixelChannels],
237 channel_sum[MaxPixelChannels];
239 register const Quantum
241 *magick_restrict pixels;
255 if (status == MagickFalse)
257 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
258 (height/2L),image->columns+width,height,exception);
259 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
261 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
266 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
267 GetPixelChannels(image)*(width/2);
268 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
270 PixelChannel channel = GetPixelChannelChannel(image,i);
271 PixelTrait traits = GetPixelChannelTraits(image,channel);
272 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
274 if ((traits == UndefinedPixelTrait) ||
275 (threshold_traits == UndefinedPixelTrait))
277 if (((threshold_traits & CopyPixelTrait) != 0) ||
278 (GetPixelWriteMask(image,p) <= (QuantumRange/2)))
280 SetPixelChannel(threshold_image,channel,p[center+i],q);
284 channel_bias[channel]=0.0;
285 channel_sum[channel]=0.0;
286 for (v=0; v < (ssize_t) height; v++)
288 for (u=0; u < (ssize_t) width; u++)
290 if (u == (ssize_t) (width-1))
291 channel_bias[channel]+=pixels[i];
292 channel_sum[channel]+=pixels[i];
293 pixels+=GetPixelChannels(image);
295 pixels+=GetPixelChannels(image)*image->columns;
298 for (x=0; x < (ssize_t) image->columns; x++)
300 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
305 PixelChannel channel = GetPixelChannelChannel(image,i);
306 PixelTrait traits = GetPixelChannelTraits(image,channel);
307 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
309 if ((traits == UndefinedPixelTrait) ||
310 (threshold_traits == UndefinedPixelTrait))
312 if (((threshold_traits & CopyPixelTrait) != 0) ||
313 (GetPixelWriteMask(image,p) <= (QuantumRange/2)))
315 SetPixelChannel(threshold_image,channel,p[center+i],q);
318 channel_sum[channel]-=channel_bias[channel];
319 channel_bias[channel]=0.0;
321 for (v=0; v < (ssize_t) height; v++)
323 channel_bias[channel]+=pixels[i];
324 pixels+=(width-1)*GetPixelChannels(image);
325 channel_sum[channel]+=pixels[i];
326 pixels+=GetPixelChannels(image)*(image->columns+1);
328 mean=(double) (channel_sum[channel]/number_pixels+bias);
329 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
330 p[center+i] <= mean ? 0 : QuantumRange),q);
332 p+=GetPixelChannels(image);
333 q+=GetPixelChannels(threshold_image);
335 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
337 if (image->progress_monitor != (MagickProgressMonitor) NULL)
342 #if defined(MAGICKCORE_OPENMP_SUPPORT)
343 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
345 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
347 if (proceed == MagickFalse)
351 threshold_image->type=image->type;
352 threshold_view=DestroyCacheView(threshold_view);
353 image_view=DestroyCacheView(image_view);
354 if (status == MagickFalse)
355 threshold_image=DestroyImage(threshold_image);
356 return(threshold_image);
360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
364 % A u t o T h r e s h o l d I m a g e %
368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370 % AutoThresholdImage() automatically selects a threshold and replaces each
371 % pixel in the image with a black pixel if the image intentsity is less than
372 % the selected threshold otherwise white.
374 % The format of the AutoThresholdImage method is:
376 % MagickBooleanType AutoThresholdImage(Image *image,
377 % const AutoThresholdMethod method,ExceptionInfo *exception)
379 % A description of each parameter follows:
381 % o image: The image to auto-threshold.
383 % o method: choose from Kapur, OTSU, or Triangle.
385 % o exception: return any errors or warnings in this structure.
389 static double KapurThreshold(const Image *image,const double *histogram,
390 ExceptionInfo *exception)
392 #define MaxIntensity 255
396 *cumulative_histogram,
410 Compute optimal threshold from the entopy of the histogram.
412 cumulative_histogram=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
413 sizeof(*cumulative_histogram));
414 black_entropy=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
415 sizeof(*black_entropy));
416 white_entropy=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
417 sizeof(*white_entropy));
418 if ((cumulative_histogram == (double *) NULL) ||
419 (black_entropy == (double *) NULL) || (white_entropy == (double *) NULL))
421 if (white_entropy != (double *) NULL)
422 white_entropy=(double *) RelinquishMagickMemory(white_entropy);
423 if (black_entropy != (double *) NULL)
424 black_entropy=(double *) RelinquishMagickMemory(black_entropy);
425 if (cumulative_histogram != (double *) NULL)
426 cumulative_histogram=(double *)
427 RelinquishMagickMemory(cumulative_histogram);
428 (void) ThrowMagickException(exception,GetMagickModule(),
429 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
433 Entropy for black and white parts of the histogram.
435 cumulative_histogram[0]=histogram[0];
436 for (i=1; i <= MaxIntensity; i++)
437 cumulative_histogram[i]=cumulative_histogram[i-1]+histogram[i];
438 epsilon=MagickMinimumValue;
439 for (j=0; j <= MaxIntensity; j++)
444 black_entropy[j]=0.0;
445 if (cumulative_histogram[j] > epsilon)
448 for (i=0; i <= j; i++)
449 if (histogram[i] > epsilon)
450 entropy-=histogram[i]/cumulative_histogram[j]*
451 log(histogram[i]/cumulative_histogram[j]);
452 black_entropy[j]=entropy;
457 white_entropy[j]=0.0;
458 if ((1.0-cumulative_histogram[j]) > epsilon)
461 for (i=j+1; i <= MaxIntensity; i++)
462 if (histogram[i] > epsilon)
463 entropy-=histogram[i]/(1.0-cumulative_histogram[j])*
464 log(histogram[i]/(1.0-cumulative_histogram[j]));
465 white_entropy[j]=entropy;
469 Find histogram bin with maximum entropy.
471 maximum_entropy=black_entropy[0]+white_entropy[0];
473 for (j=1; j <= MaxIntensity; j++)
474 if ((black_entropy[j]+white_entropy[j]) > maximum_entropy)
476 maximum_entropy=black_entropy[j]+white_entropy[j];
477 threshold=(size_t) j;
482 white_entropy=(double *) RelinquishMagickMemory(white_entropy);
483 black_entropy=(double *) RelinquishMagickMemory(black_entropy);
484 cumulative_histogram=(double *) RelinquishMagickMemory(cumulative_histogram);
485 return(100.0*threshold/MaxIntensity);
488 static double OTSUThreshold(const Image *image,const double *histogram,
489 ExceptionInfo *exception)
503 Compute optimal threshold from maximization of inter-class variance.
505 myu=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*myu));
506 omega=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*omega));
507 probability=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
508 sizeof(*probability));
509 sigma=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*sigma));
510 if ((myu == (double *) NULL) || (omega == (double *) NULL) ||
511 (probability == (double *) NULL) || (sigma == (double *) NULL))
513 if (sigma != (double *) NULL)
514 sigma=(double *) RelinquishMagickMemory(sigma);
515 if (probability != (double *) NULL)
516 probability=(double *) RelinquishMagickMemory(probability);
517 if (omega != (double *) NULL)
518 omega=(double *) RelinquishMagickMemory(omega);
519 if (myu != (double *) NULL)
520 myu=(double *) RelinquishMagickMemory(myu);
521 (void) ThrowMagickException(exception,GetMagickModule(),
522 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
526 Calculate probability density.
528 for (i=0; i <= (ssize_t) MaxIntensity; i++)
529 probability[i]=histogram[i];
531 Generate probability of graylevels and mean value for separation.
533 omega[0]=probability[0];
535 for (i=1; i <= (ssize_t) MaxIntensity; i++)
537 omega[i]=omega[i-1]+probability[i];
538 myu[i]=myu[i-1]+i*probability[i];
541 Sigma maximization: inter-class variance and compute optimal threshold.
545 for (i=0; i < (ssize_t) MaxIntensity; i++)
548 if ((omega[i] != 0.0) && (omega[i] != 1.0))
549 sigma[i]=pow(myu[MaxIntensity]*omega[i]-myu[i],2.0)/(omega[i]*(1.0-
551 if (sigma[i] > max_sigma)
554 threshold=(double) i;
560 myu=(double *) RelinquishMagickMemory(myu);
561 omega=(double *) RelinquishMagickMemory(omega);
562 probability=(double *) RelinquishMagickMemory(probability);
563 sigma=(double *) RelinquishMagickMemory(sigma);
564 return(100.0*threshold/MaxIntensity);
567 static double TriangleThreshold(const Image *image,const double *histogram,
568 ExceptionInfo *exception)
594 Compute optimal threshold with triangle algorithm.
596 start=0; /* find start bin, first bin not zero count */
597 for (i=0; i <= (ssize_t) MaxIntensity; i++)
598 if (histogram[i] > 0.0)
603 end=0; /* find end bin, last bin not zero count */
604 for (i=(ssize_t) MaxIntensity; i >= 0; i--)
605 if (histogram[i] > 0.0)
610 max=0; /* find max bin, bin with largest count */
612 for (i=0; i <= (ssize_t) MaxIntensity; i++)
613 if (histogram[i] > count)
619 Compute threshold at split point.
624 if ((max-start) >= (end-max))
629 c=(-1.0)*(a*x1+b*y1);
630 inverse_ratio=1.0/sqrt(a*a+b*b+c*c);
633 if (x2 == (double) start)
634 for (i=start; i < max; i++)
636 segment=inverse_ratio*(a*i+b*histogram[i]+c);
637 distance=sqrt(segment*segment);
638 if ((distance > max_distance) && (segment > 0.0))
641 max_distance=distance;
645 for (i=end; i > max; i--)
647 segment=inverse_ratio*(a*i+b*histogram[i]+c);
648 distance=sqrt(segment*segment);
649 if ((distance > max_distance) && (segment < 0.0))
652 max_distance=distance;
655 return(100.0*threshold/MaxIntensity);
658 MagickExport MagickBooleanType AutoThresholdImage(Image *image,
659 const AutoThresholdMethod method,ExceptionInfo *exception)
665 property[MagickPathExtent];
685 assert(image != (Image *) NULL);
686 assert(image->signature == MagickCoreSignature);
687 if (image->debug != MagickFalse)
688 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
689 histogram=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
691 if (histogram == (double *) NULL)
692 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
695 (void) ResetMagickMemory(histogram,0,(MaxIntensity+1UL)*sizeof(*histogram));
696 image_view=AcquireVirtualCacheView(image,exception);
697 for (y=0; y < (ssize_t) image->rows; y++)
699 register const Quantum
705 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
706 if (p == (const Quantum *) NULL)
708 for (x=0; x < (ssize_t) image->columns; x++)
710 double intensity = GetPixelIntensity(image,p);
711 histogram[ScaleQuantumToChar(ClampToQuantum(intensity))]++;
712 p+=GetPixelChannels(image);
715 image_view=DestroyCacheView(image_view);
720 for (i=0; i <= (ssize_t) MaxIntensity; i++)
722 gamma=PerceptibleReciprocal(sum);
723 for (i=0; i <= (ssize_t) MaxIntensity; i++)
724 histogram[i]=gamma*histogram[i];
726 Discover threshold from histogram.
730 case KapurThresholdMethod:
732 threshold=KapurThreshold(image,histogram,exception);
735 case OTSUThresholdMethod:
738 threshold=OTSUThreshold(image,histogram,exception);
741 case TriangleThresholdMethod:
743 threshold=TriangleThreshold(image,histogram,exception);
747 histogram=(double *) RelinquishMagickMemory(histogram);
750 if (status == MagickFalse)
755 (void) FormatLocaleString(property,MagickPathExtent,"%g%%",threshold);
756 (void) SetImageProperty(image,"auto-threshold:threshold",property,exception);
757 return(BilevelImage(image,QuantumRange*threshold/100.0,exception));
761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765 % B i l e v e l I m a g e %
769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771 % BilevelImage() changes the value of individual pixels based on the
772 % intensity of each pixel channel. The result is a high-contrast image.
774 % More precisely each channel value of the image is 'thresholded' so that if
775 % it is equal to or less than the given value it is set to zero, while any
776 % value greater than that give is set to it maximum or QuantumRange.
778 % This function is what is used to implement the "-threshold" operator for
779 % the command line API.
781 % If the default channel setting is given the image is thresholded using just
782 % the gray 'intensity' of the image, rather than the individual channels.
784 % The format of the BilevelImage method is:
786 % MagickBooleanType BilevelImage(Image *image,const double threshold,
787 % ExceptionInfo *exception)
789 % A description of each parameter follows:
791 % o image: the image.
793 % o threshold: define the threshold values.
795 % o exception: return any errors or warnings in this structure.
797 % Aside: You can get the same results as operator using LevelImages()
798 % with the 'threshold' value for both the black_point and the white_point.
801 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
802 ExceptionInfo *exception)
804 #define ThresholdImageTag "Threshold/Image"
818 assert(image != (Image *) NULL);
819 assert(image->signature == MagickCoreSignature);
820 if (image->debug != MagickFalse)
821 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
822 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
824 if (IsGrayColorspace(image->colorspace) != MagickFalse)
825 (void) SetImageColorspace(image,sRGBColorspace,exception);
827 Bilevel threshold image.
831 image_view=AcquireAuthenticCacheView(image,exception);
832 #if defined(MAGICKCORE_OPENMP_SUPPORT)
833 #pragma omp parallel for schedule(static,4) shared(progress,status) \
834 magick_threads(image,image,image->rows,1)
836 for (y=0; y < (ssize_t) image->rows; y++)
844 if (status == MagickFalse)
846 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
847 if (q == (Quantum *) NULL)
852 for (x=0; x < (ssize_t) image->columns; x++)
860 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
862 q+=GetPixelChannels(image);
865 pixel=GetPixelIntensity(image,q);
866 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
868 PixelChannel channel = GetPixelChannelChannel(image,i);
869 PixelTrait traits = GetPixelChannelTraits(image,channel);
870 if ((traits & UpdatePixelTrait) == 0)
872 if (image->channel_mask != DefaultChannels)
874 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
876 q+=GetPixelChannels(image);
878 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
880 if (image->progress_monitor != (MagickProgressMonitor) NULL)
885 #if defined(MAGICKCORE_OPENMP_SUPPORT)
886 #pragma omp critical (MagickCore_BilevelImage)
888 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
890 if (proceed == MagickFalse)
894 image_view=DestroyCacheView(image_view);
899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
903 % B l a c k T h r e s h o l d I m a g e %
907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
909 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
910 % the threshold into black while leaving all pixels at or above the threshold
913 % The format of the BlackThresholdImage method is:
915 % MagickBooleanType BlackThresholdImage(Image *image,
916 % const char *threshold,ExceptionInfo *exception)
918 % A description of each parameter follows:
920 % o image: the image.
922 % o threshold: define the threshold value.
924 % o exception: return any errors or warnings in this structure.
927 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
928 const char *thresholds,ExceptionInfo *exception)
930 #define ThresholdImageTag "Threshold/Image"
953 assert(image != (Image *) NULL);
954 assert(image->signature == MagickCoreSignature);
955 if (image->debug != MagickFalse)
956 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
957 if (thresholds == (const char *) NULL)
959 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
961 if (IsGrayColorspace(image->colorspace) != MagickFalse)
962 (void) SetImageColorspace(image,sRGBColorspace,exception);
963 GetPixelInfo(image,&threshold);
964 flags=ParseGeometry(thresholds,&geometry_info);
965 threshold.red=geometry_info.rho;
966 threshold.green=geometry_info.rho;
967 threshold.blue=geometry_info.rho;
968 threshold.black=geometry_info.rho;
969 threshold.alpha=100.0;
970 if ((flags & SigmaValue) != 0)
971 threshold.green=geometry_info.sigma;
972 if ((flags & XiValue) != 0)
973 threshold.blue=geometry_info.xi;
974 if ((flags & PsiValue) != 0)
975 threshold.alpha=geometry_info.psi;
976 if (threshold.colorspace == CMYKColorspace)
978 if ((flags & PsiValue) != 0)
979 threshold.black=geometry_info.psi;
980 if ((flags & ChiValue) != 0)
981 threshold.alpha=geometry_info.chi;
983 if ((flags & PercentValue) != 0)
985 threshold.red*=(MagickRealType) (QuantumRange/100.0);
986 threshold.green*=(MagickRealType) (QuantumRange/100.0);
987 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
988 threshold.black*=(MagickRealType) (QuantumRange/100.0);
989 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
992 White threshold image.
996 image_view=AcquireAuthenticCacheView(image,exception);
997 #if defined(MAGICKCORE_OPENMP_SUPPORT)
998 #pragma omp parallel for schedule(static,4) shared(progress,status) \
999 magick_threads(image,image,image->rows,1)
1001 for (y=0; y < (ssize_t) image->rows; y++)
1009 if (status == MagickFalse)
1011 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1012 if (q == (Quantum *) NULL)
1017 for (x=0; x < (ssize_t) image->columns; x++)
1025 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1027 q+=GetPixelChannels(image);
1030 pixel=GetPixelIntensity(image,q);
1031 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1033 PixelChannel channel = GetPixelChannelChannel(image,i);
1034 PixelTrait traits = GetPixelChannelTraits(image,channel);
1035 if ((traits & UpdatePixelTrait) == 0)
1037 if (image->channel_mask != DefaultChannels)
1038 pixel=(double) q[i];
1039 if (pixel < GetPixelInfoChannel(&threshold,channel))
1042 q+=GetPixelChannels(image);
1044 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1046 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1051 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1052 #pragma omp critical (MagickCore_BlackThresholdImage)
1054 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1056 if (proceed == MagickFalse)
1060 image_view=DestroyCacheView(image_view);
1065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069 % C l a m p I m a g e %
1073 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1075 % ClampImage() set each pixel whose value is below zero to zero and any the
1076 % pixel whose value is above the quantum range to the quantum range (e.g.
1077 % 65535) otherwise the pixel value remains unchanged.
1079 % The format of the ClampImage method is:
1081 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
1083 % A description of each parameter follows:
1085 % o image: the image.
1087 % o exception: return any errors or warnings in this structure.
1091 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
1093 #define ClampImageTag "Clamp/Image"
1107 assert(image != (Image *) NULL);
1108 assert(image->signature == MagickCoreSignature);
1109 if (image->debug != MagickFalse)
1110 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1111 if (image->storage_class == PseudoClass)
1120 for (i=0; i < (ssize_t) image->colors; i++)
1122 q->red=(double) ClampPixel(q->red);
1123 q->green=(double) ClampPixel(q->green);
1124 q->blue=(double) ClampPixel(q->blue);
1125 q->alpha=(double) ClampPixel(q->alpha);
1128 return(SyncImage(image,exception));
1135 image_view=AcquireAuthenticCacheView(image,exception);
1136 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1137 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1138 magick_threads(image,image,image->rows,1)
1140 for (y=0; y < (ssize_t) image->rows; y++)
1148 if (status == MagickFalse)
1150 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1151 if (q == (Quantum *) NULL)
1156 for (x=0; x < (ssize_t) image->columns; x++)
1161 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1163 q+=GetPixelChannels(image);
1166 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1168 PixelChannel channel = GetPixelChannelChannel(image,i);
1169 PixelTrait traits = GetPixelChannelTraits(image,channel);
1170 if ((traits & UpdatePixelTrait) == 0)
1172 q[i]=ClampPixel((MagickRealType) q[i]);
1174 q+=GetPixelChannels(image);
1176 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1178 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1183 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1184 #pragma omp critical (MagickCore_ClampImage)
1186 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
1187 if (proceed == MagickFalse)
1191 image_view=DestroyCacheView(image_view);
1196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1200 % D e s t r o y T h r e s h o l d M a p %
1204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1206 % DestroyThresholdMap() de-allocate the given ThresholdMap
1208 % The format of the ListThresholdMaps method is:
1210 % ThresholdMap *DestroyThresholdMap(Threshold *map)
1212 % A description of each parameter follows.
1214 % o map: Pointer to the Threshold map to destroy
1217 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
1219 assert(map != (ThresholdMap *) NULL);
1220 if (map->map_id != (char *) NULL)
1221 map->map_id=DestroyString(map->map_id);
1222 if (map->description != (char *) NULL)
1223 map->description=DestroyString(map->description);
1224 if (map->levels != (ssize_t *) NULL)
1225 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1226 map=(ThresholdMap *) RelinquishMagickMemory(map);
1231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1235 % G e t T h r e s h o l d M a p %
1239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1241 % GetThresholdMap() loads and searches one or more threshold map files for the
1242 % map matching the given name or alias.
1244 % The format of the GetThresholdMap method is:
1246 % ThresholdMap *GetThresholdMap(const char *map_id,
1247 % ExceptionInfo *exception)
1249 % A description of each parameter follows.
1251 % o map_id: ID of the map to look for.
1253 % o exception: return any errors or warnings in this structure.
1256 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1257 ExceptionInfo *exception)
1262 map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
1263 if (map != (ThresholdMap *) NULL)
1265 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
1273 options=GetConfigureOptions(ThresholdsFilename,exception);
1274 option=(const StringInfo *) GetNextValueInLinkedList(options);
1275 while (option != (const StringInfo *) NULL)
1277 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1278 GetStringInfoPath(option),map_id,exception);
1279 if (map != (ThresholdMap *) NULL)
1281 option=(const StringInfo *) GetNextValueInLinkedList(options);
1283 options=DestroyConfigureOptions(options);
1290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1294 + G e t T h r e s h o l d M a p F i l e %
1298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1300 % GetThresholdMapFile() look for a given threshold map name or alias in the
1301 % given XML file data, and return the allocated the map when found.
1303 % The format of the ListThresholdMaps method is:
1305 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
1306 % const char *map_id,ExceptionInfo *exception)
1308 % A description of each parameter follows.
1310 % o xml: The threshold map list in XML format.
1312 % o filename: The threshold map XML filename.
1314 % o map_id: ID of the map to look for in XML list.
1316 % o exception: return any errors or warnings in this structure.
1319 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
1320 const char *map_id,ExceptionInfo *exception)
1344 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1345 "Loading threshold map file \"%s\" ...",filename);
1346 map=(ThresholdMap *) NULL;
1347 thresholds=NewXMLTree(xml,exception);
1348 if (thresholds == (XMLTreeInfo *) NULL)
1350 for (threshold=GetXMLTreeChild(thresholds,"threshold");
1351 threshold != (XMLTreeInfo *) NULL;
1352 threshold=GetNextXMLTreeTag(threshold))
1354 attribute=GetXMLTreeAttribute(threshold,"map");
1355 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1357 attribute=GetXMLTreeAttribute(threshold,"alias");
1358 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1361 if (threshold == (XMLTreeInfo *) NULL)
1363 thresholds=DestroyXMLTree(thresholds);
1366 description=GetXMLTreeChild(threshold,"description");
1367 if (description == (XMLTreeInfo *) NULL)
1369 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1370 "XmlMissingElement", "<description>, map \"%s\"",map_id);
1371 thresholds=DestroyXMLTree(thresholds);
1374 levels=GetXMLTreeChild(threshold,"levels");
1375 if (levels == (XMLTreeInfo *) NULL)
1377 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1378 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
1379 thresholds=DestroyXMLTree(thresholds);
1382 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
1383 if (map == (ThresholdMap *) NULL)
1384 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1385 map->map_id=(char *) NULL;
1386 map->description=(char *) NULL;
1387 map->levels=(ssize_t *) NULL;
1388 attribute=GetXMLTreeAttribute(threshold,"map");
1389 if (attribute != (char *) NULL)
1390 map->map_id=ConstantString(attribute);
1391 content=GetXMLTreeContent(description);
1392 if (content != (char *) NULL)
1393 map->description=ConstantString(content);
1394 attribute=GetXMLTreeAttribute(levels,"width");
1395 if (attribute == (char *) NULL)
1397 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1398 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1399 thresholds=DestroyXMLTree(thresholds);
1400 map=DestroyThresholdMap(map);
1403 map->width=StringToUnsignedLong(attribute);
1404 if (map->width == 0)
1406 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1407 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1408 thresholds=DestroyXMLTree(thresholds);
1409 map=DestroyThresholdMap(map);
1412 attribute=GetXMLTreeAttribute(levels,"height");
1413 if (attribute == (char *) NULL)
1415 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1416 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1417 thresholds=DestroyXMLTree(thresholds);
1418 map=DestroyThresholdMap(map);
1421 map->height=StringToUnsignedLong(attribute);
1422 if (map->height == 0)
1424 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1425 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1426 thresholds=DestroyXMLTree(thresholds);
1427 map=DestroyThresholdMap(map);
1430 attribute=GetXMLTreeAttribute(levels,"divisor");
1431 if (attribute == (char *) NULL)
1433 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1434 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1435 thresholds=DestroyXMLTree(thresholds);
1436 map=DestroyThresholdMap(map);
1439 map->divisor=(ssize_t) StringToLong(attribute);
1440 if (map->divisor < 2)
1442 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1443 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1444 thresholds=DestroyXMLTree(thresholds);
1445 map=DestroyThresholdMap(map);
1448 content=GetXMLTreeContent(levels);
1449 if (content == (char *) NULL)
1451 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1452 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1453 thresholds=DestroyXMLTree(thresholds);
1454 map=DestroyThresholdMap(map);
1457 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1458 sizeof(*map->levels));
1459 if (map->levels == (ssize_t *) NULL)
1460 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1461 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1463 map->levels[i]=(ssize_t) strtol(content,&p,10);
1466 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1467 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1468 thresholds=DestroyXMLTree(thresholds);
1469 map=DestroyThresholdMap(map);
1472 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1474 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1475 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1476 (double) map->levels[i],map_id);
1477 thresholds=DestroyXMLTree(thresholds);
1478 map=DestroyThresholdMap(map);
1483 value=(double) strtol(content,&p,10);
1487 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1488 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1489 thresholds=DestroyXMLTree(thresholds);
1490 map=DestroyThresholdMap(map);
1493 thresholds=DestroyXMLTree(thresholds);
1498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1502 + L i s t T h r e s h o l d M a p F i l e %
1506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1508 % ListThresholdMapFile() lists the threshold maps and their descriptions
1509 % in the given XML file data.
1511 % The format of the ListThresholdMaps method is:
1513 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1514 % const char *filename,ExceptionInfo *exception)
1516 % A description of each parameter follows.
1518 % o file: An pointer to the output FILE.
1520 % o xml: The threshold map list in XML format.
1522 % o filename: The threshold map XML filename.
1524 % o exception: return any errors or warnings in this structure.
1527 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1528 const char *filename,ExceptionInfo *exception)
1540 assert( xml != (char *) NULL );
1541 assert( file != (FILE *) NULL );
1542 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1543 "Loading threshold map file \"%s\" ...",filename);
1544 thresholds=NewXMLTree(xml,exception);
1545 if ( thresholds == (XMLTreeInfo *) NULL )
1546 return(MagickFalse);
1547 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1548 (void) FormatLocaleFile(file,
1549 "----------------------------------------------------\n");
1550 threshold=GetXMLTreeChild(thresholds,"threshold");
1551 for ( ; threshold != (XMLTreeInfo *) NULL;
1552 threshold=GetNextXMLTreeTag(threshold))
1554 map=GetXMLTreeAttribute(threshold,"map");
1555 if (map == (char *) NULL)
1557 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1558 "XmlMissingAttribute", "<map>");
1559 thresholds=DestroyXMLTree(thresholds);
1560 return(MagickFalse);
1562 alias=GetXMLTreeAttribute(threshold,"alias");
1563 description=GetXMLTreeChild(threshold,"description");
1564 if (description == (XMLTreeInfo *) NULL)
1566 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1567 "XmlMissingElement", "<description>, map \"%s\"",map);
1568 thresholds=DestroyXMLTree(thresholds);
1569 return(MagickFalse);
1571 content=GetXMLTreeContent(description);
1572 if (content == (char *) NULL)
1574 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1575 "XmlMissingContent", "<description>, map \"%s\"", map);
1576 thresholds=DestroyXMLTree(thresholds);
1577 return(MagickFalse);
1579 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1582 thresholds=DestroyXMLTree(thresholds);
1587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1591 % L i s t T h r e s h o l d M a p s %
1595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1597 % ListThresholdMaps() lists the threshold maps and their descriptions
1598 % as defined by "threshold.xml" to a file.
1600 % The format of the ListThresholdMaps method is:
1602 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1604 % A description of each parameter follows.
1606 % o file: An pointer to the output FILE.
1608 % o exception: return any errors or warnings in this structure.
1611 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1612 ExceptionInfo *exception)
1624 if (file == (FILE *) NULL)
1626 options=GetConfigureOptions(ThresholdsFilename,exception);
1627 (void) FormatLocaleFile(file,
1628 "\n Threshold Maps for Ordered Dither Operations\n");
1629 option=(const StringInfo *) GetNextValueInLinkedList(options);
1630 while (option != (const StringInfo *) NULL)
1632 (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1633 status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1634 GetStringInfoPath(option),exception);
1635 option=(const StringInfo *) GetNextValueInLinkedList(options);
1637 options=DestroyConfigureOptions(options);
1638 return(status != 0 ? MagickTrue : MagickFalse);
1642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646 % O r d e r e d D i t h e r I m a g e %
1650 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1652 % OrderedDitherImage() will perform a ordered dither based on a number
1653 % of pre-defined dithering threshold maps, but over multiple intensity
1654 % levels, which can be different for different channels, according to the
1657 % The format of the OrderedDitherImage method is:
1659 % MagickBooleanType OrderedDitherImage(Image *image,
1660 % const char *threshold_map,ExceptionInfo *exception)
1662 % A description of each parameter follows:
1664 % o image: the image.
1666 % o threshold_map: A string containing the name of the threshold dither
1667 % map to use, followed by zero or more numbers representing the number
1668 % of color levels tho dither between.
1670 % Any level number less than 2 will be equivalent to 2, and means only
1671 % binary dithering will be applied to each color channel.
1673 % No numbers also means a 2 level (bitmap) dither will be applied to all
1674 % channels, while a single number is the number of levels applied to each
1675 % channel in sequence. More numbers will be applied in turn to each of
1676 % the color channels.
1678 % For example: "o3x3,6" will generate a 6 level posterization of the
1679 % image with a ordered 3x3 diffused pixel dither being applied between
1680 % each level. While checker,8,8,4 will produce a 332 colormaped image
1681 % with only a single checkerboard hash pattern (50% grey) between each
1682 % color level, to basically double the number of color levels with
1683 % a bare minimim of dithering.
1685 % o exception: return any errors or warnings in this structure.
1688 MagickExport MagickBooleanType OrderedDitherImage(Image *image,
1689 const char *threshold_map,ExceptionInfo *exception)
1691 #define DitherImageTag "Dither/Image"
1697 token[MagickPathExtent];
1703 levels[CompositePixelChannel];
1720 assert(image != (Image *) NULL);
1721 assert(image->signature == MagickCoreSignature);
1722 if (image->debug != MagickFalse)
1723 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1724 assert(exception != (ExceptionInfo *) NULL);
1725 assert(exception->signature == MagickCoreSignature);
1726 if (threshold_map == (const char *) NULL)
1728 p=(char *) threshold_map;
1729 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1733 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1736 if ((p-threshold_map) >= (MagickPathExtent-1))
1738 token[p-threshold_map]=(*p);
1741 token[p-threshold_map]='\0';
1742 map=GetThresholdMap(token,exception);
1743 if (map == (ThresholdMap *) NULL)
1745 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1746 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1747 return(MagickFalse);
1749 for (i=0; i < MaxPixelChannels; i++)
1751 p=strchr((char *) threshold_map,',');
1752 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1754 GetNextToken(p,&p,MagickPathExtent,token);
1755 for (i=0; (i < MaxPixelChannels); i++)
1756 levels[i]=StringToDouble(token,(char **) NULL);
1757 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1759 GetNextToken(p,&p,MagickPathExtent,token);
1761 GetNextToken(p,&p,MagickPathExtent,token);
1762 levels[i]=StringToDouble(token,(char **) NULL);
1765 for (i=0; i < MaxPixelChannels; i++)
1766 if (fabs(levels[i]) >= 1)
1768 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1769 return(MagickFalse);
1772 image_view=AcquireAuthenticCacheView(image,exception);
1773 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1774 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1775 magick_threads(image,image,image->rows,1)
1777 for (y=0; y < (ssize_t) image->rows; y++)
1785 if (status == MagickFalse)
1787 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1788 if (q == (Quantum *) NULL)
1793 for (x=0; x < (ssize_t) image->columns; x++)
1802 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1804 q+=GetPixelChannels(image);
1807 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1813 PixelChannel channel = GetPixelChannelChannel(image,i);
1814 PixelTrait traits = GetPixelChannelTraits(image,channel);
1815 if ((traits & UpdatePixelTrait) == 0)
1817 if (fabs(levels[n]) < MagickEpsilon)
1822 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1823 level=threshold/(map->divisor-1);
1824 threshold-=level*(map->divisor-1);
1825 q[i]=ClampToQuantum((double) (level+(threshold >=
1826 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1827 QuantumRange/levels[n]);
1830 q+=GetPixelChannels(image);
1832 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1834 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1839 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1840 #pragma omp critical (MagickCore_OrderedDitherImage)
1842 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1843 if (proceed == MagickFalse)
1847 image_view=DestroyCacheView(image_view);
1848 map=DestroyThresholdMap(map);
1853 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1857 % P e r c e p t i b l e I m a g e %
1861 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1863 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1864 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1867 % The format of the PerceptibleImage method is:
1869 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1870 % ExceptionInfo *exception)
1872 % A description of each parameter follows:
1874 % o image: the image.
1876 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1878 % o exception: return any errors or warnings in this structure.
1882 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1883 const double epsilon)
1888 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1889 if ((sign*quantum) >= epsilon)
1891 return((Quantum) (sign*epsilon));
1894 MagickExport MagickBooleanType PerceptibleImage(Image *image,
1895 const double epsilon,ExceptionInfo *exception)
1897 #define PerceptibleImageTag "Perceptible/Image"
1911 assert(image != (Image *) NULL);
1912 assert(image->signature == MagickCoreSignature);
1913 if (image->debug != MagickFalse)
1914 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1915 if (image->storage_class == PseudoClass)
1924 for (i=0; i < (ssize_t) image->colors; i++)
1926 q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1928 q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1930 q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1932 q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1936 return(SyncImage(image,exception));
1943 image_view=AcquireAuthenticCacheView(image,exception);
1944 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1945 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1946 magick_threads(image,image,image->rows,1)
1948 for (y=0; y < (ssize_t) image->rows; y++)
1956 if (status == MagickFalse)
1958 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1959 if (q == (Quantum *) NULL)
1964 for (x=0; x < (ssize_t) image->columns; x++)
1969 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
1971 q+=GetPixelChannels(image);
1974 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1976 PixelChannel channel = GetPixelChannelChannel(image,i);
1977 PixelTrait traits = GetPixelChannelTraits(image,channel);
1978 if (traits == UndefinedPixelTrait)
1980 q[i]=PerceptibleThreshold(q[i],epsilon);
1982 q+=GetPixelChannels(image);
1984 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1986 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1991 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1992 #pragma omp critical (MagickCore_PerceptibleImage)
1994 proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1995 if (proceed == MagickFalse)
1999 image_view=DestroyCacheView(image_view);
2004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2008 % R a n d o m T h r e s h o l d I m a g e %
2012 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2014 % RandomThresholdImage() changes the value of individual pixels based on the
2015 % intensity of each pixel compared to a random threshold. The result is a
2016 % low-contrast, two color image.
2018 % The format of the RandomThresholdImage method is:
2020 % MagickBooleanType RandomThresholdImage(Image *image,
2021 % const char *thresholds,ExceptionInfo *exception)
2023 % A description of each parameter follows:
2025 % o image: the image.
2027 % o low,high: Specify the high and low thresholds. These values range from
2028 % 0 to QuantumRange.
2030 % o exception: return any errors or warnings in this structure.
2033 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
2034 const double min_threshold, const double max_threshold,ExceptionInfo *exception)
2036 #define ThresholdImageTag "Threshold/Image"
2051 **magick_restrict random_info;
2056 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2061 assert(image != (Image *) NULL);
2062 assert(image->signature == MagickCoreSignature);
2063 if (image->debug != MagickFalse)
2064 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2065 assert(exception != (ExceptionInfo *) NULL);
2066 assert(exception->signature == MagickCoreSignature);
2067 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2068 return(MagickFalse);
2069 GetPixelInfo(image,&threshold);
2071 Random threshold image.
2075 random_info=AcquireRandomInfoThreadSet();
2076 image_view=AcquireAuthenticCacheView(image,exception);
2077 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2078 key=GetRandomSecretKey(random_info[0]);
2079 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2080 magick_threads(image,image,image->rows,key == ~0UL)
2082 for (y=0; y < (ssize_t) image->rows; y++)
2085 id = GetOpenMPThreadId();
2093 if (status == MagickFalse)
2095 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2096 if (q == (Quantum *) NULL)
2101 for (x=0; x < (ssize_t) image->columns; x++)
2106 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2108 q+=GetPixelChannels(image);
2111 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2116 PixelChannel channel = GetPixelChannelChannel(image,i);
2117 PixelTrait traits = GetPixelChannelTraits(image,channel);
2118 if ((traits & UpdatePixelTrait) == 0)
2120 if ((double) q[i] < min_threshold)
2121 threshold=min_threshold;
2123 if ((double) q[i] > max_threshold)
2124 threshold=max_threshold;
2126 threshold=(double) (QuantumRange*
2127 GetPseudoRandomValue(random_info[id]));
2128 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
2130 q+=GetPixelChannels(image);
2132 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2134 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2139 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2140 #pragma omp critical (MagickCore_RandomThresholdImage)
2142 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2144 if (proceed == MagickFalse)
2148 image_view=DestroyCacheView(image_view);
2149 random_info=DestroyRandomInfoThreadSet(random_info);
2154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2158 % W h i t e T h r e s h o l d I m a g e %
2162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2164 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
2165 % the threshold into white while leaving all pixels at or below the threshold
2168 % The format of the WhiteThresholdImage method is:
2170 % MagickBooleanType WhiteThresholdImage(Image *image,
2171 % const char *threshold,ExceptionInfo *exception)
2173 % A description of each parameter follows:
2175 % o image: the image.
2177 % o threshold: Define the threshold value.
2179 % o exception: return any errors or warnings in this structure.
2182 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
2183 const char *thresholds,ExceptionInfo *exception)
2185 #define ThresholdImageTag "Threshold/Image"
2208 assert(image != (Image *) NULL);
2209 assert(image->signature == MagickCoreSignature);
2210 if (image->debug != MagickFalse)
2211 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2212 if (thresholds == (const char *) NULL)
2214 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2215 return(MagickFalse);
2216 if (IsGrayColorspace(image->colorspace) != MagickFalse)
2217 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2218 GetPixelInfo(image,&threshold);
2219 flags=ParseGeometry(thresholds,&geometry_info);
2220 threshold.red=geometry_info.rho;
2221 threshold.green=geometry_info.rho;
2222 threshold.blue=geometry_info.rho;
2223 threshold.black=geometry_info.rho;
2224 threshold.alpha=100.0;
2225 if ((flags & SigmaValue) != 0)
2226 threshold.green=geometry_info.sigma;
2227 if ((flags & XiValue) != 0)
2228 threshold.blue=geometry_info.xi;
2229 if ((flags & PsiValue) != 0)
2230 threshold.alpha=geometry_info.psi;
2231 if (threshold.colorspace == CMYKColorspace)
2233 if ((flags & PsiValue) != 0)
2234 threshold.black=geometry_info.psi;
2235 if ((flags & ChiValue) != 0)
2236 threshold.alpha=geometry_info.chi;
2238 if ((flags & PercentValue) != 0)
2240 threshold.red*=(MagickRealType) (QuantumRange/100.0);
2241 threshold.green*=(MagickRealType) (QuantumRange/100.0);
2242 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
2243 threshold.black*=(MagickRealType) (QuantumRange/100.0);
2244 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
2247 White threshold image.
2251 image_view=AcquireAuthenticCacheView(image,exception);
2252 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2253 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2254 magick_threads(image,image,image->rows,1)
2256 for (y=0; y < (ssize_t) image->rows; y++)
2264 if (status == MagickFalse)
2266 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2267 if (q == (Quantum *) NULL)
2272 for (x=0; x < (ssize_t) image->columns; x++)
2280 if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
2282 q+=GetPixelChannels(image);
2285 pixel=GetPixelIntensity(image,q);
2286 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2288 PixelChannel channel = GetPixelChannelChannel(image,i);
2289 PixelTrait traits = GetPixelChannelTraits(image,channel);
2290 if ((traits & UpdatePixelTrait) == 0)
2292 if (image->channel_mask != DefaultChannels)
2293 pixel=(double) q[i];
2294 if (pixel > GetPixelInfoChannel(&threshold,channel))
2297 q+=GetPixelChannels(image);
2299 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2301 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2306 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2307 #pragma omp critical (MagickCore_WhiteThresholdImage)
2309 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2311 if (proceed == MagickFalse)
2315 image_view=DestroyCacheView(image_view);