MagickCore  7.0.8
Convert, Edit, Or Compose Bitmap Images
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-2018 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://www.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,
213  exception);
214  if (threshold_image == (Image *) NULL)
215  return((Image *) NULL);
216  status=SetImageStorageClass(threshold_image,DirectClass,exception);
217  if (status == MagickFalse)
218  {
219  threshold_image=DestroyImage(threshold_image);
220  return((Image *) NULL);
221  }
222  /*
223  Threshold image.
224  */
225  status=MagickTrue;
226  progress=0;
227  number_pixels=(MagickSizeType) width*height;
228  image_view=AcquireVirtualCacheView(image,exception);
229  threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
230 #if defined(MAGICKCORE_OPENMP_SUPPORT)
231  #pragma omp parallel for schedule(static) shared(progress,status) \
232  magick_number_threads(image,threshold_image,image->rows,1)
233 #endif
234  for (y=0; y < (ssize_t) image->rows; y++)
235  {
236  double
237  channel_bias[MaxPixelChannels],
238  channel_sum[MaxPixelChannels];
239 
240  register const Quantum
241  *magick_restrict p,
242  *magick_restrict pixels;
243 
244  register Quantum
245  *magick_restrict q;
246 
247  register ssize_t
248  i,
249  x;
250 
251  ssize_t
252  center,
253  u,
254  v;
255 
256  if (status == MagickFalse)
257  continue;
258  p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
259  (height/2L),image->columns+width,height,exception);
260  q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
261  1,exception);
262  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
263  {
264  status=MagickFalse;
265  continue;
266  }
267  center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
268  GetPixelChannels(image)*(width/2);
269  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
270  {
271  PixelChannel channel = GetPixelChannelChannel(image,i);
272  PixelTrait traits = GetPixelChannelTraits(image,channel);
273  PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
274  channel);
275  if ((traits == UndefinedPixelTrait) ||
276  (threshold_traits == UndefinedPixelTrait))
277  continue;
278  if ((threshold_traits & CopyPixelTrait) != 0)
279  {
280  SetPixelChannel(threshold_image,channel,p[center+i],q);
281  continue;
282  }
283  pixels=p;
284  channel_bias[channel]=0.0;
285  channel_sum[channel]=0.0;
286  for (v=0; v < (ssize_t) height; v++)
287  {
288  for (u=0; u < (ssize_t) width; u++)
289  {
290  if (u == (ssize_t) (width-1))
291  channel_bias[channel]+=pixels[i];
292  channel_sum[channel]+=pixels[i];
293  pixels+=GetPixelChannels(image);
294  }
295  pixels+=GetPixelChannels(image)*image->columns;
296  }
297  }
298  for (x=0; x < (ssize_t) image->columns; x++)
299  {
300  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
301  {
302  double
303  mean;
304 
305  PixelChannel channel = GetPixelChannelChannel(image,i);
306  PixelTrait traits = GetPixelChannelTraits(image,channel);
307  PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
308  channel);
309  if ((traits == UndefinedPixelTrait) ||
310  (threshold_traits == UndefinedPixelTrait))
311  continue;
312  if ((threshold_traits & CopyPixelTrait) != 0)
313  {
314  SetPixelChannel(threshold_image,channel,p[center+i],q);
315  continue;
316  }
317  channel_sum[channel]-=channel_bias[channel];
318  channel_bias[channel]=0.0;
319  pixels=p;
320  for (v=0; v < (ssize_t) height; v++)
321  {
322  channel_bias[channel]+=pixels[i];
323  pixels+=(width-1)*GetPixelChannels(image);
324  channel_sum[channel]+=pixels[i];
325  pixels+=GetPixelChannels(image)*(image->columns+1);
326  }
327  mean=(double) (channel_sum[channel]/number_pixels+bias);
328  SetPixelChannel(threshold_image,channel,(Quantum) ((double)
329  p[center+i] <= mean ? 0 : QuantumRange),q);
330  }
331  p+=GetPixelChannels(image);
332  q+=GetPixelChannels(threshold_image);
333  }
334  if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
335  status=MagickFalse;
336  if (image->progress_monitor != (MagickProgressMonitor) NULL)
337  {
339  proceed;
340 
341 #if defined(MAGICKCORE_OPENMP_SUPPORT)
342  #pragma omp critical (MagickCore_AdaptiveThresholdImage)
343 #endif
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 selects a threshold and replaces each
370 % pixel in the image with a black pixel if the image intentsity is less than
371 % the selected threshold otherwise white.
372 %
373 % The format of the AutoThresholdImage method is:
374 %
375 % MagickBooleanType AutoThresholdImage(Image *image,
376 % const AutoThresholdMethod method,ExceptionInfo *exception)
377 %
378 % A description of each parameter follows:
379 %
380 % o image: The image to auto-threshold.
381 %
382 % o method: choose from Kapur, OTSU, or Triangle.
383 %
384 % o exception: return any errors or warnings in this structure.
385 %
386 */
387 
388 static double KapurThreshold(const Image *image,const double *histogram,
389  ExceptionInfo *exception)
390 {
391 #define MaxIntensity 255
392 
393  double
394  *black_entropy,
395  *cumulative_histogram,
396  entropy,
397  epsilon,
398  maximum_entropy,
399  *white_entropy;
400 
401  register ssize_t
402  i,
403  j;
404 
405  size_t
406  threshold;
407 
408  /*
409  Compute optimal threshold from the entopy of the histogram.
410  */
411  cumulative_histogram=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
412  sizeof(*cumulative_histogram));
413  black_entropy=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
414  sizeof(*black_entropy));
415  white_entropy=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
416  sizeof(*white_entropy));
417  if ((cumulative_histogram == (double *) NULL) ||
418  (black_entropy == (double *) NULL) || (white_entropy == (double *) NULL))
419  {
420  if (white_entropy != (double *) NULL)
421  white_entropy=(double *) RelinquishMagickMemory(white_entropy);
422  if (black_entropy != (double *) NULL)
423  black_entropy=(double *) RelinquishMagickMemory(black_entropy);
424  if (cumulative_histogram != (double *) NULL)
425  cumulative_histogram=(double *)
426  RelinquishMagickMemory(cumulative_histogram);
427  (void) ThrowMagickException(exception,GetMagickModule(),
428  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
429  return(-1.0);
430  }
431  /*
432  Entropy for black and white parts of the histogram.
433  */
434  cumulative_histogram[0]=histogram[0];
435  for (i=1; i <= MaxIntensity; i++)
436  cumulative_histogram[i]=cumulative_histogram[i-1]+histogram[i];
437  epsilon=MagickMinimumValue;
438  for (j=0; j <= MaxIntensity; j++)
439  {
440  /*
441  Black entropy.
442  */
443  black_entropy[j]=0.0;
444  if (cumulative_histogram[j] > epsilon)
445  {
446  entropy=0.0;
447  for (i=0; i <= j; i++)
448  if (histogram[i] > epsilon)
449  entropy-=histogram[i]/cumulative_histogram[j]*
450  log(histogram[i]/cumulative_histogram[j]);
451  black_entropy[j]=entropy;
452  }
453  /*
454  White entropy.
455  */
456  white_entropy[j]=0.0;
457  if ((1.0-cumulative_histogram[j]) > epsilon)
458  {
459  entropy=0.0;
460  for (i=j+1; i <= MaxIntensity; i++)
461  if (histogram[i] > epsilon)
462  entropy-=histogram[i]/(1.0-cumulative_histogram[j])*
463  log(histogram[i]/(1.0-cumulative_histogram[j]));
464  white_entropy[j]=entropy;
465  }
466  }
467  /*
468  Find histogram bin with maximum entropy.
469  */
470  maximum_entropy=black_entropy[0]+white_entropy[0];
471  threshold=0;
472  for (j=1; j <= MaxIntensity; j++)
473  if ((black_entropy[j]+white_entropy[j]) > maximum_entropy)
474  {
475  maximum_entropy=black_entropy[j]+white_entropy[j];
476  threshold=(size_t) j;
477  }
478  /*
479  Free resources.
480  */
481  white_entropy=(double *) RelinquishMagickMemory(white_entropy);
482  black_entropy=(double *) RelinquishMagickMemory(black_entropy);
483  cumulative_histogram=(double *) RelinquishMagickMemory(cumulative_histogram);
484  return(100.0*threshold/MaxIntensity);
485 }
486 
487 static double OTSUThreshold(const Image *image,const double *histogram,
488  ExceptionInfo *exception)
489 {
490  double
491  max_sigma,
492  *myu,
493  *omega,
494  *probability,
495  *sigma,
496  threshold;
497 
498  register ssize_t
499  i;
500 
501  /*
502  Compute optimal threshold from maximization of inter-class variance.
503  */
504  myu=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*myu));
505  omega=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*omega));
506  probability=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
507  sizeof(*probability));
508  sigma=(double *) AcquireQuantumMemory(MaxIntensity+1UL,sizeof(*sigma));
509  if ((myu == (double *) NULL) || (omega == (double *) NULL) ||
510  (probability == (double *) NULL) || (sigma == (double *) NULL))
511  {
512  if (sigma != (double *) NULL)
513  sigma=(double *) RelinquishMagickMemory(sigma);
514  if (probability != (double *) NULL)
515  probability=(double *) RelinquishMagickMemory(probability);
516  if (omega != (double *) NULL)
517  omega=(double *) RelinquishMagickMemory(omega);
518  if (myu != (double *) NULL)
519  myu=(double *) RelinquishMagickMemory(myu);
520  (void) ThrowMagickException(exception,GetMagickModule(),
521  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
522  return(-1.0);
523  }
524  /*
525  Calculate probability density.
526  */
527  for (i=0; i <= (ssize_t) MaxIntensity; i++)
528  probability[i]=histogram[i];
529  /*
530  Generate probability of graylevels and mean value for separation.
531  */
532  omega[0]=probability[0];
533  myu[0]=0.0;
534  for (i=1; i <= (ssize_t) MaxIntensity; i++)
535  {
536  omega[i]=omega[i-1]+probability[i];
537  myu[i]=myu[i-1]+i*probability[i];
538  }
539  /*
540  Sigma maximization: inter-class variance and compute optimal threshold.
541  */
542  threshold=0;
543  max_sigma=0.0;
544  for (i=0; i < (ssize_t) MaxIntensity; i++)
545  {
546  sigma[i]=0.0;
547  if ((omega[i] != 0.0) && (omega[i] != 1.0))
548  sigma[i]=pow(myu[MaxIntensity]*omega[i]-myu[i],2.0)/(omega[i]*(1.0-
549  omega[i]));
550  if (sigma[i] > max_sigma)
551  {
552  max_sigma=sigma[i];
553  threshold=(double) i;
554  }
555  }
556  /*
557  Free resources.
558  */
559  myu=(double *) RelinquishMagickMemory(myu);
560  omega=(double *) RelinquishMagickMemory(omega);
561  probability=(double *) RelinquishMagickMemory(probability);
562  sigma=(double *) RelinquishMagickMemory(sigma);
563  return(100.0*threshold/MaxIntensity);
564 }
565 
566 static double TriangleThreshold(const Image *image,const double *histogram,
567  ExceptionInfo *exception)
568 {
569  double
570  a,
571  b,
572  c,
573  count,
574  distance,
575  inverse_ratio,
576  max_distance,
577  segment,
578  x1,
579  x2,
580  y1,
581  y2;
582 
583  register ssize_t
584  i;
585 
586  ssize_t
587  end,
588  max,
589  start,
590  threshold;
591 
592  /*
593  Compute optimal threshold with triangle algorithm.
594  */
595  (void) exception;
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)
599  {
600  start=i;
601  break;
602  }
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)
606  {
607  end=i;
608  break;
609  }
610  max=0; /* find max bin, bin with largest count */
611  count=0.0;
612  for (i=0; i <= (ssize_t) MaxIntensity; i++)
613  if (histogram[i] > count)
614  {
615  max=i;
616  count=histogram[i];
617  }
618  /*
619  Compute threshold at split point.
620  */
621  x1=(double) max;
622  y1=histogram[max];
623  x2=(double) end;
624  if ((max-start) >= (end-max))
625  x2=(double) start;
626  y2=0.0;
627  a=y1-y2;
628  b=x2-x1;
629  c=(-1.0)*(a*x1+b*y1);
630  inverse_ratio=1.0/sqrt(a*a+b*b+c*c);
631  threshold=0;
632  max_distance=0.0;
633  if (x2 == (double) start)
634  for (i=start; i < max; i++)
635  {
636  segment=inverse_ratio*(a*i+b*histogram[i]+c);
637  distance=sqrt(segment*segment);
638  if ((distance > max_distance) && (segment > 0.0))
639  {
640  threshold=i;
641  max_distance=distance;
642  }
643  }
644  else
645  for (i=end; i > max; i--)
646  {
647  segment=inverse_ratio*(a*i+b*histogram[i]+c);
648  distance=sqrt(segment*segment);
649  if ((distance > max_distance) && (segment < 0.0))
650  {
651  threshold=i;
652  max_distance=distance;
653  }
654  }
655  return(100.0*threshold/MaxIntensity);
656 }
657 
659  const AutoThresholdMethod method,ExceptionInfo *exception)
660 {
661  CacheView
662  *image_view;
663 
664  char
665  property[MagickPathExtent];
666 
667  double
668  gamma,
669  *histogram,
670  sum,
671  threshold;
672 
674  status;
675 
676  register ssize_t
677  i;
678 
679  ssize_t
680  y;
681 
682  /*
683  Form histogram.
684  */
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,
690  sizeof(*histogram));
691  if (histogram == (double *) NULL)
692  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
693  image->filename);
694  status=MagickTrue;
695  (void) memset(histogram,0,(MaxIntensity+1UL)*sizeof(*histogram));
696  image_view=AcquireVirtualCacheView(image,exception);
697  for (y=0; y < (ssize_t) image->rows; y++)
698  {
699  register const Quantum
700  *magick_restrict p;
701 
702  register ssize_t
703  x;
704 
705  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
706  if (p == (const Quantum *) NULL)
707  break;
708  for (x=0; x < (ssize_t) image->columns; x++)
709  {
710  double intensity = GetPixelIntensity(image,p);
711  histogram[ScaleQuantumToChar(ClampToQuantum(intensity))]++;
712  p+=GetPixelChannels(image);
713  }
714  }
715  image_view=DestroyCacheView(image_view);
716  /*
717  Normalize histogram.
718  */
719  sum=0.0;
720  for (i=0; i <= (ssize_t) MaxIntensity; i++)
721  sum+=histogram[i];
722  gamma=PerceptibleReciprocal(sum);
723  for (i=0; i <= (ssize_t) MaxIntensity; i++)
724  histogram[i]=gamma*histogram[i];
725  /*
726  Discover threshold from histogram.
727  */
728  switch (method)
729  {
731  {
732  threshold=KapurThreshold(image,histogram,exception);
733  break;
734  }
735  case OTSUThresholdMethod:
736  default:
737  {
738  threshold=OTSUThreshold(image,histogram,exception);
739  break;
740  }
742  {
743  threshold=TriangleThreshold(image,histogram,exception);
744  break;
745  }
746  }
747  histogram=(double *) RelinquishMagickMemory(histogram);
748  if (threshold < 0.0)
749  status=MagickFalse;
750  if (status == MagickFalse)
751  return(MagickFalse);
752  /*
753  Threshold image.
754  */
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));
758 }
759 
760 /*
761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762 % %
763 % %
764 % %
765 % B i l e v e l I m a g e %
766 % %
767 % %
768 % %
769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
770 %
771 % BilevelImage() changes the value of individual pixels based on the
772 % intensity of each pixel channel. The result is a high-contrast image.
773 %
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.
777 %
778 % This function is what is used to implement the "-threshold" operator for
779 % the command line API.
780 %
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.
783 %
784 % The format of the BilevelImage method is:
785 %
786 % MagickBooleanType BilevelImage(Image *image,const double threshold,
787 % ExceptionInfo *exception)
788 %
789 % A description of each parameter follows:
790 %
791 % o image: the image.
792 %
793 % o threshold: define the threshold values.
794 %
795 % o exception: return any errors or warnings in this structure.
796 %
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.
799 %
800 */
801 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
802  ExceptionInfo *exception)
803 {
804 #define ThresholdImageTag "Threshold/Image"
805 
806  CacheView
807  *image_view;
808 
810  status;
811 
813  progress;
814 
815  ssize_t
816  y;
817 
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)
823  return(MagickFalse);
824  if (IsGrayColorspace(image->colorspace) != MagickFalse)
825  (void) SetImageColorspace(image,sRGBColorspace,exception);
826  /*
827  Bilevel threshold image.
828  */
829  status=MagickTrue;
830  progress=0;
831  image_view=AcquireAuthenticCacheView(image,exception);
832 #if defined(MAGICKCORE_OPENMP_SUPPORT)
833  #pragma omp parallel for schedule(static) shared(progress,status) \
834  magick_number_threads(image,image,image->rows,1)
835 #endif
836  for (y=0; y < (ssize_t) image->rows; y++)
837  {
838  register ssize_t
839  x;
840 
841  register Quantum
842  *magick_restrict q;
843 
844  if (status == MagickFalse)
845  continue;
846  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
847  if (q == (Quantum *) NULL)
848  {
849  status=MagickFalse;
850  continue;
851  }
852  for (x=0; x < (ssize_t) image->columns; x++)
853  {
854  double
855  pixel;
856 
857  register ssize_t
858  i;
859 
860  pixel=GetPixelIntensity(image,q);
861  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
862  {
863  PixelChannel channel = GetPixelChannelChannel(image,i);
864  PixelTrait traits = GetPixelChannelTraits(image,channel);
865  if ((traits & UpdatePixelTrait) == 0)
866  continue;
867  if (image->channel_mask != DefaultChannels)
868  pixel=(double) q[i];
869  q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
870  }
871  q+=GetPixelChannels(image);
872  }
873  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
874  status=MagickFalse;
875  if (image->progress_monitor != (MagickProgressMonitor) NULL)
876  {
878  proceed;
879 
880 #if defined(MAGICKCORE_OPENMP_SUPPORT)
881  #pragma omp critical (MagickCore_BilevelImage)
882 #endif
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 critical (MagickCore_BlackThresholdImage)
1043 #endif
1044  proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1045  image->rows);
1046  if (proceed == MagickFalse)
1047  status=MagickFalse;
1048  }
1049  }
1050  image_view=DestroyCacheView(image_view);
1051  return(status);
1052 }
1053 
1054 /*
1055 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1056 % %
1057 % %
1058 % %
1059 % C l a m p I m a g e %
1060 % %
1061 % %
1062 % %
1063 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1064 %
1065 % ClampImage() set each pixel whose value is below zero to zero and any the
1066 % pixel whose value is above the quantum range to the quantum range (e.g.
1067 % 65535) otherwise the pixel value remains unchanged.
1068 %
1069 % The format of the ClampImage method is:
1070 %
1071 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
1072 %
1073 % A description of each parameter follows:
1074 %
1075 % o image: the image.
1076 %
1077 % o exception: return any errors or warnings in this structure.
1078 %
1079 */
1080 
1082 {
1083 #define ClampImageTag "Clamp/Image"
1084 
1085  CacheView
1086  *image_view;
1087 
1089  status;
1090 
1092  progress;
1093 
1094  ssize_t
1095  y;
1096 
1097  assert(image != (Image *) NULL);
1098  assert(image->signature == MagickCoreSignature);
1099  if (image->debug != MagickFalse)
1100  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1101  if (image->storage_class == PseudoClass)
1102  {
1103  register ssize_t
1104  i;
1105 
1106  register PixelInfo
1107  *magick_restrict q;
1108 
1109  q=image->colormap;
1110  for (i=0; i < (ssize_t) image->colors; i++)
1111  {
1112  q->red=(double) ClampPixel(q->red);
1113  q->green=(double) ClampPixel(q->green);
1114  q->blue=(double) ClampPixel(q->blue);
1115  q->alpha=(double) ClampPixel(q->alpha);
1116  q++;
1117  }
1118  return(SyncImage(image,exception));
1119  }
1120  /*
1121  Clamp image.
1122  */
1123  status=MagickTrue;
1124  progress=0;
1125  image_view=AcquireAuthenticCacheView(image,exception);
1126 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1127  #pragma omp parallel for schedule(static) shared(progress,status) \
1128  magick_number_threads(image,image,image->rows,1)
1129 #endif
1130  for (y=0; y < (ssize_t) image->rows; y++)
1131  {
1132  register ssize_t
1133  x;
1134 
1135  register Quantum
1136  *magick_restrict q;
1137 
1138  if (status == MagickFalse)
1139  continue;
1140  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1141  if (q == (Quantum *) NULL)
1142  {
1143  status=MagickFalse;
1144  continue;
1145  }
1146  for (x=0; x < (ssize_t) image->columns; x++)
1147  {
1148  register ssize_t
1149  i;
1150 
1151  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1152  {
1153  PixelChannel channel = GetPixelChannelChannel(image,i);
1154  PixelTrait traits = GetPixelChannelTraits(image,channel);
1155  if ((traits & UpdatePixelTrait) == 0)
1156  continue;
1157  q[i]=ClampPixel((MagickRealType) q[i]);
1158  }
1159  q+=GetPixelChannels(image);
1160  }
1161  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1162  status=MagickFalse;
1163  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1164  {
1166  proceed;
1167 
1168 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1169  #pragma omp critical (MagickCore_ClampImage)
1170 #endif
1171  proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
1172  if (proceed == MagickFalse)
1173  status=MagickFalse;
1174  }
1175  }
1176  image_view=DestroyCacheView(image_view);
1177  return(status);
1178 }
1179 
1180 /*
1181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182 % %
1183 % %
1184 % %
1185 % D e s t r o y T h r e s h o l d M a p %
1186 % %
1187 % %
1188 % %
1189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1190 %
1191 % DestroyThresholdMap() de-allocate the given ThresholdMap
1192 %
1193 % The format of the ListThresholdMaps method is:
1194 %
1195 % ThresholdMap *DestroyThresholdMap(Threshold *map)
1196 %
1197 % A description of each parameter follows.
1198 %
1199 % o map: Pointer to the Threshold map to destroy
1200 %
1201 */
1203 {
1204  assert(map != (ThresholdMap *) NULL);
1205  if (map->map_id != (char *) NULL)
1206  map->map_id=DestroyString(map->map_id);
1207  if (map->description != (char *) NULL)
1209  if (map->levels != (ssize_t *) NULL)
1210  map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1211  map=(ThresholdMap *) RelinquishMagickMemory(map);
1212  return(map);
1213 }
1214 
1215 /*
1216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217 % %
1218 % %
1219 % %
1220 % G e t T h r e s h o l d M a p %
1221 % %
1222 % %
1223 % %
1224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1225 %
1226 % GetThresholdMap() loads and searches one or more threshold map files for the
1227 % map matching the given name or alias.
1228 %
1229 % The format of the GetThresholdMap method is:
1230 %
1231 % ThresholdMap *GetThresholdMap(const char *map_id,
1232 % ExceptionInfo *exception)
1233 %
1234 % A description of each parameter follows.
1235 %
1236 % o map_id: ID of the map to look for.
1237 %
1238 % o exception: return any errors or warnings in this structure.
1239 %
1240 */
1242  ExceptionInfo *exception)
1243 {
1244  ThresholdMap
1245  *map;
1246 
1247  map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
1248  if (map != (ThresholdMap *) NULL)
1249  return(map);
1250 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
1251  {
1252  const StringInfo
1253  *option;
1254 
1256  *options;
1257 
1258  options=GetConfigureOptions(ThresholdsFilename,exception);
1259  option=(const StringInfo *) GetNextValueInLinkedList(options);
1260  while (option != (const StringInfo *) NULL)
1261  {
1262  map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1263  GetStringInfoPath(option),map_id,exception);
1264  if (map != (ThresholdMap *) NULL)
1265  break;
1266  option=(const StringInfo *) GetNextValueInLinkedList(options);
1267  }
1268  options=DestroyConfigureOptions(options);
1269  }
1270 #endif
1271  return(map);
1272 }
1273 
1274 /*
1275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276 % %
1277 % %
1278 % %
1279 + G e t T h r e s h o l d M a p F i l e %
1280 % %
1281 % %
1282 % %
1283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1284 %
1285 % GetThresholdMapFile() look for a given threshold map name or alias in the
1286 % given XML file data, and return the allocated the map when found.
1287 %
1288 % The format of the ListThresholdMaps method is:
1289 %
1290 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
1291 % const char *map_id,ExceptionInfo *exception)
1292 %
1293 % A description of each parameter follows.
1294 %
1295 % o xml: The threshold map list in XML format.
1296 %
1297 % o filename: The threshold map XML filename.
1298 %
1299 % o map_id: ID of the map to look for in XML list.
1300 %
1301 % o exception: return any errors or warnings in this structure.
1302 %
1303 */
1304 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
1305  const char *map_id,ExceptionInfo *exception)
1306 {
1307  char
1308  *p;
1309 
1310  const char
1311  *attribute,
1312  *content;
1313 
1314  double
1315  value;
1316 
1317  register ssize_t
1318  i;
1319 
1320  ThresholdMap
1321  *map;
1322 
1323  XMLTreeInfo
1324  *description,
1325  *levels,
1326  *threshold,
1327  *thresholds;
1328 
1330  "Loading threshold map file \"%s\" ...",filename);
1331  map=(ThresholdMap *) NULL;
1332  thresholds=NewXMLTree(xml,exception);
1333  if (thresholds == (XMLTreeInfo *) NULL)
1334  return(map);
1335  for (threshold=GetXMLTreeChild(thresholds,"threshold");
1336  threshold != (XMLTreeInfo *) NULL;
1337  threshold=GetNextXMLTreeTag(threshold))
1338  {
1339  attribute=GetXMLTreeAttribute(threshold,"map");
1340  if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1341  break;
1342  attribute=GetXMLTreeAttribute(threshold,"alias");
1343  if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1344  break;
1345  }
1346  if (threshold == (XMLTreeInfo *) NULL)
1347  {
1348  thresholds=DestroyXMLTree(thresholds);
1349  return(map);
1350  }
1351  description=GetXMLTreeChild(threshold,"description");
1352  if (description == (XMLTreeInfo *) NULL)
1353  {
1355  "XmlMissingElement", "<description>, map \"%s\"",map_id);
1356  thresholds=DestroyXMLTree(thresholds);
1357  return(map);
1358  }
1359  levels=GetXMLTreeChild(threshold,"levels");
1360  if (levels == (XMLTreeInfo *) NULL)
1361  {
1363  "XmlMissingElement", "<levels>, map \"%s\"", map_id);
1364  thresholds=DestroyXMLTree(thresholds);
1365  return(map);
1366  }
1367  map=(ThresholdMap *) AcquireCriticalMemory(sizeof(*map));
1368  map->map_id=(char *) NULL;
1369  map->description=(char *) NULL;
1370  map->levels=(ssize_t *) NULL;
1371  attribute=GetXMLTreeAttribute(threshold,"map");
1372  if (attribute != (char *) NULL)
1373  map->map_id=ConstantString(attribute);
1374  content=GetXMLTreeContent(description);
1375  if (content != (char *) NULL)
1376  map->description=ConstantString(content);
1377  attribute=GetXMLTreeAttribute(levels,"width");
1378  if (attribute == (char *) NULL)
1379  {
1381  "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1382  thresholds=DestroyXMLTree(thresholds);
1383  map=DestroyThresholdMap(map);
1384  return(map);
1385  }
1386  map->width=StringToUnsignedLong(attribute);
1387  if (map->width == 0)
1388  {
1390  "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1391  thresholds=DestroyXMLTree(thresholds);
1392  map=DestroyThresholdMap(map);
1393  return(map);
1394  }
1395  attribute=GetXMLTreeAttribute(levels,"height");
1396  if (attribute == (char *) NULL)
1397  {
1399  "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1400  thresholds=DestroyXMLTree(thresholds);
1401  map=DestroyThresholdMap(map);
1402  return(map);
1403  }
1404  map->height=StringToUnsignedLong(attribute);
1405  if (map->height == 0)
1406  {
1408  "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1409  thresholds=DestroyXMLTree(thresholds);
1410  map=DestroyThresholdMap(map);
1411  return(map);
1412  }
1413  attribute=GetXMLTreeAttribute(levels,"divisor");
1414  if (attribute == (char *) NULL)
1415  {
1417  "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1418  thresholds=DestroyXMLTree(thresholds);
1419  map=DestroyThresholdMap(map);
1420  return(map);
1421  }
1422  map->divisor=(ssize_t) StringToLong(attribute);
1423  if (map->divisor < 2)
1424  {
1426  "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1427  thresholds=DestroyXMLTree(thresholds);
1428  map=DestroyThresholdMap(map);
1429  return(map);
1430  }
1431  content=GetXMLTreeContent(levels);
1432  if (content == (char *) NULL)
1433  {
1435  "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1436  thresholds=DestroyXMLTree(thresholds);
1437  map=DestroyThresholdMap(map);
1438  return(map);
1439  }
1440  map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1441  sizeof(*map->levels));
1442  if (map->levels == (ssize_t *) NULL)
1443  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1444  for (i=0; i < (ssize_t) (map->width*map->height); i++)
1445  {
1446  map->levels[i]=(ssize_t) strtol(content,&p,10);
1447  if (p == content)
1448  {
1450  "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1451  thresholds=DestroyXMLTree(thresholds);
1452  map=DestroyThresholdMap(map);
1453  return(map);
1454  }
1455  if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1456  {
1458  "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1459  (double) map->levels[i],map_id);
1460  thresholds=DestroyXMLTree(thresholds);
1461  map=DestroyThresholdMap(map);
1462  return(map);
1463  }
1464  content=p;
1465  }
1466  value=(double) strtol(content,&p,10);
1467  (void) value;
1468  if (p != content)
1469  {
1471  "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1472  thresholds=DestroyXMLTree(thresholds);
1473  map=DestroyThresholdMap(map);
1474  return(map);
1475  }
1476  thresholds=DestroyXMLTree(thresholds);
1477  return(map);
1478 }
1479 
1480 /*
1481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1482 % %
1483 % %
1484 % %
1485 + L i s t T h r e s h o l d M a p F i l e %
1486 % %
1487 % %
1488 % %
1489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1490 %
1491 % ListThresholdMapFile() lists the threshold maps and their descriptions
1492 % in the given XML file data.
1493 %
1494 % The format of the ListThresholdMaps method is:
1495 %
1496 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1497 % const char *filename,ExceptionInfo *exception)
1498 %
1499 % A description of each parameter follows.
1500 %
1501 % o file: An pointer to the output FILE.
1502 %
1503 % o xml: The threshold map list in XML format.
1504 %
1505 % o filename: The threshold map XML filename.
1506 %
1507 % o exception: return any errors or warnings in this structure.
1508 %
1509 */
1510 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1511  const char *filename,ExceptionInfo *exception)
1512 {
1513  const char
1514  *alias,
1515  *content,
1516  *map;
1517 
1518  XMLTreeInfo
1519  *description,
1520  *threshold,
1521  *thresholds;
1522 
1523  assert( xml != (char *) NULL );
1524  assert( file != (FILE *) NULL );
1526  "Loading threshold map file \"%s\" ...",filename);
1527  thresholds=NewXMLTree(xml,exception);
1528  if ( thresholds == (XMLTreeInfo *) NULL )
1529  return(MagickFalse);
1530  (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1531  (void) FormatLocaleFile(file,
1532  "----------------------------------------------------\n");
1533  threshold=GetXMLTreeChild(thresholds,"threshold");
1534  for ( ; threshold != (XMLTreeInfo *) NULL;
1535  threshold=GetNextXMLTreeTag(threshold))
1536  {
1537  map=GetXMLTreeAttribute(threshold,"map");
1538  if (map == (char *) NULL)
1539  {
1541  "XmlMissingAttribute", "<map>");
1542  thresholds=DestroyXMLTree(thresholds);
1543  return(MagickFalse);
1544  }
1545  alias=GetXMLTreeAttribute(threshold,"alias");
1546  description=GetXMLTreeChild(threshold,"description");
1547  if (description == (XMLTreeInfo *) NULL)
1548  {
1550  "XmlMissingElement", "<description>, map \"%s\"",map);
1551  thresholds=DestroyXMLTree(thresholds);
1552  return(MagickFalse);
1553  }
1554  content=GetXMLTreeContent(description);
1555  if (content == (char *) NULL)
1556  {
1558  "XmlMissingContent", "<description>, map \"%s\"", map);
1559  thresholds=DestroyXMLTree(thresholds);
1560  return(MagickFalse);
1561  }
1562  (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1563  content);
1564  }
1565  thresholds=DestroyXMLTree(thresholds);
1566  return(MagickTrue);
1567 }
1568 
1569 /*
1570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1571 % %
1572 % %
1573 % %
1574 % L i s t T h r e s h o l d M a p s %
1575 % %
1576 % %
1577 % %
1578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1579 %
1580 % ListThresholdMaps() lists the threshold maps and their descriptions
1581 % as defined by "threshold.xml" to a file.
1582 %
1583 % The format of the ListThresholdMaps method is:
1584 %
1585 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1586 %
1587 % A description of each parameter follows.
1588 %
1589 % o file: An pointer to the output FILE.
1590 %
1591 % o exception: return any errors or warnings in this structure.
1592 %
1593 */
1595  ExceptionInfo *exception)
1596 {
1597  const StringInfo
1598  *option;
1599 
1601  *options;
1602 
1604  status;
1605 
1606  status=MagickTrue;
1607  if (file == (FILE *) NULL)
1608  file=stdout;
1609  options=GetConfigureOptions(ThresholdsFilename,exception);
1610  (void) FormatLocaleFile(file,
1611  "\n Threshold Maps for Ordered Dither Operations\n");
1612  option=(const StringInfo *) GetNextValueInLinkedList(options);
1613  while (option != (const StringInfo *) NULL)
1614  {
1615  (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1616  status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1617  GetStringInfoPath(option),exception);
1618  option=(const StringInfo *) GetNextValueInLinkedList(options);
1619  }
1620  options=DestroyConfigureOptions(options);
1621  return(status != 0 ? MagickTrue : MagickFalse);
1622 }
1623 
1624 /*
1625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626 % %
1627 % %
1628 % %
1629 % O r d e r e d D i t h e r I m a g e %
1630 % %
1631 % %
1632 % %
1633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1634 %
1635 % OrderedDitherImage() will perform a ordered dither based on a number
1636 % of pre-defined dithering threshold maps, but over multiple intensity
1637 % levels, which can be different for different channels, according to the
1638 % input argument.
1639 %
1640 % The format of the OrderedDitherImage method is:
1641 %
1642 % MagickBooleanType OrderedDitherImage(Image *image,
1643 % const char *threshold_map,ExceptionInfo *exception)
1644 %
1645 % A description of each parameter follows:
1646 %
1647 % o image: the image.
1648 %
1649 % o threshold_map: A string containing the name of the threshold dither
1650 % map to use, followed by zero or more numbers representing the number
1651 % of color levels tho dither between.
1652 %
1653 % Any level number less than 2 will be equivalent to 2, and means only
1654 % binary dithering will be applied to each color channel.
1655 %
1656 % No numbers also means a 2 level (bitmap) dither will be applied to all
1657 % channels, while a single number is the number of levels applied to each
1658 % channel in sequence. More numbers will be applied in turn to each of
1659 % the color channels.
1660 %
1661 % For example: "o3x3,6" will generate a 6 level posterization of the
1662 % image with a ordered 3x3 diffused pixel dither being applied between
1663 % each level. While checker,8,8,4 will produce a 332 colormaped image
1664 % with only a single checkerboard hash pattern (50% grey) between each
1665 % color level, to basically double the number of color levels with
1666 % a bare minimim of dithering.
1667 %
1668 % o exception: return any errors or warnings in this structure.
1669 %
1670 */
1672  const char *threshold_map,ExceptionInfo *exception)
1673 {
1674 #define DitherImageTag "Dither/Image"
1675 
1676  CacheView
1677  *image_view;
1678 
1679  char
1680  token[MagickPathExtent];
1681 
1682  const char
1683  *p;
1684 
1685  double
1686  levels[CompositePixelChannel];
1687 
1689  status;
1690 
1692  progress;
1693 
1694  register ssize_t
1695  i;
1696 
1697  ssize_t
1698  y;
1699 
1700  ThresholdMap
1701  *map;
1702 
1703  assert(image != (Image *) NULL);
1704  assert(image->signature == MagickCoreSignature);
1705  if (image->debug != MagickFalse)
1706  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1707  assert(exception != (ExceptionInfo *) NULL);
1708  assert(exception->signature == MagickCoreSignature);
1709  if (threshold_map == (const char *) NULL)
1710  return(MagickTrue);
1711  p=(char *) threshold_map;
1712  while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1713  (*p != '\0'))
1714  p++;
1715  threshold_map=p;
1716  while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1717  (*p != '\0'))
1718  {
1719  if ((p-threshold_map) >= (MagickPathExtent-1))
1720  break;
1721  token[p-threshold_map]=(*p);
1722  p++;
1723  }
1724  token[p-threshold_map]='\0';
1725  map=GetThresholdMap(token,exception);
1726  if (map == (ThresholdMap *) NULL)
1727  {
1729  "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1730  return(MagickFalse);
1731  }
1732  for (i=0; i < MaxPixelChannels; i++)
1733  levels[i]=2.0;
1734  p=strchr((char *) threshold_map,',');
1735  if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1736  {
1737  GetNextToken(p,&p,MagickPathExtent,token);
1738  for (i=0; (i < MaxPixelChannels); i++)
1739  levels[i]=StringToDouble(token,(char **) NULL);
1740  for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1741  {
1742  GetNextToken(p,&p,MagickPathExtent,token);
1743  if (*token == ',')
1744  GetNextToken(p,&p,MagickPathExtent,token);
1745  levels[i]=StringToDouble(token,(char **) NULL);
1746  }
1747  }
1748  for (i=0; i < MaxPixelChannels; i++)
1749  if (fabs(levels[i]) >= 1)
1750  levels[i]-=1.0;
1751  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1752  return(MagickFalse);
1753  status=MagickTrue;
1754  progress=0;
1755  image_view=AcquireAuthenticCacheView(image,exception);
1756 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1757  #pragma omp parallel for schedule(static) shared(progress,status) \
1758  magick_number_threads(image,image,image->rows,1)
1759 #endif
1760  for (y=0; y < (ssize_t) image->rows; y++)
1761  {
1762  register ssize_t
1763  x;
1764 
1765  register Quantum
1766  *magick_restrict q;
1767 
1768  if (status == MagickFalse)
1769  continue;
1770  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1771  if (q == (Quantum *) NULL)
1772  {
1773  status=MagickFalse;
1774  continue;
1775  }
1776  for (x=0; x < (ssize_t) image->columns; x++)
1777  {
1778  register ssize_t
1779  i;
1780 
1781  ssize_t
1782  n;
1783 
1784  n=0;
1785  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1786  {
1787  ssize_t
1788  level,
1789  threshold;
1790 
1791  PixelChannel channel = GetPixelChannelChannel(image,i);
1792  PixelTrait traits = GetPixelChannelTraits(image,channel);
1793  if ((traits & UpdatePixelTrait) == 0)
1794  continue;
1795  if (fabs(levels[n]) < MagickEpsilon)
1796  {
1797  n++;
1798  continue;
1799  }
1800  threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1801  level=threshold/(map->divisor-1);
1802  threshold-=level*(map->divisor-1);
1803  q[i]=ClampToQuantum((double) (level+(threshold >=
1804  map->levels[(x % map->width)+map->width*(y % map->height)]))*
1805  QuantumRange/levels[n]);
1806  n++;
1807  }
1808  q+=GetPixelChannels(image);
1809  }
1810  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1811  status=MagickFalse;
1812  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1813  {
1815  proceed;
1816 
1817 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1818  #pragma omp critical (MagickCore_OrderedDitherImage)
1819 #endif
1820  proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1821  if (proceed == MagickFalse)
1822  status=MagickFalse;
1823  }
1824  }
1825  image_view=DestroyCacheView(image_view);
1826  map=DestroyThresholdMap(map);
1827  return(MagickTrue);
1828 }
1829 
1830 /*
1831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1832 % %
1833 % %
1834 % %
1835 % P e r c e p t i b l e I m a g e %
1836 % %
1837 % %
1838 % %
1839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1840 %
1841 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1842 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1843 % unchanged.
1844 %
1845 % The format of the PerceptibleImage method is:
1846 %
1847 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1848 % ExceptionInfo *exception)
1849 %
1850 % A description of each parameter follows:
1851 %
1852 % o image: the image.
1853 %
1854 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1855 %
1856 % o exception: return any errors or warnings in this structure.
1857 %
1858 */
1859 
1860 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1861  const double epsilon)
1862 {
1863  double
1864  sign;
1865 
1866  sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1867  if ((sign*quantum) >= epsilon)
1868  return(quantum);
1869  return((Quantum) (sign*epsilon));
1870 }
1871 
1873  const double epsilon,ExceptionInfo *exception)
1874 {
1875 #define PerceptibleImageTag "Perceptible/Image"
1876 
1877  CacheView
1878  *image_view;
1879 
1881  status;
1882 
1884  progress;
1885 
1886  ssize_t
1887  y;
1888 
1889  assert(image != (Image *) NULL);
1890  assert(image->signature == MagickCoreSignature);
1891  if (image->debug != MagickFalse)
1892  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1893  if (image->storage_class == PseudoClass)
1894  {
1895  register ssize_t
1896  i;
1897 
1898  register PixelInfo
1899  *magick_restrict q;
1900 
1901  q=image->colormap;
1902  for (i=0; i < (ssize_t) image->colors; i++)
1903  {
1904  q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1905  epsilon);
1906  q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1907  epsilon);
1908  q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1909  epsilon);
1910  q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1911  epsilon);
1912  q++;
1913  }
1914  return(SyncImage(image,exception));
1915  }
1916  /*
1917  Perceptible image.
1918  */
1919  status=MagickTrue;
1920  progress=0;
1921  image_view=AcquireAuthenticCacheView(image,exception);
1922 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1923  #pragma omp parallel for schedule(static) shared(progress,status) \
1924  magick_number_threads(image,image,image->rows,1)
1925 #endif
1926  for (y=0; y < (ssize_t) image->rows; y++)
1927  {
1928  register ssize_t
1929  x;
1930 
1931  register Quantum
1932  *magick_restrict q;
1933 
1934  if (status == MagickFalse)
1935  continue;
1936  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1937  if (q == (Quantum *) NULL)
1938  {
1939  status=MagickFalse;
1940  continue;
1941  }
1942  for (x=0; x < (ssize_t) image->columns; x++)
1943  {
1944  register ssize_t
1945  i;
1946 
1947  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1948  {
1949  PixelChannel channel = GetPixelChannelChannel(image,i);
1950  PixelTrait traits = GetPixelChannelTraits(image,channel);
1951  if (traits == UndefinedPixelTrait)
1952  continue;
1953  q[i]=PerceptibleThreshold(q[i],epsilon);
1954  }
1955  q+=GetPixelChannels(image);
1956  }
1957  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1958  status=MagickFalse;
1959  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1960  {
1962  proceed;
1963 
1964 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1965  #pragma omp critical (MagickCore_PerceptibleImage)
1966 #endif
1967  proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1968  if (proceed == MagickFalse)
1969  status=MagickFalse;
1970  }
1971  }
1972  image_view=DestroyCacheView(image_view);
1973  return(status);
1974 }
1975 
1976 /*
1977 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1978 % %
1979 % %
1980 % %
1981 % R a n d o m T h r e s h o l d I m a g e %
1982 % %
1983 % %
1984 % %
1985 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1986 %
1987 % RandomThresholdImage() changes the value of individual pixels based on the
1988 % intensity of each pixel compared to a random threshold. The result is a
1989 % low-contrast, two color image.
1990 %
1991 % The format of the RandomThresholdImage method is:
1992 %
1993 % MagickBooleanType RandomThresholdImage(Image *image,
1994 % const char *thresholds,ExceptionInfo *exception)
1995 %
1996 % A description of each parameter follows:
1997 %
1998 % o image: the image.
1999 %
2000 % o low,high: Specify the high and low thresholds. These values range from
2001 % 0 to QuantumRange.
2002 %
2003 % o exception: return any errors or warnings in this structure.
2004 %
2005 */
2007  const double min_threshold, const double max_threshold,ExceptionInfo *exception)
2008 {
2009 #define ThresholdImageTag "Threshold/Image"
2010 
2011  CacheView
2012  *image_view;
2013 
2015  status;
2016 
2018  progress;
2019 
2020  PixelInfo
2021  threshold;
2022 
2023  RandomInfo
2025 
2026  ssize_t
2027  y;
2028 
2029 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2030  unsigned long
2031  key;
2032 #endif
2033 
2034  assert(image != (Image *) NULL);
2035  assert(image->signature == MagickCoreSignature);
2036  if (image->debug != MagickFalse)
2037  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2038  assert(exception != (ExceptionInfo *) NULL);
2039  assert(exception->signature == MagickCoreSignature);
2040  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2041  return(MagickFalse);
2042  GetPixelInfo(image,&threshold);
2043  /*
2044  Random threshold image.
2045  */
2046  status=MagickTrue;
2047  progress=0;
2049  image_view=AcquireAuthenticCacheView(image,exception);
2050 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2052  #pragma omp parallel for schedule(static) shared(progress,status) \
2053  magick_number_threads(image,image,image->rows,key == ~0UL)
2054 #endif
2055  for (y=0; y < (ssize_t) image->rows; y++)
2056  {
2057  const int
2058  id = GetOpenMPThreadId();
2059 
2060  register Quantum
2061  *magick_restrict q;
2062 
2063  register ssize_t
2064  x;
2065 
2066  if (status == MagickFalse)
2067  continue;
2068  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2069  if (q == (Quantum *) NULL)
2070  {
2071  status=MagickFalse;
2072  continue;
2073  }
2074  for (x=0; x < (ssize_t) image->columns; x++)
2075  {
2076  register ssize_t
2077  i;
2078 
2079  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2080  {
2081  double
2082  threshold;
2083 
2084  PixelChannel channel = GetPixelChannelChannel(image,i);
2085  PixelTrait traits = GetPixelChannelTraits(image,channel);
2086  if ((traits & UpdatePixelTrait) == 0)
2087  continue;
2088  if ((double) q[i] < min_threshold)
2089  threshold=min_threshold;
2090  else
2091  if ((double) q[i] > max_threshold)
2092  threshold=max_threshold;
2093  else
2094  threshold=(double) (QuantumRange*
2096  q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
2097  }
2098  q+=GetPixelChannels(image);
2099  }
2100  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2101  status=MagickFalse;
2102  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2103  {
2105  proceed;
2106 
2107 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2108  #pragma omp critical (MagickCore_RandomThresholdImage)
2109 #endif
2110  proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2111  image->rows);
2112  if (proceed == MagickFalse)
2113  status=MagickFalse;
2114  }
2115  }
2116  image_view=DestroyCacheView(image_view);
2118  return(status);
2119 }
2120 
2121 /*
2122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2123 % %
2124 % %
2125 % %
2126 % W h i t e T h r e s h o l d I m a g e %
2127 % %
2128 % %
2129 % %
2130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2131 %
2132 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
2133 % the threshold into white while leaving all pixels at or below the threshold
2134 % unchanged.
2135 %
2136 % The format of the WhiteThresholdImage method is:
2137 %
2138 % MagickBooleanType WhiteThresholdImage(Image *image,
2139 % const char *threshold,ExceptionInfo *exception)
2140 %
2141 % A description of each parameter follows:
2142 %
2143 % o image: the image.
2144 %
2145 % o threshold: Define the threshold value.
2146 %
2147 % o exception: return any errors or warnings in this structure.
2148 %
2149 */
2151  const char *thresholds,ExceptionInfo *exception)
2152 {
2153 #define ThresholdImageTag "Threshold/Image"
2154 
2155  CacheView
2156  *image_view;
2157 
2158  GeometryInfo
2159  geometry_info;
2160 
2162  status;
2163 
2165  progress;
2166 
2167  PixelInfo
2168  threshold;
2169 
2171  flags;
2172 
2173  ssize_t
2174  y;
2175 
2176  assert(image != (Image *) NULL);
2177  assert(image->signature == MagickCoreSignature);
2178  if (image->debug != MagickFalse)
2179  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2180  if (thresholds == (const char *) NULL)
2181  return(MagickTrue);
2182  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2183  return(MagickFalse);
2184  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2185  (void) TransformImageColorspace(image,sRGBColorspace,exception);
2186  GetPixelInfo(image,&threshold);
2187  flags=ParseGeometry(thresholds,&geometry_info);
2188  threshold.red=geometry_info.rho;
2189  threshold.green=geometry_info.rho;
2190  threshold.blue=geometry_info.rho;
2191  threshold.black=geometry_info.rho;
2192  threshold.alpha=100.0;
2193  if ((flags & SigmaValue) != 0)
2194  threshold.green=geometry_info.sigma;
2195  if ((flags & XiValue) != 0)
2196  threshold.blue=geometry_info.xi;
2197  if ((flags & PsiValue) != 0)
2198  threshold.alpha=geometry_info.psi;
2199  if (threshold.colorspace == CMYKColorspace)
2200  {
2201  if ((flags & PsiValue) != 0)
2202  threshold.black=geometry_info.psi;
2203  if ((flags & ChiValue) != 0)
2204  threshold.alpha=geometry_info.chi;
2205  }
2206  if ((flags & PercentValue) != 0)
2207  {
2208  threshold.red*=(MagickRealType) (QuantumRange/100.0);
2209  threshold.green*=(MagickRealType) (QuantumRange/100.0);
2210  threshold.blue*=(MagickRealType) (QuantumRange/100.0);
2211  threshold.black*=(MagickRealType) (QuantumRange/100.0);
2212  threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
2213  }
2214  /*
2215  White threshold image.
2216  */
2217  status=MagickTrue;
2218  progress=0;
2219  image_view=AcquireAuthenticCacheView(image,exception);
2220 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2221  #pragma omp parallel for schedule(static) shared(progress,status) \
2222  magick_number_threads(image,image,image->rows,1)
2223 #endif
2224  for (y=0; y < (ssize_t) image->rows; y++)
2225  {
2226  register ssize_t
2227  x;
2228 
2229  register Quantum
2230  *magick_restrict q;
2231 
2232  if (status == MagickFalse)
2233  continue;
2234  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2235  if (q == (Quantum *) NULL)
2236  {
2237  status=MagickFalse;
2238  continue;
2239  }
2240  for (x=0; x < (ssize_t) image->columns; x++)
2241  {
2242  double
2243  pixel;
2244 
2245  register ssize_t
2246  i;
2247 
2248  pixel=GetPixelIntensity(image,q);
2249  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2250  {
2251  PixelChannel channel = GetPixelChannelChannel(image,i);
2252  PixelTrait traits = GetPixelChannelTraits(image,channel);
2253  if ((traits & UpdatePixelTrait) == 0)
2254  continue;
2255  if (image->channel_mask != DefaultChannels)
2256  pixel=(double) q[i];
2257  if (pixel > GetPixelInfoChannel(&threshold,channel))
2258  q[i]=QuantumRange;
2259  }
2260  q+=GetPixelChannels(image);
2261  }
2262  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2263  status=MagickFalse;
2264  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2265  {
2267  proceed;
2268 
2269 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2270  #pragma omp critical (MagickCore_WhiteThresholdImage)
2271 #endif
2272  proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2273  image->rows);
2274  if (proceed == MagickFalse)
2275  status=MagickFalse;
2276  }
2277  }
2278  image_view=DestroyCacheView(image_view);
2279  return(status);
2280 }
#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
MagickDoubleType MagickRealType
Definition: magick-type.h:118
static double TriangleThreshold(const Image *image, const double *histogram, ExceptionInfo *exception)
Definition: threshold.c:566
MagickExport CacheView * DestroyCacheView(CacheView *cache_view)
Definition: cache-view.c:252
static MagickBooleanType SetImageProgress(const Image *image, const char *tag, const MagickOffsetType offset, const MagickSizeType extent)
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:3829
MagickExport MagickBooleanType TransformImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1325
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:1594
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:504
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:1872
ClassType storage_class
Definition: image.h:154
#define ThrowBinaryException(severity, tag, context)
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:127
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:1510
#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:1295
MagickExport LinkedListInfo * GetConfigureOptions(const char *filename, ExceptionInfo *exception)
Definition: configure.c:654
MagickExport ssize_t FormatLocaleFile(FILE *file, const char *magick_restrict format,...)
Definition: locale.c:409
MagickBooleanType
Definition: magick-type.h:156
unsigned int MagickStatusType
Definition: magick-type.h:119
static double PerceptibleReciprocal(const double x)
#define ThresholdImageTag
MagickExport void * AcquireQuantumMemory(const size_t count, const size_t quantum)
Definition: memory.c:533
#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:4098
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:128
#define MagickPathExtent
static double OTSUThreshold(const Image *image, const double *histogram, ExceptionInfo *exception)
Definition: threshold.c:487
MagickExport ThresholdMap * GetThresholdMap(const char *map_id, ExceptionInfo *exception)
Definition: threshold.c:1241
#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:1064
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1397
size_t signature
Definition: image.h:354
#define QuantumScale
Definition: magick-type.h:113
size_t columns
Definition: image.h:172
MagickBooleanType(* MagickProgressMonitor)(const char *, const MagickOffsetType, const MagickSizeType, void *)
Definition: monitor.h:26
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:2613
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:1440
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:1382
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:171
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:2006
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:1134
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:852
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:1202
static ThresholdMap * GetThresholdMapFile(const char *, const char *, const char *, ExceptionInfo *)
Definition: threshold.c:1304
MagickExport MagickBooleanType BilevelImage(Image *image, const double threshold, ExceptionInfo *exception)
Definition: threshold.c:801
static RandomInfo * random_info
Definition: resource.c:111
static Quantum PerceptibleThreshold(const Quantum quantum, const double epsilon)
Definition: threshold.c:1860
MagickExport void * RelinquishMagickMemory(void *memory)
Definition: memory.c:1053
#define MaxPixelChannels
Definition: pixel.h:27
MagickExport MagickBooleanType WhiteThresholdImage(Image *image, const char *thresholds, ExceptionInfo *exception)
Definition: threshold.c:2150
MagickExport MagickBooleanType ClampImage(Image *image, ExceptionInfo *exception)
Definition: threshold.c:1081
MagickRealType green
Definition: pixel.h:191
static double KapurThreshold(const Image *image, const double *histogram, ExceptionInfo *exception)
Definition: threshold.c:388
#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:1671
PixelTrait
Definition: pixel.h:135
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:326
MagickExport MagickBooleanType AutoThresholdImage(Image *image, const AutoThresholdMethod method, ExceptionInfo *exception)
Definition: threshold.c:658
ssize_t * levels
Definition: threshold.c:112
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1177
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:794
ColorspaceType colorspace
Definition: image.h:157
MagickExport XMLTreeInfo * NewXMLTree(const char *xml, ExceptionInfo *exception)
Definition: xml-tree.c:1952
#define QuantumRange
Definition: magick-type.h:83
MagickBooleanType debug
Definition: image.h:334