MagickCore  7.0.3
threshold.c
Go to the documentation of this file.
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
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 %
11 % %
12 % %
13 % MagickCore Image Threshold Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % October 1996 %
18 % %
19 % %
20 % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
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. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 
40 /*
41  Include declarations.
42 */
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"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colorspace.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"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/gem.h"
62 #include "MagickCore/geometry.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/memory_.h"
68 #include "MagickCore/monitor.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/option.h"
74 #include "MagickCore/quantize.h"
75 #include "MagickCore/quantum.h"
77 #include "MagickCore/random_.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/segment.h"
82 #include "MagickCore/shear.h"
84 #include "MagickCore/string_.h"
87 #include "MagickCore/threshold.h"
88 #include "MagickCore/token.h"
89 #include "MagickCore/transform.h"
90 #include "MagickCore/xml-tree.h"
92 
93 /*
94  Define declarations.
95 */
96 #define ThresholdsFilename "thresholds.xml"
97 
98 /*
99  Typedef declarations.
100 */
102 {
103  char
105  *description;
106 
107  size_t
109  height;
110 
111  ssize_t
113  *levels;
114 };
115 
116 /*
117  Static declarations.
118 */
119 static const char
121  "<?xml version=\"1.0\"?>"
122  "<thresholds>"
123  " <threshold map=\"threshold\" alias=\"1x1\">"
124  " <description>Threshold 1x1 (non-dither)</description>"
125  " <levels width=\"1\" height=\"1\" divisor=\"2\">"
126  " 1"
127  " </levels>"
128  " </threshold>"
129  " <threshold map=\"checks\" alias=\"2x1\">"
130  " <description>Checkerboard 2x1 (dither)</description>"
131  " <levels width=\"2\" height=\"2\" divisor=\"3\">"
132  " 1 2"
133  " 2 1"
134  " </levels>"
135  " </threshold>"
136  "</thresholds>";
137 
138 /*
139  Forward declarations.
140 */
141 static ThresholdMap
142  *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
143 
144 /*
145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 % %
147 % %
148 % %
149 % A d a p t i v e T h r e s h o l d I m a g e %
150 % %
151 % %
152 % %
153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 %
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.
159 %
160 % The format of the AdaptiveThresholdImage method is:
161 %
162 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
163 % const size_t height,const double bias,ExceptionInfo *exception)
164 %
165 % A description of each parameter follows:
166 %
167 % o image: the image.
168 %
169 % o width: the width of the local neighborhood.
170 %
171 % o height: the height of the local neighborhood.
172 %
173 % o bias: the mean bias.
174 %
175 % o exception: return any errors or warnings in this structure.
176 %
177 */
179  const size_t width,const size_t height,const double bias,
180  ExceptionInfo *exception)
181 {
182 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
183 
184  CacheView
185  *image_view,
186  *threshold_view;
187 
188  Image
189  *threshold_image;
190 
192  status;
193 
195  progress;
196 
198  number_pixels;
199 
200  ssize_t
201  y;
202 
203  /*
204  Initialize threshold image attributes.
205  */
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,0,0,MagickTrue,exception);
213  if (threshold_image == (Image *) NULL)
214  return((Image *) NULL);
215  status=SetImageStorageClass(threshold_image,DirectClass,exception);
216  if (status == MagickFalse)
217  {
218  threshold_image=DestroyImage(threshold_image);
219  return((Image *) NULL);
220  }
221  /*
222  Threshold image.
223  */
224  status=MagickTrue;
225  progress=0;
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) shared(progress,status) \
231  magick_number_threads(image,threshold_image,image->rows,1)
232 #endif
233  for (y=0; y < (ssize_t) image->rows; y++)
234  {
235  double
236  channel_bias[MaxPixelChannels],
237  channel_sum[MaxPixelChannels];
238 
239  register const Quantum
240  *magick_restrict p,
241  *magick_restrict pixels;
242 
243  register Quantum
244  *magick_restrict q;
245 
246  register ssize_t
247  i,
248  x;
249 
250  ssize_t
251  center,
252  u,
253  v;
254 
255  if (status == MagickFalse)
256  continue;
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,
260  1,exception);
261  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
262  {
263  status=MagickFalse;
264  continue;
265  }
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++)
269  {
270  PixelChannel channel = GetPixelChannelChannel(image,i);
271  PixelTrait traits = GetPixelChannelTraits(image,channel);
272  PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
273  channel);
274  if ((traits == UndefinedPixelTrait) ||
275  (threshold_traits == UndefinedPixelTrait))
276  continue;
277  if ((threshold_traits & CopyPixelTrait) != 0)
278  {
279  SetPixelChannel(threshold_image,channel,p[center+i],q);
280  continue;
281  }
282  pixels=p;
283  channel_bias[channel]=0.0;
284  channel_sum[channel]=0.0;
285  for (v=0; v < (ssize_t) height; v++)
286  {
287  for (u=0; u < (ssize_t) width; u++)
288  {
289  if (u == (ssize_t) (width-1))
290  channel_bias[channel]+=pixels[i];
291  channel_sum[channel]+=pixels[i];
292  pixels+=GetPixelChannels(image);
293  }
294  pixels+=GetPixelChannels(image)*image->columns;
295  }
296  }
297  for (x=0; x < (ssize_t) image->columns; x++)
298  {
299  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
300  {
301  double
302  mean;
303 
304  PixelChannel channel = GetPixelChannelChannel(image,i);
305  PixelTrait traits = GetPixelChannelTraits(image,channel);
306  PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
307  channel);
308  if ((traits == UndefinedPixelTrait) ||
309  (threshold_traits == UndefinedPixelTrait))
310  continue;
311  if ((threshold_traits & CopyPixelTrait) != 0)
312  {
313  SetPixelChannel(threshold_image,channel,p[center+i],q);
314  continue;
315  }
316  channel_sum[channel]-=channel_bias[channel];
317  channel_bias[channel]=0.0;
318  pixels=p;
319  for (v=0; v < (ssize_t) height; v++)
320  {
321  channel_bias[channel]+=pixels[i];
322  pixels+=(width-1)*GetPixelChannels(image);
323  channel_sum[channel]+=pixels[i];
324  pixels+=GetPixelChannels(image)*(image->columns+1);
325  }
326  mean=(double) (channel_sum[channel]/number_pixels+bias);
327  SetPixelChannel(threshold_image,channel,(Quantum) ((double)
328  p[center+i] <= mean ? 0 : QuantumRange),q);
329  }
330  p+=GetPixelChannels(image);
331  q+=GetPixelChannels(threshold_image);
332  }
333  if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
334  status=MagickFalse;
335  if (image->progress_monitor != (MagickProgressMonitor) NULL)
336  {
338  proceed;
339 
340 #if defined(MAGICKCORE_OPENMP_SUPPORT)
341  #pragma omp atomic
342 #endif
343  progress++;
344  proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress,
345  image->rows);
346  if (proceed == MagickFalse)
347  status=MagickFalse;
348  }
349  }
350  threshold_image->type=image->type;
351  threshold_view=DestroyCacheView(threshold_view);
352  image_view=DestroyCacheView(image_view);
353  if (status == MagickFalse)
354  threshold_image=DestroyImage(threshold_image);
355  return(threshold_image);
356 }
357 
358 /*
359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360 % %
361 % %
362 % %
363 % A u t o T h r e s h o l d I m a g e %
364 % %
365 % %
366 % %
367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368 %
369 % AutoThresholdImage() automatically performs image thresholding
370 % dependent on which method you specify.
371 %
372 % The format of the AutoThresholdImage method is:
373 %
374 % MagickBooleanType AutoThresholdImage(Image *image,
375 % const AutoThresholdMethod method,ExceptionInfo *exception)
376 %
377 % A description of each parameter follows:
378 %
379 % o image: The image to auto-threshold.
380 %
381 % o method: choose from Kapur, OTSU, or Triangle.
382 %
383 % o exception: return any errors or warnings in this structure.
384 %
385 */
386 
387 static double KapurThreshold(const Image *image,const double *histogram,
388  ExceptionInfo *exception)
389 {
390 #define MaxIntensity 255
391 
392  double
393  *black_entropy,
394  *cumulative_histogram,
395  entropy,
396  epsilon,
397  maximum_entropy,
398  *white_entropy;
399 
400  register ssize_t
401  i,
402  j;
403 
404  size_t
405  threshold;
406 
407  /*
408  Compute optimal threshold from the entopy of the histogram.
409  */
410  cumulative_histogram=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
411  sizeof(*cumulative_histogram));
412  black_entropy=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
413  sizeof(*black_entropy));
414  white_entropy=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
415  sizeof(*white_entropy));
416  if ((cumulative_histogram == (double *) NULL) ||
417  (black_entropy == (double *) NULL) || (white_entropy == (double *) NULL))
418  {
419  if (white_entropy != (double *) NULL)
420  white_entropy=(double *) RelinquishMagickMemory(white_entropy);
421  if (black_entropy != (double *) NULL)
422  black_entropy=(double *) RelinquishMagickMemory(black_entropy);
423  if (cumulative_histogram != (double *) NULL)
424  cumulative_histogram=(double *)
425  RelinquishMagickMemory(cumulative_histogram);
426  (void) ThrowMagickException(exception,GetMagickModule(),
427  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
428  return(-1.0);
429  }
430  /*
431  Entropy for black and white parts of the histogram.
432  */
433  cumulative_histogram[0]=histogram[0];
434  for (i=1; i <= MaxIntensity; i++)
435  cumulative_histogram[i]=cumulative_histogram[i-1]+histogram[i];
436  epsilon=MagickMinimumValue;
437  for (j=0; j <= MaxIntensity; j++)
438  {
439  /*
440  Black entropy.
441  */
442  black_entropy[j]=0.0;
443  if (cumulative_histogram[j] > epsilon)
444  {
445  entropy=0.0;
446  for (i=0; i <= j; i++)
447  if (histogram[i] > epsilon)
448  entropy-=histogram[i]/cumulative_histogram[j]*
449  log(histogram[i]/cumulative_histogram[j]);
450  black_entropy[j]=entropy;
451  }
452  /*
453  White entropy.
454  */
455  white_entropy[j]=0.0;
456  if ((1.0-cumulative_histogram[j]) > epsilon)
457  {
458  entropy=0.0;
459  for (i=j+1; i <= MaxIntensity; i++)
460  if (histogram[i] > epsilon)
461  entropy-=histogram[i]/(1.0-cumulative_histogram[j])*
462  log(histogram[i]/(1.0-cumulative_histogram[j]));
463  white_entropy[j]=entropy;
464  }
465  }
466  /*
467  Find histogram bin with maximum entropy.
468  */
469  maximum_entropy=black_entropy[0]+white_entropy[0];
470  threshold=0;
471  for (j=1; j <= MaxIntensity; j++)
472  if ((black_entropy[j]+white_entropy[j]) > maximum_entropy)
473  {
474  maximum_entropy=black_entropy[j]+white_entropy[j];
475  threshold=(size_t) j;
476  }
477  /*
478  Free resources.
479  */
480  white_entropy=(double *) RelinquishMagickMemory(white_entropy);
481  black_entropy=(double *) RelinquishMagickMemory(black_entropy);
482  cumulative_histogram=(double *) RelinquishMagickMemory(cumulative_histogram);
483  return(100.0*threshold/MaxIntensity);
484 }
485 
486 static double OTSUThreshold(const Image *image,const double *histogram,
487  ExceptionInfo *exception)
488 {
489  double
490  max_sigma,
491  *myu,
492  *omega,
493  *probability,
494  *sigma,
495  threshold;
496 
497  register ssize_t
498  i;
499 
500  /*
501  Compute optimal threshold from maximization of inter-class variance.
502  */
503  myu=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*myu));
504  omega=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*omega));
505  probability=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
506  sizeof(*probability));
507  sigma=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*sigma));
508  if ((myu == (double *) NULL) || (omega == (double *) NULL) ||
509  (probability == (double *) NULL) || (sigma == (double *) NULL))
510  {
511  if (sigma != (double *) NULL)
512  sigma=(double *) RelinquishMagickMemory(sigma);
513  if (probability != (double *) NULL)
514  probability=(double *) RelinquishMagickMemory(probability);
515  if (omega != (double *) NULL)
516  omega=(double *) RelinquishMagickMemory(omega);
517  if (myu != (double *) NULL)
518  myu=(double *) RelinquishMagickMemory(myu);
519  (void) ThrowMagickException(exception,GetMagickModule(),
520  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
521  return(-1.0);
522  }
523  /*
524  Calculate probability density.
525  */
526  for (i=0; i <= (ssize_t) MaxIntensity; i++)
527  probability[i]=histogram[i];
528  /*
529  Generate probability of graylevels and mean value for separation.
530  */
531  omega[0]=probability[0];
532  myu[0]=0.0;
533  for (i=1; i <= (ssize_t) MaxIntensity; i++)
534  {
535  omega[i]=omega[i-1]+probability[i];
536  myu[i]=myu[i-1]+i*probability[i];
537  }
538  /*
539  Sigma maximization: inter-class variance and compute optimal threshold.
540  */
541  threshold=0;
542  max_sigma=0.0;
543  for (i=0; i < (ssize_t) MaxIntensity; i++)
544  {
545  sigma[i]=0.0;
546  if ((omega[i] != 0.0) && (omega[i] != 1.0))
547  sigma[i]=pow(myu[MaxIntensity]*omega[i]-myu[i],2.0)/(omega[i]*(1.0-
548  omega[i]));
549  if (sigma[i] > max_sigma)
550  {
551  max_sigma=sigma[i];
552  threshold=(double) i;
553  }
554  }
555  /*
556  Free resources.
557  */
558  myu=(double *) RelinquishMagickMemory(myu);
559  omega=(double *) RelinquishMagickMemory(omega);
560  probability=(double *) RelinquishMagickMemory(probability);
561  sigma=(double *) RelinquishMagickMemory(sigma);
562  return(100.0*threshold/MaxIntensity);
563 }
564 
565 static double TriangleThreshold(const double *histogram,
566  ExceptionInfo *exception)
567 {
568  double
569  a,
570  b,
571  c,
572  count,
573  distance,
574  inverse_ratio,
575  max_distance,
576  segment,
577  x1,
578  x2,
579  y1,
580  y2;
581 
582  register ssize_t
583  i;
584 
585  ssize_t
586  end,
587  max,
588  start,
589  threshold;
590 
591  /*
592  Compute optimal threshold with triangle algorithm.
593  */
594  (void) exception;
595  start=0; /* find start bin, first bin not zero count */
596  for (i=0; i <= (ssize_t) MaxIntensity; i++)
597  if (histogram[i] > 0.0)
598  {
599  start=i;
600  break;
601  }
602  end=0; /* find end bin, last bin not zero count */
603  for (i=(ssize_t) MaxIntensity; i >= 0; i--)
604  if (histogram[i] > 0.0)
605  {
606  end=i;
607  break;
608  }
609  max=0; /* find max bin, bin with largest count */
610  count=0.0;
611  for (i=0; i <= (ssize_t) MaxIntensity; i++)
612  if (histogram[i] > count)
613  {
614  max=i;
615  count=histogram[i];
616  }
617  /*
618  Compute threshold at split point.
619  */
620  x1=(double) max;
621  y1=histogram[max];
622  x2=(double) end;
623  if ((max-start) >= (end-max))
624  x2=(double) start;
625  y2=0.0;
626  a=y1-y2;
627  b=x2-x1;
628  c=(-1.0)*(a*x1+b*y1);
629  inverse_ratio=1.0/sqrt(a*a+b*b+c*c);
630  threshold=0;
631  max_distance=0.0;
632  if (x2 == (double) start)
633  for (i=start; i < max; i++)
634  {
635  segment=inverse_ratio*(a*i+b*histogram[i]+c);
636  distance=sqrt(segment*segment);
637  if ((distance > max_distance) && (segment > 0.0))
638  {
639  threshold=i;
640  max_distance=distance;
641  }
642  }
643  else
644  for (i=end; i > max; i--)
645  {
646  segment=inverse_ratio*(a*i+b*histogram[i]+c);
647  distance=sqrt(segment*segment);
648  if ((distance > max_distance) && (segment < 0.0))
649  {
650  threshold=i;
651  max_distance=distance;
652  }
653  }
654  return(100.0*threshold/MaxIntensity);
655 }
656 
658  const AutoThresholdMethod method,ExceptionInfo *exception)
659 {
660  CacheView
661  *image_view;
662 
663  char
664  property[MagickPathExtent];
665 
666  double
667  gamma,
668  *histogram,
669  sum,
670  threshold;
671 
673  status;
674 
675  register ssize_t
676  i;
677 
678  ssize_t
679  y;
680 
681  /*
682  Form histogram.
683  */
684  assert(image != (Image *) NULL);
685  assert(image->signature == MagickCoreSignature);
686  if (image->debug != MagickFalse)
687  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
688  histogram=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
689  sizeof(*histogram));
690  if (histogram == (double *) NULL)
691  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
692  image->filename);
693  status=MagickTrue;
694  (void) memset(histogram,0,(MaxIntensity+1UL)*sizeof(*histogram));
695  image_view=AcquireVirtualCacheView(image,exception);
696  for (y=0; y < (ssize_t) image->rows; y++)
697  {
698  register const Quantum
699  *magick_restrict p;
700 
701  register ssize_t
702  x;
703 
704  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
705  if (p == (const Quantum *) NULL)
706  break;
707  for (x=0; x < (ssize_t) image->columns; x++)
708  {
709  double intensity = GetPixelIntensity(image,p);
710  histogram[ScaleQuantumToChar(ClampToQuantum(intensity))]++;
711  p+=GetPixelChannels(image);
712  }
713  }
714  image_view=DestroyCacheView(image_view);
715  /*
716  Normalize histogram.
717  */
718  sum=0.0;
719  for (i=0; i <= (ssize_t) MaxIntensity; i++)
720  sum+=histogram[i];
721  gamma=PerceptibleReciprocal(sum);
722  for (i=0; i <= (ssize_t) MaxIntensity; i++)
723  histogram[i]=gamma*histogram[i];
724  /*
725  Discover threshold from histogram.
726  */
727  switch (method)
728  {
730  {
731  threshold=KapurThreshold(image,histogram,exception);
732  break;
733  }
734  case OTSUThresholdMethod:
735  default:
736  {
737  threshold=OTSUThreshold(image,histogram,exception);
738  break;
739  }
741  {
742  threshold=TriangleThreshold(histogram,exception);
743  break;
744  }
745  }
746  histogram=(double *) RelinquishMagickMemory(histogram);
747  if (threshold < 0.0)
748  status=MagickFalse;
749  if (status == MagickFalse)
750  return(MagickFalse);
751  /*
752  Threshold image.
753  */
754  (void) FormatLocaleString(property,MagickPathExtent,"%g%%",threshold);
755  (void) SetImageProperty(image,"auto-threshold:threshold",property,exception);
756  return(BilevelImage(image,QuantumRange*threshold/100.0,exception));
757 }
758 
759 /*
760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
761 % %
762 % %
763 % %
764 % B i l e v e l I m a g e %
765 % %
766 % %
767 % %
768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769 %
770 % BilevelImage() changes the value of individual pixels based on the
771 % intensity of each pixel channel. The result is a high-contrast image.
772 %
773 % More precisely each channel value of the image is 'thresholded' so that if
774 % it is equal to or less than the given value it is set to zero, while any
775 % value greater than that give is set to it maximum or QuantumRange.
776 %
777 % This function is what is used to implement the "-threshold" operator for
778 % the command line API.
779 %
780 % If the default channel setting is given the image is thresholded using just
781 % the gray 'intensity' of the image, rather than the individual channels.
782 %
783 % The format of the BilevelImage method is:
784 %
785 % MagickBooleanType BilevelImage(Image *image,const double threshold,
786 % ExceptionInfo *exception)
787 %
788 % A description of each parameter follows:
789 %
790 % o image: the image.
791 %
792 % o threshold: define the threshold values.
793 %
794 % o exception: return any errors or warnings in this structure.
795 %
796 % Aside: You can get the same results as operator using LevelImages()
797 % with the 'threshold' value for both the black_point and the white_point.
798 %
799 */
800 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
801  ExceptionInfo *exception)
802 {
803 #define ThresholdImageTag "Threshold/Image"
804 
805  CacheView
806  *image_view;
807 
809  status;
810 
812  progress;
813 
814  ssize_t
815  y;
816 
817  assert(image != (Image *) NULL);
818  assert(image->signature == MagickCoreSignature);
819  if (image->debug != MagickFalse)
820  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
821  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
822  return(MagickFalse);
823  if (IsGrayColorspace(image->colorspace) != MagickFalse)
824  (void) SetImageColorspace(image,sRGBColorspace,exception);
825  /*
826  Bilevel threshold image.
827  */
828  status=MagickTrue;
829  progress=0;
830  image_view=AcquireAuthenticCacheView(image,exception);
831 #if defined(MAGICKCORE_OPENMP_SUPPORT)
832  #pragma omp parallel for schedule(static) shared(progress,status) \
833  magick_number_threads(image,image,image->rows,1)
834 #endif
835  for (y=0; y < (ssize_t) image->rows; y++)
836  {
837  register ssize_t
838  x;
839 
840  register Quantum
841  *magick_restrict q;
842 
843  if (status == MagickFalse)
844  continue;
845  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
846  if (q == (Quantum *) NULL)
847  {
848  status=MagickFalse;
849  continue;
850  }
851  for (x=0; x < (ssize_t) image->columns; x++)
852  {
853  double
854  pixel;
855 
856  register ssize_t
857  i;
858 
859  pixel=GetPixelIntensity(image,q);
860  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
861  {
862  PixelChannel channel = GetPixelChannelChannel(image,i);
863  PixelTrait traits = GetPixelChannelTraits(image,channel);
864  if ((traits & UpdatePixelTrait) == 0)
865  continue;
866  if (image->channel_mask != DefaultChannels)
867  pixel=(double) q[i];
868  q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
869  }
870  q+=GetPixelChannels(image);
871  }
872  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
873  status=MagickFalse;
874  if (image->progress_monitor != (MagickProgressMonitor) NULL)
875  {
877  proceed;
878 
879 #if defined(MAGICKCORE_OPENMP_SUPPORT)
880  #pragma omp atomic
881 #endif
882  progress++;
883  proceed=SetImageProgress(image,ThresholdImageTag,progress++,
884  image->rows);
885  if (proceed == MagickFalse)
886  status=MagickFalse;
887  }
888  }
889  image_view=DestroyCacheView(image_view);
890  return(status);
891 }
892 
893 /*
894 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
895 % %
896 % %
897 % %
898 % B l a c k T h r e s h o l d I m a g e %
899 % %
900 % %
901 % %
902 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
903 %
904 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
905 % the threshold into black while leaving all pixels at or above the threshold
906 % unchanged.
907 %
908 % The format of the BlackThresholdImage method is:
909 %
910 % MagickBooleanType BlackThresholdImage(Image *image,
911 % const char *threshold,ExceptionInfo *exception)
912 %
913 % A description of each parameter follows:
914 %
915 % o image: the image.
916 %
917 % o threshold: define the threshold value.
918 %
919 % o exception: return any errors or warnings in this structure.
920 %
921 */
923  const char *thresholds,ExceptionInfo *exception)
924 {
925 #define ThresholdImageTag "Threshold/Image"
926 
927  CacheView
928  *image_view;
929 
931  geometry_info;
932 
934  status;
935 
937  progress;
938 
939  PixelInfo
940  threshold;
941 
943  flags;
944 
945  ssize_t
946  y;
947 
948  assert(image != (Image *) NULL);
949  assert(image->signature == MagickCoreSignature);
950  if (image->debug != MagickFalse)
951  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
952  if (thresholds == (const char *) NULL)
953  return(MagickTrue);
954  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
955  return(MagickFalse);
956  if (IsGrayColorspace(image->colorspace) != MagickFalse)
957  (void) SetImageColorspace(image,sRGBColorspace,exception);
958  GetPixelInfo(image,&threshold);
959  flags=ParseGeometry(thresholds,&geometry_info);
960  threshold.red=geometry_info.rho;
961  threshold.green=geometry_info.rho;
962  threshold.blue=geometry_info.rho;
963  threshold.black=geometry_info.rho;
964  threshold.alpha=100.0;
965  if ((flags & SigmaValue) != 0)
966  threshold.green=geometry_info.sigma;
967  if ((flags & XiValue) != 0)
968  threshold.blue=geometry_info.xi;
969  if ((flags & PsiValue) != 0)
970  threshold.alpha=geometry_info.psi;
971  if (threshold.colorspace == CMYKColorspace)
972  {
973  if ((flags & PsiValue) != 0)
974  threshold.black=geometry_info.psi;
975  if ((flags & ChiValue) != 0)
976  threshold.alpha=geometry_info.chi;
977  }
978  if ((flags & PercentValue) != 0)
979  {
980  threshold.red*=(MagickRealType) (QuantumRange/100.0);
981  threshold.green*=(MagickRealType) (QuantumRange/100.0);
982  threshold.blue*=(MagickRealType) (QuantumRange/100.0);
983  threshold.black*=(MagickRealType) (QuantumRange/100.0);
984  threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
985  }
986  /*
987  White threshold image.
988  */
989  status=MagickTrue;
990  progress=0;
991  image_view=AcquireAuthenticCacheView(image,exception);
992 #if defined(MAGICKCORE_OPENMP_SUPPORT)
993  #pragma omp parallel for schedule(static) shared(progress,status) \
994  magick_number_threads(image,image,image->rows,1)
995 #endif
996  for (y=0; y < (ssize_t) image->rows; y++)
997  {
998  register ssize_t
999  x;
1000 
1001  register Quantum
1002  *magick_restrict q;
1003 
1004  if (status == MagickFalse)
1005  continue;
1006  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1007  if (q == (Quantum *) NULL)
1008  {
1009  status=MagickFalse;
1010  continue;
1011  }
1012  for (x=0; x < (ssize_t) image->columns; x++)
1013  {
1014  double
1015  pixel;
1016 
1017  register ssize_t
1018  i;
1019 
1020  pixel=GetPixelIntensity(image,q);
1021  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1022  {
1023  PixelChannel channel = GetPixelChannelChannel(image,i);
1024  PixelTrait traits = GetPixelChannelTraits(image,channel);
1025  if ((traits & UpdatePixelTrait) == 0)
1026  continue;
1027  if (image->channel_mask != DefaultChannels)
1028  pixel=(double) q[i];
1029  if (pixel < GetPixelInfoChannel(&threshold,channel))
1030  q[i]=(Quantum) 0;
1031  }
1032  q+=GetPixelChannels(image);
1033  }
1034  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1035  status=MagickFalse;
1036  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1037  {
1039  proceed;
1040 
1041 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1042  #pragma omp atomic
1043 #endif
1044  progress++;
1045  proceed=SetImageProgress(image,ThresholdImageTag,progress,
1046  image->rows);
1047  if (proceed == MagickFalse)
1048  status=MagickFalse;
1049  }
1050  }
1051  image_view=DestroyCacheView(image_view);
1052  return(status);
1053 }
1054 
1055 /*
1056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1057 % %
1058 % %
1059 % %
1060 % C l a m p I m a g e %
1061 % %
1062 % %
1063 % %
1064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065 %
1066 % ClampImage() set each pixel whose value is below zero to zero and any the
1067 % pixel whose value is above the quantum range to the quantum range (e.g.
1068 % 65535) otherwise the pixel value remains unchanged.
1069 %
1070 % The format of the ClampImage method is:
1071 %
1072 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
1073 %
1074 % A description of each parameter follows:
1075 %
1076 % o image: the image.
1077 %
1078 % o exception: return any errors or warnings in this structure.
1079 %
1080 */
1081 
1083 {
1084 #define ClampImageTag "Clamp/Image"
1085 
1086  CacheView
1087  *image_view;
1088 
1090  status;
1091 
1093  progress;
1094 
1095  ssize_t
1096  y;
1097 
1098  assert(image != (Image *) NULL);
1099  assert(image->signature == MagickCoreSignature);
1100  if (image->debug != MagickFalse)
1101  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1102  if (image->storage_class == PseudoClass)
1103  {
1104  register ssize_t
1105  i;
1106 
1107  register PixelInfo
1108  *magick_restrict q;
1109 
1110  q=image->colormap;
1111  for (i=0; i < (ssize_t) image->colors; i++)
1112  {
1113  q->red=(double) ClampPixel(q->red);
1114  q->green=(double) ClampPixel(q->green);
1115  q->blue=(double) ClampPixel(q->blue);
1116  q->alpha=(double) ClampPixel(q->alpha);
1117  q++;
1118  }
1119  return(SyncImage(image,exception));
1120  }
1121  /*
1122  Clamp image.
1123  */
1124  status=MagickTrue;
1125  progress=0;
1126  image_view=AcquireAuthenticCacheView(image,exception);
1127 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1128  #pragma omp parallel for schedule(static) shared(progress,status) \
1129  magick_number_threads(image,image,image->rows,1)
1130 #endif
1131  for (y=0; y < (ssize_t) image->rows; y++)
1132  {
1133  register ssize_t
1134  x;
1135 
1136  register Quantum
1137  *magick_restrict q;
1138 
1139  if (status == MagickFalse)
1140  continue;
1141  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1142  if (q == (Quantum *) NULL)
1143  {
1144  status=MagickFalse;
1145  continue;
1146  }
1147  for (x=0; x < (ssize_t) image->columns; x++)
1148  {
1149  register ssize_t
1150  i;
1151 
1152  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1153  {
1154  PixelChannel channel = GetPixelChannelChannel(image,i);
1155  PixelTrait traits = GetPixelChannelTraits(image,channel);
1156  if ((traits & UpdatePixelTrait) == 0)
1157  continue;
1158  q[i]=ClampPixel((MagickRealType) q[i]);
1159  }
1160  q+=GetPixelChannels(image);
1161  }
1162  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1163  status=MagickFalse;
1164  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1165  {
1167  proceed;
1168 
1169 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1170  #pragma omp atomic
1171 #endif
1172  progress++;
1173  proceed=SetImageProgress(image,ClampImageTag,progress,image->rows);
1174  if (proceed == MagickFalse)
1175  status=MagickFalse;
1176  }
1177  }
1178  image_view=DestroyCacheView(image_view);
1179  return(status);
1180 }
1181 
1182 /*
1183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184 % %
1185 % %
1186 % %
1187 % D e s t r o y T h r e s h o l d M a p %
1188 % %
1189 % %
1190 % %
1191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1192 %
1193 % DestroyThresholdMap() de-allocate the given ThresholdMap
1194 %
1195 % The format of the ListThresholdMaps method is:
1196 %
1197 % ThresholdMap *DestroyThresholdMap(Threshold *map)
1198 %
1199 % A description of each parameter follows.
1200 %
1201 % o map: Pointer to the Threshold map to destroy
1202 %
1203 */
1205 {
1206  assert(map != (ThresholdMap *) NULL);
1207  if (map->map_id != (char *) NULL)
1208  map->map_id=DestroyString(map->map_id);
1209  if (map->description != (char *) NULL)
1211  if (map->levels != (ssize_t *) NULL)
1212  map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1213  map=(ThresholdMap *) RelinquishMagickMemory(map);
1214  return(map);
1215 }
1216 
1217 /*
1218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1219 % %
1220 % %
1221 % %
1222 % G e t T h r e s h o l d M a p %
1223 % %
1224 % %
1225 % %
1226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1227 %
1228 % GetThresholdMap() loads and searches one or more threshold map files for the
1229 % map matching the given name or alias.
1230 %
1231 % The format of the GetThresholdMap method is:
1232 %
1233 % ThresholdMap *GetThresholdMap(const char *map_id,
1234 % ExceptionInfo *exception)
1235 %
1236 % A description of each parameter follows.
1237 %
1238 % o map_id: ID of the map to look for.
1239 %
1240 % o exception: return any errors or warnings in this structure.
1241 %
1242 */
1244  ExceptionInfo *exception)
1245 {
1246  ThresholdMap
1247  *map;
1248 
1249  map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
1250  if (map != (ThresholdMap *) NULL)
1251  return(map);
1252 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
1253  {
1254  const StringInfo
1255  *option;
1256 
1258  *options;
1259 
1260  options=GetConfigureOptions(ThresholdsFilename,exception);
1261  option=(const StringInfo *) GetNextValueInLinkedList(options);
1262  while (option != (const StringInfo *) NULL)
1263  {
1264  map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1265  GetStringInfoPath(option),map_id,exception);
1266  if (map != (ThresholdMap *) NULL)
1267  break;
1268  option=(const StringInfo *) GetNextValueInLinkedList(options);
1269  }
1270  options=DestroyConfigureOptions(options);
1271  }
1272 #endif
1273  return(map);
1274 }
1275 
1276 /*
1277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278 % %
1279 % %
1280 % %
1281 + G e t T h r e s h o l d M a p F i l e %
1282 % %
1283 % %
1284 % %
1285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286 %
1287 % GetThresholdMapFile() look for a given threshold map name or alias in the
1288 % given XML file data, and return the allocated the map when found.
1289 %
1290 % The format of the ListThresholdMaps method is:
1291 %
1292 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
1293 % const char *map_id,ExceptionInfo *exception)
1294 %
1295 % A description of each parameter follows.
1296 %
1297 % o xml: The threshold map list in XML format.
1298 %
1299 % o filename: The threshold map XML filename.
1300 %
1301 % o map_id: ID of the map to look for in XML list.
1302 %
1303 % o exception: return any errors or warnings in this structure.
1304 %
1305 */
1306 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
1307  const char *map_id,ExceptionInfo *exception)
1308 {
1309  char
1310  *p;
1311 
1312  const char
1313  *attribute,
1314  *content;
1315 
1316  double
1317  value;
1318 
1319  register ssize_t
1320  i;
1321 
1322  ThresholdMap
1323  *map;
1324 
1325  XMLTreeInfo
1326  *description,
1327  *levels,
1328  *threshold,
1329  *thresholds;
1330 
1332  "Loading threshold map file \"%s\" ...",filename);
1333  map=(ThresholdMap *) NULL;
1334  thresholds=NewXMLTree(xml,exception);
1335  if (thresholds == (XMLTreeInfo *) NULL)
1336  return(map);
1337  for (threshold=GetXMLTreeChild(thresholds,"threshold");
1338  threshold != (XMLTreeInfo *) NULL;
1339  threshold=GetNextXMLTreeTag(threshold))
1340  {
1341  attribute=GetXMLTreeAttribute(threshold,"map");
1342  if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1343  break;
1344  attribute=GetXMLTreeAttribute(threshold,"alias");
1345  if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1346  break;
1347  }
1348  if (threshold == (XMLTreeInfo *) NULL)
1349  {
1350  thresholds=DestroyXMLTree(thresholds);
1351  return(map);
1352  }
1353  description=GetXMLTreeChild(threshold,"description");
1354  if (description == (XMLTreeInfo *) NULL)
1355  {
1357  "XmlMissingElement", "<description>, map \"%s\"",map_id);
1358  thresholds=DestroyXMLTree(thresholds);
1359  return(map);
1360  }
1361  levels=GetXMLTreeChild(threshold,"levels");
1362  if (levels == (XMLTreeInfo *) NULL)
1363  {
1365  "XmlMissingElement", "<levels>, map \"%s\"", map_id);
1366  thresholds=DestroyXMLTree(thresholds);
1367  return(map);
1368  }
1369  map=(ThresholdMap *) AcquireCriticalMemory(sizeof(*map));
1370  map->map_id=(char *) NULL;
1371  map->description=(char *) NULL;
1372  map->levels=(ssize_t *) NULL;
1373  attribute=GetXMLTreeAttribute(threshold,"map");
1374  if (attribute != (char *) NULL)
1375  map->map_id=ConstantString(attribute);
1376  content=GetXMLTreeContent(description);
1377  if (content != (char *) NULL)
1378  map->description=ConstantString(content);
1379  attribute=GetXMLTreeAttribute(levels,"width");
1380  if (attribute == (char *) NULL)
1381  {
1383  "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1384  thresholds=DestroyXMLTree(thresholds);
1385  map=DestroyThresholdMap(map);
1386  return(map);
1387  }
1388  map->width=StringToUnsignedLong(attribute);
1389  if (map->width == 0)
1390  {
1392  "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1393  thresholds=DestroyXMLTree(thresholds);
1394  map=DestroyThresholdMap(map);
1395  return(map);
1396  }
1397  attribute=GetXMLTreeAttribute(levels,"height");
1398  if (attribute == (char *) NULL)
1399  {
1401  "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1402  thresholds=DestroyXMLTree(thresholds);
1403  map=DestroyThresholdMap(map);
1404  return(map);
1405  }
1406  map->height=StringToUnsignedLong(attribute);
1407  if (map->height == 0)
1408  {
1410  "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1411  thresholds=DestroyXMLTree(thresholds);
1412  map=DestroyThresholdMap(map);
1413  return(map);
1414  }
1415  attribute=GetXMLTreeAttribute(levels,"divisor");
1416  if (attribute == (char *) NULL)
1417  {
1419  "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1420  thresholds=DestroyXMLTree(thresholds);
1421  map=DestroyThresholdMap(map);
1422  return(map);
1423  }
1424  map->divisor=(ssize_t) StringToLong(attribute);
1425  if (map->divisor < 2)
1426  {
1428  "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1429  thresholds=DestroyXMLTree(thresholds);
1430  map=DestroyThresholdMap(map);
1431  return(map);
1432  }
1433  content=GetXMLTreeContent(levels);
1434  if (content == (char *) NULL)
1435  {
1437  "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1438  thresholds=DestroyXMLTree(thresholds);
1439  map=DestroyThresholdMap(map);
1440  return(map);
1441  }
1442  map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1443  sizeof(*map->levels));
1444  if (map->levels == (ssize_t *) NULL)
1445  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1446  for (i=0; i < (ssize_t) (map->width*map->height); i++)
1447  {
1448  map->levels[i]=(ssize_t) strtol(content,&p,10);
1449  if (p == content)
1450  {
1452  "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1453  thresholds=DestroyXMLTree(thresholds);
1454  map=DestroyThresholdMap(map);
1455  return(map);
1456  }
1457  if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1458  {
1460  "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1461  (double) map->levels[i],map_id);
1462  thresholds=DestroyXMLTree(thresholds);
1463  map=DestroyThresholdMap(map);
1464  return(map);
1465  }
1466  content=p;
1467  }
1468  value=(double) strtol(content,&p,10);
1469  (void) value;
1470  if (p != content)
1471  {
1473  "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1474  thresholds=DestroyXMLTree(thresholds);
1475  map=DestroyThresholdMap(map);
1476  return(map);
1477  }
1478  thresholds=DestroyXMLTree(thresholds);
1479  return(map);
1480 }
1481 
1482 /*
1483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484 % %
1485 % %
1486 % %
1487 + L i s t T h r e s h o l d M a p F i l e %
1488 % %
1489 % %
1490 % %
1491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492 %
1493 % ListThresholdMapFile() lists the threshold maps and their descriptions
1494 % in the given XML file data.
1495 %
1496 % The format of the ListThresholdMaps method is:
1497 %
1498 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1499 % const char *filename,ExceptionInfo *exception)
1500 %
1501 % A description of each parameter follows.
1502 %
1503 % o file: An pointer to the output FILE.
1504 %
1505 % o xml: The threshold map list in XML format.
1506 %
1507 % o filename: The threshold map XML filename.
1508 %
1509 % o exception: return any errors or warnings in this structure.
1510 %
1511 */
1512 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1513  const char *filename,ExceptionInfo *exception)
1514 {
1515  const char
1516  *alias,
1517  *content,
1518  *map;
1519 
1520  XMLTreeInfo
1521  *description,
1522  *threshold,
1523  *thresholds;
1524 
1525  assert( xml != (char *) NULL );
1526  assert( file != (FILE *) NULL );
1528  "Loading threshold map file \"%s\" ...",filename);
1529  thresholds=NewXMLTree(xml,exception);
1530  if ( thresholds == (XMLTreeInfo *) NULL )
1531  return(MagickFalse);
1532  (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1533  (void) FormatLocaleFile(file,
1534  "----------------------------------------------------\n");
1535  threshold=GetXMLTreeChild(thresholds,"threshold");
1536  for ( ; threshold != (XMLTreeInfo *) NULL;
1537  threshold=GetNextXMLTreeTag(threshold))
1538  {
1539  map=GetXMLTreeAttribute(threshold,"map");
1540  if (map == (char *) NULL)
1541  {
1543  "XmlMissingAttribute", "<map>");
1544  thresholds=DestroyXMLTree(thresholds);
1545  return(MagickFalse);
1546  }
1547  alias=GetXMLTreeAttribute(threshold,"alias");
1548  description=GetXMLTreeChild(threshold,"description");
1549  if (description == (XMLTreeInfo *) NULL)
1550  {
1552  "XmlMissingElement", "<description>, map \"%s\"",map);
1553  thresholds=DestroyXMLTree(thresholds);
1554  return(MagickFalse);
1555  }
1556  content=GetXMLTreeContent(description);
1557  if (content == (char *) NULL)
1558  {
1560  "XmlMissingContent", "<description>, map \"%s\"", map);
1561  thresholds=DestroyXMLTree(thresholds);
1562  return(MagickFalse);
1563  }
1564  (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1565  content);
1566  }
1567  thresholds=DestroyXMLTree(thresholds);
1568  return(MagickTrue);
1569 }
1570 
1571 /*
1572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1573 % %
1574 % %
1575 % %
1576 % L i s t T h r e s h o l d M a p s %
1577 % %
1578 % %
1579 % %
1580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1581 %
1582 % ListThresholdMaps() lists the threshold maps and their descriptions
1583 % as defined by "threshold.xml" to a file.
1584 %
1585 % The format of the ListThresholdMaps method is:
1586 %
1587 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1588 %
1589 % A description of each parameter follows.
1590 %
1591 % o file: An pointer to the output FILE.
1592 %
1593 % o exception: return any errors or warnings in this structure.
1594 %
1595 */
1597  ExceptionInfo *exception)
1598 {
1599  const StringInfo
1600  *option;
1601 
1603  *options;
1604 
1606  status;
1607 
1608  status=MagickTrue;
1609  if (file == (FILE *) NULL)
1610  file=stdout;
1611  options=GetConfigureOptions(ThresholdsFilename,exception);
1612  (void) FormatLocaleFile(file,
1613  "\n Threshold Maps for Ordered Dither Operations\n");
1614  option=(const StringInfo *) GetNextValueInLinkedList(options);
1615  while (option != (const StringInfo *) NULL)
1616  {
1617  (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1618  status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1619  GetStringInfoPath(option),exception);
1620  option=(const StringInfo *) GetNextValueInLinkedList(options);
1621  }
1622  options=DestroyConfigureOptions(options);
1623  return(status != 0 ? MagickTrue : MagickFalse);
1624 }
1625 
1626 /*
1627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1628 % %
1629 % %
1630 % %
1631 % O r d e r e d D i t h e r I m a g e %
1632 % %
1633 % %
1634 % %
1635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636 %
1637 % OrderedDitherImage() will perform a ordered dither based on a number
1638 % of pre-defined dithering threshold maps, but over multiple intensity
1639 % levels, which can be different for different channels, according to the
1640 % input argument.
1641 %
1642 % The format of the OrderedDitherImage method is:
1643 %
1644 % MagickBooleanType OrderedDitherImage(Image *image,
1645 % const char *threshold_map,ExceptionInfo *exception)
1646 %
1647 % A description of each parameter follows:
1648 %
1649 % o image: the image.
1650 %
1651 % o threshold_map: A string containing the name of the threshold dither
1652 % map to use, followed by zero or more numbers representing the number
1653 % of color levels tho dither between.
1654 %
1655 % Any level number less than 2 will be equivalent to 2, and means only
1656 % binary dithering will be applied to each color channel.
1657 %
1658 % No numbers also means a 2 level (bitmap) dither will be applied to all
1659 % channels, while a single number is the number of levels applied to each
1660 % channel in sequence. More numbers will be applied in turn to each of
1661 % the color channels.
1662 %
1663 % For example: "o3x3,6" will generate a 6 level posterization of the
1664 % image with a ordered 3x3 diffused pixel dither being applied between
1665 % each level. While checker,8,8,4 will produce a 332 colormaped image
1666 % with only a single checkerboard hash pattern (50% grey) between each
1667 % color level, to basically double the number of color levels with
1668 % a bare minimim of dithering.
1669 %
1670 % o exception: return any errors or warnings in this structure.
1671 %
1672 */
1674  const char *threshold_map,ExceptionInfo *exception)
1675 {
1676 #define DitherImageTag "Dither/Image"
1677 
1678  CacheView
1679  *image_view;
1680 
1681  char
1682  token[MagickPathExtent];
1683 
1684  const char
1685  *p;
1686 
1687  double
1688  levels[CompositePixelChannel];
1689 
1691  status;
1692 
1694  progress;
1695 
1696  register ssize_t
1697  i;
1698 
1699  ssize_t
1700  y;
1701 
1702  ThresholdMap
1703  *map;
1704 
1705  assert(image != (Image *) NULL);
1706  assert(image->signature == MagickCoreSignature);
1707  if (image->debug != MagickFalse)
1708  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1709  assert(exception != (ExceptionInfo *) NULL);
1710  assert(exception->signature == MagickCoreSignature);
1711  if (threshold_map == (const char *) NULL)
1712  return(MagickTrue);
1713  p=(char *) threshold_map;
1714  while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1715  (*p != '\0'))
1716  p++;
1717  threshold_map=p;
1718  while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1719  (*p != '\0'))
1720  {
1721  if ((p-threshold_map) >= (MagickPathExtent-1))
1722  break;
1723  token[p-threshold_map]=(*p);
1724  p++;
1725  }
1726  token[p-threshold_map]='\0';
1727  map=GetThresholdMap(token,exception);
1728  if (map == (ThresholdMap *) NULL)
1729  {
1731  "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1732  return(MagickFalse);
1733  }
1734  for (i=0; i < MaxPixelChannels; i++)
1735  levels[i]=2.0;
1736  p=strchr((char *) threshold_map,',');
1737  if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1738  {
1739  GetNextToken(p,&p,MagickPathExtent,token);
1740  for (i=0; (i < MaxPixelChannels); i++)
1741  levels[i]=StringToDouble(token,(char **) NULL);
1742  for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1743  {
1744  GetNextToken(p,&p,MagickPathExtent,token);
1745  if (*token == ',')
1746  GetNextToken(p,&p,MagickPathExtent,token);
1747  levels[i]=StringToDouble(token,(char **) NULL);
1748  }
1749  }
1750  for (i=0; i < MaxPixelChannels; i++)
1751  if (fabs(levels[i]) >= 1)
1752  levels[i]-=1.0;
1753  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1754  return(MagickFalse);
1755  status=MagickTrue;
1756  progress=0;
1757  image_view=AcquireAuthenticCacheView(image,exception);
1758 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1759  #pragma omp parallel for schedule(static) shared(progress,status) \
1760  magick_number_threads(image,image,image->rows,1)
1761 #endif
1762  for (y=0; y < (ssize_t) image->rows; y++)
1763  {
1764  register ssize_t
1765  x;
1766 
1767  register Quantum
1768  *magick_restrict q;
1769 
1770  if (status == MagickFalse)
1771  continue;
1772  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1773  if (q == (Quantum *) NULL)
1774  {
1775  status=MagickFalse;
1776  continue;
1777  }
1778  for (x=0; x < (ssize_t) image->columns; x++)
1779  {
1780  register ssize_t
1781  i;
1782 
1783  ssize_t
1784  n;
1785 
1786  n=0;
1787  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1788  {
1789  ssize_t
1790  level,
1791  threshold;
1792 
1793  PixelChannel channel = GetPixelChannelChannel(image,i);
1794  PixelTrait traits = GetPixelChannelTraits(image,channel);
1795  if ((traits & UpdatePixelTrait) == 0)
1796  continue;
1797  if (fabs(levels[n]) < MagickEpsilon)
1798  {
1799  n++;
1800  continue;
1801  }
1802  threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1803  level=threshold/(map->divisor-1);
1804  threshold-=level*(map->divisor-1);
1805  q[i]=ClampToQuantum((double) (level+(threshold >=
1806  map->levels[(x % map->width)+map->width*(y % map->height)]))*
1807  QuantumRange/levels[n]);
1808  n++;
1809  }
1810  q+=GetPixelChannels(image);
1811  }
1812  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1813  status=MagickFalse;
1814  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1815  {
1817  proceed;
1818 
1819 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1820  #pragma omp atomic
1821 #endif
1822  progress++;
1823  proceed=SetImageProgress(image,DitherImageTag,progress,image->rows);
1824  if (proceed == MagickFalse)
1825  status=MagickFalse;
1826  }
1827  }
1828  image_view=DestroyCacheView(image_view);
1829  map=DestroyThresholdMap(map);
1830  return(MagickTrue);
1831 }
1832 
1833 /*
1834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1835 % %
1836 % %
1837 % %
1838 % P e r c e p t i b l e I m a g e %
1839 % %
1840 % %
1841 % %
1842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1843 %
1844 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1845 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1846 % unchanged.
1847 %
1848 % The format of the PerceptibleImage method is:
1849 %
1850 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1851 % ExceptionInfo *exception)
1852 %
1853 % A description of each parameter follows:
1854 %
1855 % o image: the image.
1856 %
1857 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1858 %
1859 % o exception: return any errors or warnings in this structure.
1860 %
1861 */
1862 
1863 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1864  const double epsilon)
1865 {
1866  double
1867  sign;
1868 
1869  sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1870  if ((sign*quantum) >= epsilon)
1871  return(quantum);
1872  return((Quantum) (sign*epsilon));
1873 }
1874 
1876  const double epsilon,ExceptionInfo *exception)
1877 {
1878 #define PerceptibleImageTag "Perceptible/Image"
1879 
1880  CacheView
1881  *image_view;
1882 
1884  status;
1885 
1887  progress;
1888 
1889  ssize_t
1890  y;
1891 
1892  assert(image != (Image *) NULL);
1893  assert(image->signature == MagickCoreSignature);
1894  if (image->debug != MagickFalse)
1895  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1896  if (image->storage_class == PseudoClass)
1897  {
1898  register ssize_t
1899  i;
1900 
1901  register PixelInfo
1902  *magick_restrict q;
1903 
1904  q=image->colormap;
1905  for (i=0; i < (ssize_t) image->colors; i++)
1906  {
1907  q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1908  epsilon);
1909  q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1910  epsilon);
1911  q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1912  epsilon);
1913  q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1914  epsilon);
1915  q++;
1916  }
1917  return(SyncImage(image,exception));
1918  }
1919  /*
1920  Perceptible image.
1921  */
1922  status=MagickTrue;
1923  progress=0;
1924  image_view=AcquireAuthenticCacheView(image,exception);
1925 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1926  #pragma omp parallel for schedule(static) shared(progress,status) \
1927  magick_number_threads(image,image,image->rows,1)
1928 #endif
1929  for (y=0; y < (ssize_t) image->rows; y++)
1930  {
1931  register ssize_t
1932  x;
1933 
1934  register Quantum
1935  *magick_restrict q;
1936 
1937  if (status == MagickFalse)
1938  continue;
1939  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1940  if (q == (Quantum *) NULL)
1941  {
1942  status=MagickFalse;
1943  continue;
1944  }
1945  for (x=0; x < (ssize_t) image->columns; x++)
1946  {
1947  register ssize_t
1948  i;
1949 
1950  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1951  {
1952  PixelChannel channel = GetPixelChannelChannel(image,i);
1953  PixelTrait traits = GetPixelChannelTraits(image,channel);
1954  if (traits == UndefinedPixelTrait)
1955  continue;
1956  q[i]=PerceptibleThreshold(q[i],epsilon);
1957  }
1958  q+=GetPixelChannels(image);
1959  }
1960  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1961  status=MagickFalse;
1962  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1963  {
1965  proceed;
1966 
1967 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1968  #pragma omp atomic
1969 #endif
1970  progress++;
1971  proceed=SetImageProgress(image,PerceptibleImageTag,progress,
1972  image->rows);
1973  if (proceed == MagickFalse)
1974  status=MagickFalse;
1975  }
1976  }
1977  image_view=DestroyCacheView(image_view);
1978  return(status);
1979 }
1980 
1981 /*
1982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1983 % %
1984 % %
1985 % %
1986 % R a n d o m T h r e s h o l d I m a g e %
1987 % %
1988 % %
1989 % %
1990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1991 %
1992 % RandomThresholdImage() changes the value of individual pixels based on the
1993 % intensity of each pixel compared to a random threshold. The result is a
1994 % low-contrast, two color image.
1995 %
1996 % The format of the RandomThresholdImage method is:
1997 %
1998 % MagickBooleanType RandomThresholdImage(Image *image,
1999 % const char *thresholds,ExceptionInfo *exception)
2000 %
2001 % A description of each parameter follows:
2002 %
2003 % o image: the image.
2004 %
2005 % o low,high: Specify the high and low thresholds. These values range from
2006 % 0 to QuantumRange.
2007 %
2008 % o exception: return any errors or warnings in this structure.
2009 %
2010 */
2012  const double min_threshold, const double max_threshold,ExceptionInfo *exception)
2013 {
2014 #define ThresholdImageTag "Threshold/Image"
2015 
2016  CacheView
2017  *image_view;
2018 
2020  status;
2021 
2023  progress;
2024 
2025  PixelInfo
2026  threshold;
2027 
2028  RandomInfo
2030 
2031  ssize_t
2032  y;
2033 
2034 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2035  unsigned long
2036  key;
2037 #endif
2038 
2039  assert(image != (Image *) NULL);
2040  assert(image->signature == MagickCoreSignature);
2041  if (image->debug != MagickFalse)
2042  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2043  assert(exception != (ExceptionInfo *) NULL);
2044  assert(exception->signature == MagickCoreSignature);
2045  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2046  return(MagickFalse);
2047  GetPixelInfo(image,&threshold);
2048  /*
2049  Random threshold image.
2050  */
2051  status=MagickTrue;
2052  progress=0;
2053  random_info=AcquireRandomInfoThreadSet();
2054  image_view=AcquireAuthenticCacheView(image,exception);
2055 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2056  key=GetRandomSecretKey(random_info[0]);
2057  #pragma omp parallel for schedule(static) shared(progress,status) \
2058  magick_number_threads(image,image,image->rows,key == ~0UL)
2059 #endif
2060  for (y=0; y < (ssize_t) image->rows; y++)
2061  {
2062  const int
2063  id = GetOpenMPThreadId();
2064 
2065  register Quantum
2066  *magick_restrict q;
2067 
2068  register ssize_t
2069  x;
2070 
2071  if (status == MagickFalse)
2072  continue;
2073  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2074  if (q == (Quantum *) NULL)
2075  {
2076  status=MagickFalse;
2077  continue;
2078  }
2079  for (x=0; x < (ssize_t) image->columns; x++)
2080  {
2081  register ssize_t
2082  i;
2083 
2084  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2085  {
2086  double
2087  threshold;
2088 
2089  PixelChannel channel = GetPixelChannelChannel(image,i);
2090  PixelTrait traits = GetPixelChannelTraits(image,channel);
2091  if ((traits & UpdatePixelTrait) == 0)
2092  continue;
2093  if ((double) q[i] < min_threshold)
2094  threshold=min_threshold;
2095  else
2096  if ((double) q[i] > max_threshold)
2097  threshold=max_threshold;
2098  else
2099  threshold=(double) (QuantumRange*
2100  GetPseudoRandomValue(random_info[id]));
2101  q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
2102  }
2103  q+=GetPixelChannels(image);
2104  }
2105  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2106  status=MagickFalse;
2107  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2108  {
2110  proceed;
2111 
2112 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2113  #pragma omp atomic
2114 #endif
2115  progress++;
2116  proceed=SetImageProgress(image,ThresholdImageTag,progress,
2117  image->rows);
2118  if (proceed == MagickFalse)
2119  status=MagickFalse;
2120  }
2121  }
2122  image_view=DestroyCacheView(image_view);
2123  random_info=DestroyRandomInfoThreadSet(random_info);
2124  return(status);
2125 }
2126 
2127 /*
2128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2129 % %
2130 % %
2131 % %
2132 % R a n g e T h r e s h o l d I m a g e %
2133 % %
2134 % %
2135 % %
2136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2137 %
2138 % RangeThresholdImage() applies soft and hard thresholding.
2139 %
2140 % The format of the RangeThresholdImage method is:
2141 %
2142 % MagickBooleanType RangeThresholdImage(Image *image,
2143 % const double low_black,const double low_white,const double high_white,
2144 % const double high_black,ExceptionInfo *exception)
2145 %
2146 % A description of each parameter follows:
2147 %
2148 % o image: the image.
2149 %
2150 % o low_black: Define the minimum threshold value.
2151 %
2152 % o low_white: Define the maximum threshold value.
2153 %
2154 % o high_white: Define the minimum threshold value.
2155 %
2156 % o low_white: Define the maximum threshold value.
2157 %
2158 % o exception: return any errors or warnings in this structure.
2159 %
2160 */
2162  const double low_black,const double low_white,const double high_white,
2163  const double high_black,ExceptionInfo *exception)
2164 {
2165 #define ThresholdImageTag "Threshold/Image"
2166 
2167  CacheView
2168  *image_view;
2169 
2171  status;
2172 
2174  progress;
2175 
2176  ssize_t
2177  y;
2178 
2179  assert(image != (Image *) NULL);
2180  assert(image->signature == MagickCoreSignature);
2181  if (image->debug != MagickFalse)
2182  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2183  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2184  return(MagickFalse);
2185  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2186  (void) TransformImageColorspace(image,sRGBColorspace,exception);
2187  /*
2188  Range threshold image.
2189  */
2190  status=MagickTrue;
2191  progress=0;
2192  image_view=AcquireAuthenticCacheView(image,exception);
2193 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2194  #pragma omp parallel for schedule(static) shared(progress,status) \
2195  magick_number_threads(image,image,image->rows,1)
2196 #endif
2197  for (y=0; y < (ssize_t) image->rows; y++)
2198  {
2199  register ssize_t
2200  x;
2201 
2202  register Quantum
2203  *magick_restrict q;
2204 
2205  if (status == MagickFalse)
2206  continue;
2207  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2208  if (q == (Quantum *) NULL)
2209  {
2210  status=MagickFalse;
2211  continue;
2212  }
2213  for (x=0; x < (ssize_t) image->columns; x++)
2214  {
2215  double
2216  pixel;
2217 
2218  register ssize_t
2219  i;
2220 
2221  pixel=GetPixelIntensity(image,q);
2222  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2223  {
2224  PixelChannel channel = GetPixelChannelChannel(image,i);
2225  PixelTrait traits = GetPixelChannelTraits(image,channel);
2226  if ((traits & UpdatePixelTrait) == 0)
2227  continue;
2228  if (image->channel_mask != DefaultChannels)
2229  pixel=(double) q[i];
2230  if (pixel < low_black)
2231  q[i]=0;
2232  else
2233  if ((pixel >= low_black) && (pixel < low_white))
2235  PerceptibleReciprocal(low_white-low_black)*(pixel-low_black));
2236  else
2237  if ((pixel >= low_white) && (pixel <= high_white))
2238  q[i]=QuantumRange;
2239  else
2240  if ((pixel > high_white) && (pixel <= high_black))
2242  high_black-high_white)*(high_black-pixel));
2243  else
2244  if (pixel > high_black)
2245  q[i]=0;
2246  else
2247  q[i]=0;
2248  }
2249  q+=GetPixelChannels(image);
2250  }
2251  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2252  status=MagickFalse;
2253  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2254  {
2256  proceed;
2257 
2258 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2259  #pragma omp atomic
2260 #endif
2261  progress++;
2262  proceed=SetImageProgress(image,ThresholdImageTag,progress,
2263  image->rows);
2264  if (proceed == MagickFalse)
2265  status=MagickFalse;
2266  }
2267  }
2268  image_view=DestroyCacheView(image_view);
2269  return(status);
2270 }
2271 
2272 /*
2273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2274 % %
2275 % %
2276 % %
2277 % W h i t e T h r e s h o l d I m a g e %
2278 % %
2279 % %
2280 % %
2281 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2282 %
2283 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
2284 % the threshold into white while leaving all pixels at or below the threshold
2285 % unchanged.
2286 %
2287 % The format of the WhiteThresholdImage method is:
2288 %
2289 % MagickBooleanType WhiteThresholdImage(Image *image,
2290 % const char *threshold,ExceptionInfo *exception)
2291 %
2292 % A description of each parameter follows:
2293 %
2294 % o image: the image.
2295 %
2296 % o threshold: Define the threshold value.
2297 %
2298 % o exception: return any errors or warnings in this structure.
2299 %
2300 */
2302  const char *thresholds,ExceptionInfo *exception)
2303 {
2304 #define ThresholdImageTag "Threshold/Image"
2305 
2306  CacheView
2307  *image_view;
2308 
2309  GeometryInfo
2310  geometry_info;
2311 
2313  status;
2314 
2316  progress;
2317 
2318  PixelInfo
2319  threshold;
2320 
2322  flags;
2323 
2324  ssize_t
2325  y;
2326 
2327  assert(image != (Image *) NULL);
2328  assert(image->signature == MagickCoreSignature);
2329  if (image->debug != MagickFalse)
2330  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2331  if (thresholds == (const char *) NULL)
2332  return(MagickTrue);
2333  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2334  return(MagickFalse);
2335  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2336  (void) TransformImageColorspace(image,sRGBColorspace,exception);
2337  GetPixelInfo(image,&threshold);
2338  flags=ParseGeometry(thresholds,&geometry_info);
2339  threshold.red=geometry_info.rho;
2340  threshold.green=geometry_info.rho;
2341  threshold.blue=geometry_info.rho;
2342  threshold.black=geometry_info.rho;
2343  threshold.alpha=100.0;
2344  if ((flags & SigmaValue) != 0)
2345  threshold.green=geometry_info.sigma;
2346  if ((flags & XiValue) != 0)
2347  threshold.blue=geometry_info.xi;
2348  if ((flags & PsiValue) != 0)
2349  threshold.alpha=geometry_info.psi;
2350  if (threshold.colorspace == CMYKColorspace)
2351  {
2352  if ((flags & PsiValue) != 0)
2353  threshold.black=geometry_info.psi;
2354  if ((flags & ChiValue) != 0)
2355  threshold.alpha=geometry_info.chi;
2356  }
2357  if ((flags & PercentValue) != 0)
2358  {
2359  threshold.red*=(MagickRealType) (QuantumRange/100.0);
2360  threshold.green*=(MagickRealType) (QuantumRange/100.0);
2361  threshold.blue*=(MagickRealType) (QuantumRange/100.0);
2362  threshold.black*=(MagickRealType) (QuantumRange/100.0);
2363  threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
2364  }
2365  /*
2366  White threshold image.
2367  */
2368  status=MagickTrue;
2369  progress=0;
2370  image_view=AcquireAuthenticCacheView(image,exception);
2371 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2372  #pragma omp parallel for schedule(static) shared(progress,status) \
2373  magick_number_threads(image,image,image->rows,1)
2374 #endif
2375  for (y=0; y < (ssize_t) image->rows; y++)
2376  {
2377  register ssize_t
2378  x;
2379 
2380  register Quantum
2381  *magick_restrict q;
2382 
2383  if (status == MagickFalse)
2384  continue;
2385  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2386  if (q == (Quantum *) NULL)
2387  {
2388  status=MagickFalse;
2389  continue;
2390  }
2391  for (x=0; x < (ssize_t) image->columns; x++)
2392  {
2393  double
2394  pixel;
2395 
2396  register ssize_t
2397  i;
2398 
2399  pixel=GetPixelIntensity(image,q);
2400  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2401  {
2402  PixelChannel channel = GetPixelChannelChannel(image,i);
2403  PixelTrait traits = GetPixelChannelTraits(image,channel);
2404  if ((traits & UpdatePixelTrait) == 0)
2405  continue;
2406  if (image->channel_mask != DefaultChannels)
2407  pixel=(double) q[i];
2408  if (pixel > GetPixelInfoChannel(&threshold,channel))
2409  q[i]=QuantumRange;
2410  }
2411  q+=GetPixelChannels(image);
2412  }
2413  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2414  status=MagickFalse;
2415  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2416  {
2418  proceed;
2419 
2420 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2421  #pragma omp atomic
2422 #endif
2423  progress++;
2424  proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
2425  if (proceed == MagickFalse)
2426  status=MagickFalse;
2427  }
2428  }
2429  image_view=DestroyCacheView(image_view);
2430  return(status);
2431 }
#define DitherImageTag
double psi
Definition: geometry.h:106
size_t rows
Definition: image.h:172
#define magick_restrict
Definition: MagickCore.h:41
MagickExport MagickBooleanType BlackThresholdImage(Image *image, const char *thresholds, ExceptionInfo *exception)
Definition: threshold.c:922
MagickExport MagickBooleanType RangeThresholdImage(Image *image, const double low_black, const double low_white, const double high_white, const double high_black, ExceptionInfo *exception)
Definition: threshold.c:2161
MagickDoubleType MagickRealType
Definition: magick-type.h:120
MagickExport CacheView * DestroyCacheView(CacheView *cache_view)
Definition: cache-view.c:252
PixelInfo * colormap
Definition: image.h:179
MagickExport XMLTreeInfo * DestroyXMLTree(XMLTreeInfo *xml_info)
Definition: xml-tree.c:558
MagickProgressMonitor progress_monitor
Definition: image.h:303
ImageType type
Definition: image.h:264
MagickExport MagickBooleanType SyncImage(Image *image, ExceptionInfo *exception)
Definition: image.c:3883
MagickExport MagickBooleanType TransformImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1326
static unsigned long StringToUnsignedLong(const char *magick_restrict value)
#define ThrowFatalException(severity, tag)
MagickExport XMLTreeInfo * GetNextXMLTreeTag(XMLTreeInfo *xml_info)
Definition: xml-tree.c:744
size_t signature
Definition: exception.h:123
size_t width
Definition: threshold.c:108
double rho
Definition: geometry.h:106
MagickExport MagickBooleanType ListThresholdMaps(FILE *file, ExceptionInfo *exception)
Definition: threshold.c:1596
MagickRealType red
Definition: pixel.h:191
static RandomInfo ** DestroyRandomInfoThreadSet(RandomInfo **random_info)
static double StringToDouble(const char *magick_restrict string, char **magick_restrict sentinal)
static PixelTrait GetPixelChannelTraits(const Image *magick_restrict image, const PixelChannel channel)
static const char * MinimalThresholdMap
Definition: threshold.c:120
MagickExport Image * AdaptiveThresholdImage(const Image *image, const size_t width, const size_t height, const double bias, ExceptionInfo *exception)
Definition: threshold.c:178
#define ThresholdsFilename
Definition: threshold.c:96
MagickExport ssize_t FormatLocaleString(char *magick_restrict string, const size_t length, const char *magick_restrict format,...)
Definition: locale.c:499
static void * AcquireCriticalMemory(const size_t size)
static MagickBooleanType IsGrayColorspace(const ColorspaceType colorspace)
char * map_id
Definition: threshold.c:104
MagickExport const Quantum * GetCacheViewVirtualPixels(const CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:651
static RandomInfo ** AcquireRandomInfoThreadSet(void)
static long StringToLong(const char *magick_restrict value)
MagickRealType alpha
Definition: pixel.h:191
#define MagickEpsilon
Definition: magick-type.h:110
MagickExport XMLTreeInfo * GetXMLTreeChild(XMLTreeInfo *xml_info, const char *tag)
Definition: xml-tree.c:896
double sigma
Definition: geometry.h:106
MagickExport MagickBooleanType PerceptibleImage(Image *image, const double epsilon, ExceptionInfo *exception)
Definition: threshold.c:1875
ClassType storage_class
Definition: image.h:154
#define ThrowBinaryException(severity, tag, context)
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:129
MagickExport unsigned long GetRandomSecretKey(const RandomInfo *random_info)
Definition: random.c:743
MagickExport void GetPixelInfo(const Image *image, PixelInfo *pixel)
Definition: pixel.c:2170
MagickBooleanType ListThresholdMapFile(FILE *file, const char *xml, const char *filename, ExceptionInfo *exception)
Definition: threshold.c:1512
#define PerceptibleImageTag
Definition: image.h:151
char * description
Definition: threshold.c:104
MagickExport void * GetNextValueInLinkedList(LinkedListInfo *list_info)
Definition: linked-list.c:305
#define MagickMinimumValue
Definition: magick-type.h:112
#define MagickCoreSignature
MagickExport Quantum * GetCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:299
static Quantum ClampPixel(const MagickRealType pixel)
MagickExport unsigned char * GetStringInfoDatum(const StringInfo *string_info)
Definition: string.c:1215
MagickExport LinkedListInfo * GetConfigureOptions(const char *filename, ExceptionInfo *exception)
Definition: configure.c:639
MagickExport ssize_t FormatLocaleFile(FILE *file, const char *magick_restrict format,...)
Definition: locale.c:404
MagickBooleanType
Definition: magick-type.h:158
unsigned int MagickStatusType
Definition: magick-type.h:121
static double PerceptibleReciprocal(const double x)
#define ThresholdImageTag
MagickExport void * AcquireQuantumMemory(const size_t count, const size_t quantum)
Definition: memory.c:543
#define MaxIntensity
static int GetOpenMPThreadId(void)
size_t height
Definition: threshold.c:108
ChannelType channel_mask
Definition: image.h:288
MagickExport MagickBooleanType SetImageProperty(Image *image, const char *property, const char *value, ExceptionInfo *exception)
Definition: property.c:4114
MagickExport const char * GetXMLTreeContent(XMLTreeInfo *xml_info)
Definition: xml-tree.c:936
ssize_t divisor
Definition: threshold.c:112
size_t MagickSizeType
Definition: magick-type.h:130
#define MagickPathExtent
static double OTSUThreshold(const Image *image, const double *histogram, ExceptionInfo *exception)
Definition: threshold.c:486
MagickExport ThresholdMap * GetThresholdMap(const char *map_id, ExceptionInfo *exception)
Definition: threshold.c:1243
#define ClampImageTag
MagickRealType blue
Definition: pixel.h:191
MagickExport Quantum * QueueCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:977
MagickExport MagickBooleanType ThrowMagickException(ExceptionInfo *exception, const char *module, const char *function, const size_t line, const ExceptionType severity, const char *tag, const char *format,...)
Definition: exception.c:1145
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1399
size_t signature
Definition: image.h:354
#define QuantumScale
Definition: magick-type.h:115
size_t columns
Definition: image.h:172
MagickExport double GetPseudoRandomValue(RandomInfo *random_info)
Definition: random.c:610
#define AdaptiveThresholdImageTag
MagickExport MagickBooleanType SetImageStorageClass(Image *image, const ClassType storage_class, ExceptionInfo *exception)
Definition: image.c:2617
PixelChannel
Definition: pixel.h:67
size_t colors
Definition: image.h:172
static size_t GetPixelChannels(const Image *magick_restrict image)
MagickExport int LocaleCompare(const char *p, const char *q)
Definition: locale.c:1435
char filename[MagickPathExtent]
Definition: image.h:319
#define GetMagickModule()
Definition: log.h:28
double chi
Definition: geometry.h:106
MagickExport const char * GetStringInfoPath(const StringInfo *string_info)
Definition: string.c:1302
static Quantum ClampToQuantum(const MagickRealType value)
Definition: quantum.h:84
static PixelChannel GetPixelChannelChannel(const Image *magick_restrict image, const ssize_t offset)
static MagickRealType GetPixelInfoChannel(const PixelInfo *magick_restrict pixel_info, const PixelChannel channel)
MagickExport CacheView * AcquireVirtualCacheView(const Image *image, ExceptionInfo *exception)
Definition: cache-view.c:149
MagickExport void GetNextToken(const char *start, const char **end, const size_t extent, char *token)
Definition: token.c:172
AutoThresholdMethod
Definition: threshold.h:25
MagickExport const char * GetXMLTreeAttribute(XMLTreeInfo *xml_info, const char *tag)
Definition: xml-tree.c:779
MagickExport MagickBooleanType RandomThresholdImage(Image *image, const double min_threshold, const double max_threshold, ExceptionInfo *exception)
Definition: threshold.c:2011
unsigned short Quantum
Definition: magick-type.h:82
double xi
Definition: geometry.h:106
MagickExport MagickBooleanType SetImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1135
MagickRealType black
Definition: pixel.h:191
MagickExport char * DestroyString(char *string)
Definition: string.c:823
MagickExport MagickStatusType ParseGeometry(const char *geometry, GeometryInfo *geometry_info)
Definition: geometry.c:853
static void SetPixelChannel(const Image *magick_restrict image, const PixelChannel channel, const Quantum quantum, Quantum *magick_restrict pixel)
MagickExport ThresholdMap * DestroyThresholdMap(ThresholdMap *map)
Definition: threshold.c:1204
static ThresholdMap * GetThresholdMapFile(const char *, const char *, const char *, ExceptionInfo *)
Definition: threshold.c:1306
MagickExport MagickBooleanType BilevelImage(Image *image, const double threshold, ExceptionInfo *exception)
Definition: threshold.c:800
static RandomInfo * random_info
Definition: resource.c:113
static Quantum PerceptibleThreshold(const Quantum quantum, const double epsilon)
Definition: threshold.c:1863
MagickExport void * RelinquishMagickMemory(void *memory)
Definition: memory.c:1069
#define MaxPixelChannels
Definition: pixel.h:27
MagickExport MagickBooleanType WhiteThresholdImage(Image *image, const char *thresholds, ExceptionInfo *exception)
Definition: threshold.c:2301
MagickExport MagickBooleanType ClampImage(Image *image, ExceptionInfo *exception)
Definition: threshold.c:1082
MagickRealType green
Definition: pixel.h:191
MagickBooleanType(* MagickProgressMonitor)(const char *, const MagickOffsetType, const MagickSizeType, void *)
Definition: monitor.h:26
static double KapurThreshold(const Image *image, const double *histogram, ExceptionInfo *exception)
Definition: threshold.c:387
#define MagickExport
MagickExport MagickBooleanType SyncCacheViewAuthenticPixels(CacheView *magick_restrict cache_view, ExceptionInfo *exception)
Definition: cache-view.c:1100
MagickExport CacheView * AcquireAuthenticCacheView(const Image *image, ExceptionInfo *exception)
Definition: cache-view.c:112
ColorspaceType colorspace
Definition: pixel.h:176
MagickExport MagickBooleanType OrderedDitherImage(Image *image, const char *threshold_map, ExceptionInfo *exception)
Definition: threshold.c:1673
PixelTrait
Definition: pixel.h:135
static double TriangleThreshold(const double *histogram, ExceptionInfo *exception)
Definition: threshold.c:565
MagickExport MagickRealType GetPixelIntensity(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
Definition: pixel.c:2358
MagickExport LinkedListInfo * DestroyConfigureOptions(LinkedListInfo *options)
Definition: configure.c:311
MagickExport MagickBooleanType AutoThresholdImage(Image *image, const AutoThresholdMethod method, ExceptionInfo *exception)
Definition: threshold.c:657
ssize_t * levels
Definition: threshold.c:112
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1181
MagickExport char * ConstantString(const char *source)
Definition: string.c:700
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:796
ColorspaceType colorspace
Definition: image.h:157
MagickExport XMLTreeInfo * NewXMLTree(const char *xml, ExceptionInfo *exception)
Definition: xml-tree.c:1951
#define QuantumRange
Definition: magick-type.h:83
MagickExport MagickBooleanType SetImageProgress(const Image *image, const char *tag, const MagickOffsetType offset, const MagickSizeType extent)
Definition: monitor.c:136
MagickBooleanType debug
Definition: image.h:334