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 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 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(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 atomic
882 #endif
883  progress++;
884  proceed=SetImageProgress(image,ThresholdImageTag,progress++,
885  image->rows);
886  if (proceed == MagickFalse)
887  status=MagickFalse;
888  }
889  }
890  image_view=DestroyCacheView(image_view);
891  return(status);
892 }
893 
894 /*
895 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
896 % %
897 % %
898 % %
899 % B l a c k T h r e s h o l d I m a g e %
900 % %
901 % %
902 % %
903 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
904 %
905 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
906 % the threshold into black while leaving all pixels at or above the threshold
907 % unchanged.
908 %
909 % The format of the BlackThresholdImage method is:
910 %
911 % MagickBooleanType BlackThresholdImage(Image *image,
912 % const char *threshold,ExceptionInfo *exception)
913 %
914 % A description of each parameter follows:
915 %
916 % o image: the image.
917 %
918 % o threshold: define the threshold value.
919 %
920 % o exception: return any errors or warnings in this structure.
921 %
922 */
924  const char *thresholds,ExceptionInfo *exception)
925 {
926 #define ThresholdImageTag "Threshold/Image"
927 
928  CacheView
929  *image_view;
930 
932  geometry_info;
933 
935  status;
936 
938  progress;
939 
940  PixelInfo
941  threshold;
942 
944  flags;
945 
946  ssize_t
947  y;
948 
949  assert(image != (Image *) NULL);
950  assert(image->signature == MagickCoreSignature);
951  if (image->debug != MagickFalse)
952  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
953  if (thresholds == (const char *) NULL)
954  return(MagickTrue);
955  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
956  return(MagickFalse);
957  if (IsGrayColorspace(image->colorspace) != MagickFalse)
958  (void) SetImageColorspace(image,sRGBColorspace,exception);
959  GetPixelInfo(image,&threshold);
960  flags=ParseGeometry(thresholds,&geometry_info);
961  threshold.red=geometry_info.rho;
962  threshold.green=geometry_info.rho;
963  threshold.blue=geometry_info.rho;
964  threshold.black=geometry_info.rho;
965  threshold.alpha=100.0;
966  if ((flags & SigmaValue) != 0)
967  threshold.green=geometry_info.sigma;
968  if ((flags & XiValue) != 0)
969  threshold.blue=geometry_info.xi;
970  if ((flags & PsiValue) != 0)
971  threshold.alpha=geometry_info.psi;
972  if (threshold.colorspace == CMYKColorspace)
973  {
974  if ((flags & PsiValue) != 0)
975  threshold.black=geometry_info.psi;
976  if ((flags & ChiValue) != 0)
977  threshold.alpha=geometry_info.chi;
978  }
979  if ((flags & PercentValue) != 0)
980  {
981  threshold.red*=(MagickRealType) (QuantumRange/100.0);
982  threshold.green*=(MagickRealType) (QuantumRange/100.0);
983  threshold.blue*=(MagickRealType) (QuantumRange/100.0);
984  threshold.black*=(MagickRealType) (QuantumRange/100.0);
985  threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
986  }
987  /*
988  White threshold image.
989  */
990  status=MagickTrue;
991  progress=0;
992  image_view=AcquireAuthenticCacheView(image,exception);
993 #if defined(MAGICKCORE_OPENMP_SUPPORT)
994  #pragma omp parallel for schedule(static) shared(progress,status) \
995  magick_number_threads(image,image,image->rows,1)
996 #endif
997  for (y=0; y < (ssize_t) image->rows; y++)
998  {
999  register ssize_t
1000  x;
1001 
1002  register Quantum
1003  *magick_restrict q;
1004 
1005  if (status == MagickFalse)
1006  continue;
1007  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1008  if (q == (Quantum *) NULL)
1009  {
1010  status=MagickFalse;
1011  continue;
1012  }
1013  for (x=0; x < (ssize_t) image->columns; x++)
1014  {
1015  double
1016  pixel;
1017 
1018  register ssize_t
1019  i;
1020 
1021  pixel=GetPixelIntensity(image,q);
1022  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1023  {
1024  PixelChannel channel = GetPixelChannelChannel(image,i);
1025  PixelTrait traits = GetPixelChannelTraits(image,channel);
1026  if ((traits & UpdatePixelTrait) == 0)
1027  continue;
1028  if (image->channel_mask != DefaultChannels)
1029  pixel=(double) q[i];
1030  if (pixel < GetPixelInfoChannel(&threshold,channel))
1031  q[i]=(Quantum) 0;
1032  }
1033  q+=GetPixelChannels(image);
1034  }
1035  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1036  status=MagickFalse;
1037  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1038  {
1040  proceed;
1041 
1042 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1043  #pragma omp atomic
1044 #endif
1045  progress++;
1046  proceed=SetImageProgress(image,ThresholdImageTag,progress,
1047  image->rows);
1048  if (proceed == MagickFalse)
1049  status=MagickFalse;
1050  }
1051  }
1052  image_view=DestroyCacheView(image_view);
1053  return(status);
1054 }
1055 
1056 /*
1057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1058 % %
1059 % %
1060 % %
1061 % C l a m p I m a g e %
1062 % %
1063 % %
1064 % %
1065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1066 %
1067 % ClampImage() set each pixel whose value is below zero to zero and any the
1068 % pixel whose value is above the quantum range to the quantum range (e.g.
1069 % 65535) otherwise the pixel value remains unchanged.
1070 %
1071 % The format of the ClampImage method is:
1072 %
1073 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
1074 %
1075 % A description of each parameter follows:
1076 %
1077 % o image: the image.
1078 %
1079 % o exception: return any errors or warnings in this structure.
1080 %
1081 */
1082 
1084 {
1085 #define ClampImageTag "Clamp/Image"
1086 
1087  CacheView
1088  *image_view;
1089 
1091  status;
1092 
1094  progress;
1095 
1096  ssize_t
1097  y;
1098 
1099  assert(image != (Image *) NULL);
1100  assert(image->signature == MagickCoreSignature);
1101  if (image->debug != MagickFalse)
1102  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1103  if (image->storage_class == PseudoClass)
1104  {
1105  register ssize_t
1106  i;
1107 
1108  register PixelInfo
1109  *magick_restrict q;
1110 
1111  q=image->colormap;
1112  for (i=0; i < (ssize_t) image->colors; i++)
1113  {
1114  q->red=(double) ClampPixel(q->red);
1115  q->green=(double) ClampPixel(q->green);
1116  q->blue=(double) ClampPixel(q->blue);
1117  q->alpha=(double) ClampPixel(q->alpha);
1118  q++;
1119  }
1120  return(SyncImage(image,exception));
1121  }
1122  /*
1123  Clamp image.
1124  */
1125  status=MagickTrue;
1126  progress=0;
1127  image_view=AcquireAuthenticCacheView(image,exception);
1128 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1129  #pragma omp parallel for schedule(static) shared(progress,status) \
1130  magick_number_threads(image,image,image->rows,1)
1131 #endif
1132  for (y=0; y < (ssize_t) image->rows; y++)
1133  {
1134  register ssize_t
1135  x;
1136 
1137  register Quantum
1138  *magick_restrict q;
1139 
1140  if (status == MagickFalse)
1141  continue;
1142  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1143  if (q == (Quantum *) NULL)
1144  {
1145  status=MagickFalse;
1146  continue;
1147  }
1148  for (x=0; x < (ssize_t) image->columns; x++)
1149  {
1150  register ssize_t
1151  i;
1152 
1153  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1154  {
1155  PixelChannel channel = GetPixelChannelChannel(image,i);
1156  PixelTrait traits = GetPixelChannelTraits(image,channel);
1157  if ((traits & UpdatePixelTrait) == 0)
1158  continue;
1159  q[i]=ClampPixel((MagickRealType) q[i]);
1160  }
1161  q+=GetPixelChannels(image);
1162  }
1163  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1164  status=MagickFalse;
1165  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1166  {
1168  proceed;
1169 
1170 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1171  #pragma omp atomic
1172 #endif
1173  progress++;
1174  proceed=SetImageProgress(image,ClampImageTag,progress,image->rows);
1175  if (proceed == MagickFalse)
1176  status=MagickFalse;
1177  }
1178  }
1179  image_view=DestroyCacheView(image_view);
1180  return(status);
1181 }
1182 
1183 /*
1184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1185 % %
1186 % %
1187 % %
1188 % D e s t r o y T h r e s h o l d M a p %
1189 % %
1190 % %
1191 % %
1192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1193 %
1194 % DestroyThresholdMap() de-allocate the given ThresholdMap
1195 %
1196 % The format of the ListThresholdMaps method is:
1197 %
1198 % ThresholdMap *DestroyThresholdMap(Threshold *map)
1199 %
1200 % A description of each parameter follows.
1201 %
1202 % o map: Pointer to the Threshold map to destroy
1203 %
1204 */
1206 {
1207  assert(map != (ThresholdMap *) NULL);
1208  if (map->map_id != (char *) NULL)
1209  map->map_id=DestroyString(map->map_id);
1210  if (map->description != (char *) NULL)
1212  if (map->levels != (ssize_t *) NULL)
1213  map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1214  map=(ThresholdMap *) RelinquishMagickMemory(map);
1215  return(map);
1216 }
1217 
1218 /*
1219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220 % %
1221 % %
1222 % %
1223 % G e t T h r e s h o l d M a p %
1224 % %
1225 % %
1226 % %
1227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1228 %
1229 % GetThresholdMap() loads and searches one or more threshold map files for the
1230 % map matching the given name or alias.
1231 %
1232 % The format of the GetThresholdMap method is:
1233 %
1234 % ThresholdMap *GetThresholdMap(const char *map_id,
1235 % ExceptionInfo *exception)
1236 %
1237 % A description of each parameter follows.
1238 %
1239 % o map_id: ID of the map to look for.
1240 %
1241 % o exception: return any errors or warnings in this structure.
1242 %
1243 */
1245  ExceptionInfo *exception)
1246 {
1247  ThresholdMap
1248  *map;
1249 
1250  map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
1251  if (map != (ThresholdMap *) NULL)
1252  return(map);
1253 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
1254  {
1255  const StringInfo
1256  *option;
1257 
1259  *options;
1260 
1261  options=GetConfigureOptions(ThresholdsFilename,exception);
1262  option=(const StringInfo *) GetNextValueInLinkedList(options);
1263  while (option != (const StringInfo *) NULL)
1264  {
1265  map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1266  GetStringInfoPath(option),map_id,exception);
1267  if (map != (ThresholdMap *) NULL)
1268  break;
1269  option=(const StringInfo *) GetNextValueInLinkedList(options);
1270  }
1271  options=DestroyConfigureOptions(options);
1272  }
1273 #endif
1274  return(map);
1275 }
1276 
1277 /*
1278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1279 % %
1280 % %
1281 % %
1282 + G e t T h r e s h o l d M a p F i l e %
1283 % %
1284 % %
1285 % %
1286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1287 %
1288 % GetThresholdMapFile() look for a given threshold map name or alias in the
1289 % given XML file data, and return the allocated the map when found.
1290 %
1291 % The format of the ListThresholdMaps method is:
1292 %
1293 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
1294 % const char *map_id,ExceptionInfo *exception)
1295 %
1296 % A description of each parameter follows.
1297 %
1298 % o xml: The threshold map list in XML format.
1299 %
1300 % o filename: The threshold map XML filename.
1301 %
1302 % o map_id: ID of the map to look for in XML list.
1303 %
1304 % o exception: return any errors or warnings in this structure.
1305 %
1306 */
1307 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
1308  const char *map_id,ExceptionInfo *exception)
1309 {
1310  char
1311  *p;
1312 
1313  const char
1314  *attribute,
1315  *content;
1316 
1317  double
1318  value;
1319 
1320  register ssize_t
1321  i;
1322 
1323  ThresholdMap
1324  *map;
1325 
1326  XMLTreeInfo
1327  *description,
1328  *levels,
1329  *threshold,
1330  *thresholds;
1331 
1333  "Loading threshold map file \"%s\" ...",filename);
1334  map=(ThresholdMap *) NULL;
1335  thresholds=NewXMLTree(xml,exception);
1336  if (thresholds == (XMLTreeInfo *) NULL)
1337  return(map);
1338  for (threshold=GetXMLTreeChild(thresholds,"threshold");
1339  threshold != (XMLTreeInfo *) NULL;
1340  threshold=GetNextXMLTreeTag(threshold))
1341  {
1342  attribute=GetXMLTreeAttribute(threshold,"map");
1343  if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1344  break;
1345  attribute=GetXMLTreeAttribute(threshold,"alias");
1346  if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1347  break;
1348  }
1349  if (threshold == (XMLTreeInfo *) NULL)
1350  {
1351  thresholds=DestroyXMLTree(thresholds);
1352  return(map);
1353  }
1354  description=GetXMLTreeChild(threshold,"description");
1355  if (description == (XMLTreeInfo *) NULL)
1356  {
1358  "XmlMissingElement", "<description>, map \"%s\"",map_id);
1359  thresholds=DestroyXMLTree(thresholds);
1360  return(map);
1361  }
1362  levels=GetXMLTreeChild(threshold,"levels");
1363  if (levels == (XMLTreeInfo *) NULL)
1364  {
1366  "XmlMissingElement", "<levels>, map \"%s\"", map_id);
1367  thresholds=DestroyXMLTree(thresholds);
1368  return(map);
1369  }
1370  map=(ThresholdMap *) AcquireCriticalMemory(sizeof(*map));
1371  map->map_id=(char *) NULL;
1372  map->description=(char *) NULL;
1373  map->levels=(ssize_t *) NULL;
1374  attribute=GetXMLTreeAttribute(threshold,"map");
1375  if (attribute != (char *) NULL)
1376  map->map_id=ConstantString(attribute);
1377  content=GetXMLTreeContent(description);
1378  if (content != (char *) NULL)
1379  map->description=ConstantString(content);
1380  attribute=GetXMLTreeAttribute(levels,"width");
1381  if (attribute == (char *) NULL)
1382  {
1384  "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1385  thresholds=DestroyXMLTree(thresholds);
1386  map=DestroyThresholdMap(map);
1387  return(map);
1388  }
1389  map->width=StringToUnsignedLong(attribute);
1390  if (map->width == 0)
1391  {
1393  "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1394  thresholds=DestroyXMLTree(thresholds);
1395  map=DestroyThresholdMap(map);
1396  return(map);
1397  }
1398  attribute=GetXMLTreeAttribute(levels,"height");
1399  if (attribute == (char *) NULL)
1400  {
1402  "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1403  thresholds=DestroyXMLTree(thresholds);
1404  map=DestroyThresholdMap(map);
1405  return(map);
1406  }
1407  map->height=StringToUnsignedLong(attribute);
1408  if (map->height == 0)
1409  {
1411  "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1412  thresholds=DestroyXMLTree(thresholds);
1413  map=DestroyThresholdMap(map);
1414  return(map);
1415  }
1416  attribute=GetXMLTreeAttribute(levels,"divisor");
1417  if (attribute == (char *) NULL)
1418  {
1420  "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1421  thresholds=DestroyXMLTree(thresholds);
1422  map=DestroyThresholdMap(map);
1423  return(map);
1424  }
1425  map->divisor=(ssize_t) StringToLong(attribute);
1426  if (map->divisor < 2)
1427  {
1429  "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1430  thresholds=DestroyXMLTree(thresholds);
1431  map=DestroyThresholdMap(map);
1432  return(map);
1433  }
1434  content=GetXMLTreeContent(levels);
1435  if (content == (char *) NULL)
1436  {
1438  "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1439  thresholds=DestroyXMLTree(thresholds);
1440  map=DestroyThresholdMap(map);
1441  return(map);
1442  }
1443  map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1444  sizeof(*map->levels));
1445  if (map->levels == (ssize_t *) NULL)
1446  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1447  for (i=0; i < (ssize_t) (map->width*map->height); i++)
1448  {
1449  map->levels[i]=(ssize_t) strtol(content,&p,10);
1450  if (p == content)
1451  {
1453  "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1454  thresholds=DestroyXMLTree(thresholds);
1455  map=DestroyThresholdMap(map);
1456  return(map);
1457  }
1458  if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1459  {
1461  "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1462  (double) map->levels[i],map_id);
1463  thresholds=DestroyXMLTree(thresholds);
1464  map=DestroyThresholdMap(map);
1465  return(map);
1466  }
1467  content=p;
1468  }
1469  value=(double) strtol(content,&p,10);
1470  (void) value;
1471  if (p != content)
1472  {
1474  "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1475  thresholds=DestroyXMLTree(thresholds);
1476  map=DestroyThresholdMap(map);
1477  return(map);
1478  }
1479  thresholds=DestroyXMLTree(thresholds);
1480  return(map);
1481 }
1482 
1483 /*
1484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1485 % %
1486 % %
1487 % %
1488 + L i s t T h r e s h o l d M a p F i l e %
1489 % %
1490 % %
1491 % %
1492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493 %
1494 % ListThresholdMapFile() lists the threshold maps and their descriptions
1495 % in the given XML file data.
1496 %
1497 % The format of the ListThresholdMaps method is:
1498 %
1499 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1500 % const char *filename,ExceptionInfo *exception)
1501 %
1502 % A description of each parameter follows.
1503 %
1504 % o file: An pointer to the output FILE.
1505 %
1506 % o xml: The threshold map list in XML format.
1507 %
1508 % o filename: The threshold map XML filename.
1509 %
1510 % o exception: return any errors or warnings in this structure.
1511 %
1512 */
1513 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1514  const char *filename,ExceptionInfo *exception)
1515 {
1516  const char
1517  *alias,
1518  *content,
1519  *map;
1520 
1521  XMLTreeInfo
1522  *description,
1523  *threshold,
1524  *thresholds;
1525 
1526  assert( xml != (char *) NULL );
1527  assert( file != (FILE *) NULL );
1529  "Loading threshold map file \"%s\" ...",filename);
1530  thresholds=NewXMLTree(xml,exception);
1531  if ( thresholds == (XMLTreeInfo *) NULL )
1532  return(MagickFalse);
1533  (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1534  (void) FormatLocaleFile(file,
1535  "----------------------------------------------------\n");
1536  threshold=GetXMLTreeChild(thresholds,"threshold");
1537  for ( ; threshold != (XMLTreeInfo *) NULL;
1538  threshold=GetNextXMLTreeTag(threshold))
1539  {
1540  map=GetXMLTreeAttribute(threshold,"map");
1541  if (map == (char *) NULL)
1542  {
1544  "XmlMissingAttribute", "<map>");
1545  thresholds=DestroyXMLTree(thresholds);
1546  return(MagickFalse);
1547  }
1548  alias=GetXMLTreeAttribute(threshold,"alias");
1549  description=GetXMLTreeChild(threshold,"description");
1550  if (description == (XMLTreeInfo *) NULL)
1551  {
1553  "XmlMissingElement", "<description>, map \"%s\"",map);
1554  thresholds=DestroyXMLTree(thresholds);
1555  return(MagickFalse);
1556  }
1557  content=GetXMLTreeContent(description);
1558  if (content == (char *) NULL)
1559  {
1561  "XmlMissingContent", "<description>, map \"%s\"", map);
1562  thresholds=DestroyXMLTree(thresholds);
1563  return(MagickFalse);
1564  }
1565  (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1566  content);
1567  }
1568  thresholds=DestroyXMLTree(thresholds);
1569  return(MagickTrue);
1570 }
1571 
1572 /*
1573 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1574 % %
1575 % %
1576 % %
1577 % L i s t T h r e s h o l d M a p s %
1578 % %
1579 % %
1580 % %
1581 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1582 %
1583 % ListThresholdMaps() lists the threshold maps and their descriptions
1584 % as defined by "threshold.xml" to a file.
1585 %
1586 % The format of the ListThresholdMaps method is:
1587 %
1588 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1589 %
1590 % A description of each parameter follows.
1591 %
1592 % o file: An pointer to the output FILE.
1593 %
1594 % o exception: return any errors or warnings in this structure.
1595 %
1596 */
1598  ExceptionInfo *exception)
1599 {
1600  const StringInfo
1601  *option;
1602 
1604  *options;
1605 
1607  status;
1608 
1609  status=MagickTrue;
1610  if (file == (FILE *) NULL)
1611  file=stdout;
1612  options=GetConfigureOptions(ThresholdsFilename,exception);
1613  (void) FormatLocaleFile(file,
1614  "\n Threshold Maps for Ordered Dither Operations\n");
1615  option=(const StringInfo *) GetNextValueInLinkedList(options);
1616  while (option != (const StringInfo *) NULL)
1617  {
1618  (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1619  status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1620  GetStringInfoPath(option),exception);
1621  option=(const StringInfo *) GetNextValueInLinkedList(options);
1622  }
1623  options=DestroyConfigureOptions(options);
1624  return(status != 0 ? MagickTrue : MagickFalse);
1625 }
1626 
1627 /*
1628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629 % %
1630 % %
1631 % %
1632 % O r d e r e d D i t h e r I m a g e %
1633 % %
1634 % %
1635 % %
1636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1637 %
1638 % OrderedDitherImage() will perform a ordered dither based on a number
1639 % of pre-defined dithering threshold maps, but over multiple intensity
1640 % levels, which can be different for different channels, according to the
1641 % input argument.
1642 %
1643 % The format of the OrderedDitherImage method is:
1644 %
1645 % MagickBooleanType OrderedDitherImage(Image *image,
1646 % const char *threshold_map,ExceptionInfo *exception)
1647 %
1648 % A description of each parameter follows:
1649 %
1650 % o image: the image.
1651 %
1652 % o threshold_map: A string containing the name of the threshold dither
1653 % map to use, followed by zero or more numbers representing the number
1654 % of color levels tho dither between.
1655 %
1656 % Any level number less than 2 will be equivalent to 2, and means only
1657 % binary dithering will be applied to each color channel.
1658 %
1659 % No numbers also means a 2 level (bitmap) dither will be applied to all
1660 % channels, while a single number is the number of levels applied to each
1661 % channel in sequence. More numbers will be applied in turn to each of
1662 % the color channels.
1663 %
1664 % For example: "o3x3,6" will generate a 6 level posterization of the
1665 % image with a ordered 3x3 diffused pixel dither being applied between
1666 % each level. While checker,8,8,4 will produce a 332 colormaped image
1667 % with only a single checkerboard hash pattern (50% grey) between each
1668 % color level, to basically double the number of color levels with
1669 % a bare minimim of dithering.
1670 %
1671 % o exception: return any errors or warnings in this structure.
1672 %
1673 */
1675  const char *threshold_map,ExceptionInfo *exception)
1676 {
1677 #define DitherImageTag "Dither/Image"
1678 
1679  CacheView
1680  *image_view;
1681 
1682  char
1683  token[MagickPathExtent];
1684 
1685  const char
1686  *p;
1687 
1688  double
1689  levels[CompositePixelChannel];
1690 
1692  status;
1693 
1695  progress;
1696 
1697  register ssize_t
1698  i;
1699 
1700  ssize_t
1701  y;
1702 
1703  ThresholdMap
1704  *map;
1705 
1706  assert(image != (Image *) NULL);
1707  assert(image->signature == MagickCoreSignature);
1708  if (image->debug != MagickFalse)
1709  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1710  assert(exception != (ExceptionInfo *) NULL);
1711  assert(exception->signature == MagickCoreSignature);
1712  if (threshold_map == (const char *) NULL)
1713  return(MagickTrue);
1714  p=(char *) threshold_map;
1715  while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1716  (*p != '\0'))
1717  p++;
1718  threshold_map=p;
1719  while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1720  (*p != '\0'))
1721  {
1722  if ((p-threshold_map) >= (MagickPathExtent-1))
1723  break;
1724  token[p-threshold_map]=(*p);
1725  p++;
1726  }
1727  token[p-threshold_map]='\0';
1728  map=GetThresholdMap(token,exception);
1729  if (map == (ThresholdMap *) NULL)
1730  {
1732  "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1733  return(MagickFalse);
1734  }
1735  for (i=0; i < MaxPixelChannels; i++)
1736  levels[i]=2.0;
1737  p=strchr((char *) threshold_map,',');
1738  if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1739  {
1740  GetNextToken(p,&p,MagickPathExtent,token);
1741  for (i=0; (i < MaxPixelChannels); i++)
1742  levels[i]=StringToDouble(token,(char **) NULL);
1743  for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1744  {
1745  GetNextToken(p,&p,MagickPathExtent,token);
1746  if (*token == ',')
1747  GetNextToken(p,&p,MagickPathExtent,token);
1748  levels[i]=StringToDouble(token,(char **) NULL);
1749  }
1750  }
1751  for (i=0; i < MaxPixelChannels; i++)
1752  if (fabs(levels[i]) >= 1)
1753  levels[i]-=1.0;
1754  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1755  return(MagickFalse);
1756  status=MagickTrue;
1757  progress=0;
1758  image_view=AcquireAuthenticCacheView(image,exception);
1759 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1760  #pragma omp parallel for schedule(static) shared(progress,status) \
1761  magick_number_threads(image,image,image->rows,1)
1762 #endif
1763  for (y=0; y < (ssize_t) image->rows; y++)
1764  {
1765  register ssize_t
1766  x;
1767 
1768  register Quantum
1769  *magick_restrict q;
1770 
1771  if (status == MagickFalse)
1772  continue;
1773  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1774  if (q == (Quantum *) NULL)
1775  {
1776  status=MagickFalse;
1777  continue;
1778  }
1779  for (x=0; x < (ssize_t) image->columns; x++)
1780  {
1781  register ssize_t
1782  i;
1783 
1784  ssize_t
1785  n;
1786 
1787  n=0;
1788  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1789  {
1790  ssize_t
1791  level,
1792  threshold;
1793 
1794  PixelChannel channel = GetPixelChannelChannel(image,i);
1795  PixelTrait traits = GetPixelChannelTraits(image,channel);
1796  if ((traits & UpdatePixelTrait) == 0)
1797  continue;
1798  if (fabs(levels[n]) < MagickEpsilon)
1799  {
1800  n++;
1801  continue;
1802  }
1803  threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1804  level=threshold/(map->divisor-1);
1805  threshold-=level*(map->divisor-1);
1806  q[i]=ClampToQuantum((double) (level+(threshold >=
1807  map->levels[(x % map->width)+map->width*(y % map->height)]))*
1808  QuantumRange/levels[n]);
1809  n++;
1810  }
1811  q+=GetPixelChannels(image);
1812  }
1813  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1814  status=MagickFalse;
1815  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1816  {
1818  proceed;
1819 
1820 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1821  #pragma omp atomic
1822 #endif
1823  progress++;
1824  proceed=SetImageProgress(image,DitherImageTag,progress,image->rows);
1825  if (proceed == MagickFalse)
1826  status=MagickFalse;
1827  }
1828  }
1829  image_view=DestroyCacheView(image_view);
1830  map=DestroyThresholdMap(map);
1831  return(MagickTrue);
1832 }
1833 
1834 /*
1835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1836 % %
1837 % %
1838 % %
1839 % P e r c e p t i b l e I m a g e %
1840 % %
1841 % %
1842 % %
1843 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1844 %
1845 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1846 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1847 % unchanged.
1848 %
1849 % The format of the PerceptibleImage method is:
1850 %
1851 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1852 % ExceptionInfo *exception)
1853 %
1854 % A description of each parameter follows:
1855 %
1856 % o image: the image.
1857 %
1858 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1859 %
1860 % o exception: return any errors or warnings in this structure.
1861 %
1862 */
1863 
1864 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1865  const double epsilon)
1866 {
1867  double
1868  sign;
1869 
1870  sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1871  if ((sign*quantum) >= epsilon)
1872  return(quantum);
1873  return((Quantum) (sign*epsilon));
1874 }
1875 
1877  const double epsilon,ExceptionInfo *exception)
1878 {
1879 #define PerceptibleImageTag "Perceptible/Image"
1880 
1881  CacheView
1882  *image_view;
1883 
1885  status;
1886 
1888  progress;
1889 
1890  ssize_t
1891  y;
1892 
1893  assert(image != (Image *) NULL);
1894  assert(image->signature == MagickCoreSignature);
1895  if (image->debug != MagickFalse)
1896  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1897  if (image->storage_class == PseudoClass)
1898  {
1899  register ssize_t
1900  i;
1901 
1902  register PixelInfo
1903  *magick_restrict q;
1904 
1905  q=image->colormap;
1906  for (i=0; i < (ssize_t) image->colors; i++)
1907  {
1908  q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1909  epsilon);
1910  q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1911  epsilon);
1912  q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1913  epsilon);
1914  q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1915  epsilon);
1916  q++;
1917  }
1918  return(SyncImage(image,exception));
1919  }
1920  /*
1921  Perceptible image.
1922  */
1923  status=MagickTrue;
1924  progress=0;
1925  image_view=AcquireAuthenticCacheView(image,exception);
1926 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1927  #pragma omp parallel for schedule(static) shared(progress,status) \
1928  magick_number_threads(image,image,image->rows,1)
1929 #endif
1930  for (y=0; y < (ssize_t) image->rows; y++)
1931  {
1932  register ssize_t
1933  x;
1934 
1935  register Quantum
1936  *magick_restrict q;
1937 
1938  if (status == MagickFalse)
1939  continue;
1940  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1941  if (q == (Quantum *) NULL)
1942  {
1943  status=MagickFalse;
1944  continue;
1945  }
1946  for (x=0; x < (ssize_t) image->columns; x++)
1947  {
1948  register ssize_t
1949  i;
1950 
1951  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1952  {
1953  PixelChannel channel = GetPixelChannelChannel(image,i);
1954  PixelTrait traits = GetPixelChannelTraits(image,channel);
1955  if (traits == UndefinedPixelTrait)
1956  continue;
1957  q[i]=PerceptibleThreshold(q[i],epsilon);
1958  }
1959  q+=GetPixelChannels(image);
1960  }
1961  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1962  status=MagickFalse;
1963  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1964  {
1966  proceed;
1967 
1968 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1969  #pragma omp atomic
1970 #endif
1971  progress++;
1972  proceed=SetImageProgress(image,PerceptibleImageTag,progress,
1973  image->rows);
1974  if (proceed == MagickFalse)
1975  status=MagickFalse;
1976  }
1977  }
1978  image_view=DestroyCacheView(image_view);
1979  return(status);
1980 }
1981 
1982 /*
1983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1984 % %
1985 % %
1986 % %
1987 % R a n d o m T h r e s h o l d I m a g e %
1988 % %
1989 % %
1990 % %
1991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1992 %
1993 % RandomThresholdImage() changes the value of individual pixels based on the
1994 % intensity of each pixel compared to a random threshold. The result is a
1995 % low-contrast, two color image.
1996 %
1997 % The format of the RandomThresholdImage method is:
1998 %
1999 % MagickBooleanType RandomThresholdImage(Image *image,
2000 % const char *thresholds,ExceptionInfo *exception)
2001 %
2002 % A description of each parameter follows:
2003 %
2004 % o image: the image.
2005 %
2006 % o low,high: Specify the high and low thresholds. These values range from
2007 % 0 to QuantumRange.
2008 %
2009 % o exception: return any errors or warnings in this structure.
2010 %
2011 */
2013  const double min_threshold, const double max_threshold,ExceptionInfo *exception)
2014 {
2015 #define ThresholdImageTag "Threshold/Image"
2016 
2017  CacheView
2018  *image_view;
2019 
2021  status;
2022 
2024  progress;
2025 
2026  PixelInfo
2027  threshold;
2028 
2029  RandomInfo
2031 
2032  ssize_t
2033  y;
2034 
2035 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2036  unsigned long
2037  key;
2038 #endif
2039 
2040  assert(image != (Image *) NULL);
2041  assert(image->signature == MagickCoreSignature);
2042  if (image->debug != MagickFalse)
2043  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2044  assert(exception != (ExceptionInfo *) NULL);
2045  assert(exception->signature == MagickCoreSignature);
2046  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2047  return(MagickFalse);
2048  GetPixelInfo(image,&threshold);
2049  /*
2050  Random threshold image.
2051  */
2052  status=MagickTrue;
2053  progress=0;
2054  random_info=AcquireRandomInfoThreadSet();
2055  image_view=AcquireAuthenticCacheView(image,exception);
2056 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2057  key=GetRandomSecretKey(random_info[0]);
2058  #pragma omp parallel for schedule(static) shared(progress,status) \
2059  magick_number_threads(image,image,image->rows,key == ~0UL)
2060 #endif
2061  for (y=0; y < (ssize_t) image->rows; y++)
2062  {
2063  const int
2064  id = GetOpenMPThreadId();
2065 
2066  register Quantum
2067  *magick_restrict q;
2068 
2069  register ssize_t
2070  x;
2071 
2072  if (status == MagickFalse)
2073  continue;
2074  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2075  if (q == (Quantum *) NULL)
2076  {
2077  status=MagickFalse;
2078  continue;
2079  }
2080  for (x=0; x < (ssize_t) image->columns; x++)
2081  {
2082  register ssize_t
2083  i;
2084 
2085  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2086  {
2087  double
2088  threshold;
2089 
2090  PixelChannel channel = GetPixelChannelChannel(image,i);
2091  PixelTrait traits = GetPixelChannelTraits(image,channel);
2092  if ((traits & UpdatePixelTrait) == 0)
2093  continue;
2094  if ((double) q[i] < min_threshold)
2095  threshold=min_threshold;
2096  else
2097  if ((double) q[i] > max_threshold)
2098  threshold=max_threshold;
2099  else
2100  threshold=(double) (QuantumRange*
2101  GetPseudoRandomValue(random_info[id]));
2102  q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
2103  }
2104  q+=GetPixelChannels(image);
2105  }
2106  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2107  status=MagickFalse;
2108  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2109  {
2111  proceed;
2112 
2113 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2114  #pragma omp atomic
2115 #endif
2116  progress++;
2117  proceed=SetImageProgress(image,ThresholdImageTag,progress,
2118  image->rows);
2119  if (proceed == MagickFalse)
2120  status=MagickFalse;
2121  }
2122  }
2123  image_view=DestroyCacheView(image_view);
2124  random_info=DestroyRandomInfoThreadSet(random_info);
2125  return(status);
2126 }
2127 
2128 /*
2129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2130 % %
2131 % %
2132 % %
2133 % R a n g e T h r e s h o l d I m a g e %
2134 % %
2135 % %
2136 % %
2137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2138 %
2139 % RangeThresholdImage() applies soft and hard thresholding.
2140 %
2141 % The format of the RangeThresholdImage method is:
2142 %
2143 % MagickBooleanType RangeThresholdImage(Image *image,
2144 % const double low_black,const double low_white,const double high_white,
2145 % const double high_black,ExceptionInfo *exception)
2146 %
2147 % A description of each parameter follows:
2148 %
2149 % o image: the image.
2150 %
2151 % o low_black: Define the minimum threshold value.
2152 %
2153 % o low_white: Define the maximum threshold value.
2154 %
2155 % o high_white: Define the minimum threshold value.
2156 %
2157 % o low_white: Define the maximum threshold value.
2158 %
2159 % o exception: return any errors or warnings in this structure.
2160 %
2161 */
2163  const double low_black,const double low_white,const double high_white,
2164  const double high_black,ExceptionInfo *exception)
2165 {
2166 #define ThresholdImageTag "Threshold/Image"
2167 
2168  CacheView
2169  *image_view;
2170 
2172  status;
2173 
2175  progress;
2176 
2177  ssize_t
2178  y;
2179 
2180  assert(image != (Image *) NULL);
2181  assert(image->signature == MagickCoreSignature);
2182  if (image->debug != MagickFalse)
2183  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2184  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2185  return(MagickFalse);
2186  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2187  (void) TransformImageColorspace(image,sRGBColorspace,exception);
2188  /*
2189  Range threshold image.
2190  */
2191  status=MagickTrue;
2192  progress=0;
2193  image_view=AcquireAuthenticCacheView(image,exception);
2194 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2195  #pragma omp parallel for schedule(static) shared(progress,status) \
2196  magick_number_threads(image,image,image->rows,1)
2197 #endif
2198  for (y=0; y < (ssize_t) image->rows; y++)
2199  {
2200  register ssize_t
2201  x;
2202 
2203  register Quantum
2204  *magick_restrict q;
2205 
2206  if (status == MagickFalse)
2207  continue;
2208  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2209  if (q == (Quantum *) NULL)
2210  {
2211  status=MagickFalse;
2212  continue;
2213  }
2214  for (x=0; x < (ssize_t) image->columns; x++)
2215  {
2216  double
2217  pixel;
2218 
2219  register ssize_t
2220  i;
2221 
2222  pixel=GetPixelIntensity(image,q);
2223  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2224  {
2225  PixelChannel channel = GetPixelChannelChannel(image,i);
2226  PixelTrait traits = GetPixelChannelTraits(image,channel);
2227  if ((traits & UpdatePixelTrait) == 0)
2228  continue;
2229  if (image->channel_mask != DefaultChannels)
2230  pixel=(double) q[i];
2231  if (pixel < low_black)
2232  q[i]=0;
2233  else
2234  if ((pixel >= low_black) && (pixel < low_white))
2236  PerceptibleReciprocal(low_white-low_black)*(pixel-low_black));
2237  else
2238  if ((pixel >= low_white) && (pixel <= high_white))
2239  q[i]=QuantumRange;
2240  else
2241  if ((pixel > high_white) && (pixel <= high_black))
2243  high_black-high_white)*(high_black-pixel));
2244  else
2245  if (pixel > high_black)
2246  q[i]=0;
2247  else
2248  q[i]=0;
2249  }
2250  q+=GetPixelChannels(image);
2251  }
2252  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2253  status=MagickFalse;
2254  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2255  {
2257  proceed;
2258 
2259 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2260  #pragma omp atomic
2261 #endif
2262  progress++;
2263  proceed=SetImageProgress(image,ThresholdImageTag,progress,
2264  image->rows);
2265  if (proceed == MagickFalse)
2266  status=MagickFalse;
2267  }
2268  }
2269  image_view=DestroyCacheView(image_view);
2270  return(status);
2271 }
2272 
2273 /*
2274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2275 % %
2276 % %
2277 % %
2278 % W h i t e T h r e s h o l d I m a g e %
2279 % %
2280 % %
2281 % %
2282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2283 %
2284 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
2285 % the threshold into white while leaving all pixels at or below the threshold
2286 % unchanged.
2287 %
2288 % The format of the WhiteThresholdImage method is:
2289 %
2290 % MagickBooleanType WhiteThresholdImage(Image *image,
2291 % const char *threshold,ExceptionInfo *exception)
2292 %
2293 % A description of each parameter follows:
2294 %
2295 % o image: the image.
2296 %
2297 % o threshold: Define the threshold value.
2298 %
2299 % o exception: return any errors or warnings in this structure.
2300 %
2301 */
2303  const char *thresholds,ExceptionInfo *exception)
2304 {
2305 #define ThresholdImageTag "Threshold/Image"
2306 
2307  CacheView
2308  *image_view;
2309 
2310  GeometryInfo
2311  geometry_info;
2312 
2314  status;
2315 
2317  progress;
2318 
2319  PixelInfo
2320  threshold;
2321 
2323  flags;
2324 
2325  ssize_t
2326  y;
2327 
2328  assert(image != (Image *) NULL);
2329  assert(image->signature == MagickCoreSignature);
2330  if (image->debug != MagickFalse)
2331  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2332  if (thresholds == (const char *) NULL)
2333  return(MagickTrue);
2334  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2335  return(MagickFalse);
2336  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2337  (void) TransformImageColorspace(image,sRGBColorspace,exception);
2338  GetPixelInfo(image,&threshold);
2339  flags=ParseGeometry(thresholds,&geometry_info);
2340  threshold.red=geometry_info.rho;
2341  threshold.green=geometry_info.rho;
2342  threshold.blue=geometry_info.rho;
2343  threshold.black=geometry_info.rho;
2344  threshold.alpha=100.0;
2345  if ((flags & SigmaValue) != 0)
2346  threshold.green=geometry_info.sigma;
2347  if ((flags & XiValue) != 0)
2348  threshold.blue=geometry_info.xi;
2349  if ((flags & PsiValue) != 0)
2350  threshold.alpha=geometry_info.psi;
2351  if (threshold.colorspace == CMYKColorspace)
2352  {
2353  if ((flags & PsiValue) != 0)
2354  threshold.black=geometry_info.psi;
2355  if ((flags & ChiValue) != 0)
2356  threshold.alpha=geometry_info.chi;
2357  }
2358  if ((flags & PercentValue) != 0)
2359  {
2360  threshold.red*=(MagickRealType) (QuantumRange/100.0);
2361  threshold.green*=(MagickRealType) (QuantumRange/100.0);
2362  threshold.blue*=(MagickRealType) (QuantumRange/100.0);
2363  threshold.black*=(MagickRealType) (QuantumRange/100.0);
2364  threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
2365  }
2366  /*
2367  White threshold image.
2368  */
2369  status=MagickTrue;
2370  progress=0;
2371  image_view=AcquireAuthenticCacheView(image,exception);
2372 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2373  #pragma omp parallel for schedule(static) shared(progress,status) \
2374  magick_number_threads(image,image,image->rows,1)
2375 #endif
2376  for (y=0; y < (ssize_t) image->rows; y++)
2377  {
2378  register ssize_t
2379  x;
2380 
2381  register Quantum
2382  *magick_restrict q;
2383 
2384  if (status == MagickFalse)
2385  continue;
2386  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2387  if (q == (Quantum *) NULL)
2388  {
2389  status=MagickFalse;
2390  continue;
2391  }
2392  for (x=0; x < (ssize_t) image->columns; x++)
2393  {
2394  double
2395  pixel;
2396 
2397  register ssize_t
2398  i;
2399 
2400  pixel=GetPixelIntensity(image,q);
2401  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2402  {
2403  PixelChannel channel = GetPixelChannelChannel(image,i);
2404  PixelTrait traits = GetPixelChannelTraits(image,channel);
2405  if ((traits & UpdatePixelTrait) == 0)
2406  continue;
2407  if (image->channel_mask != DefaultChannels)
2408  pixel=(double) q[i];
2409  if (pixel > GetPixelInfoChannel(&threshold,channel))
2410  q[i]=QuantumRange;
2411  }
2412  q+=GetPixelChannels(image);
2413  }
2414  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2415  status=MagickFalse;
2416  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2417  {
2419  proceed;
2420 
2421 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2422  #pragma omp atomic
2423 #endif
2424  progress++;
2425  proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
2426  if (proceed == MagickFalse)
2427  status=MagickFalse;
2428  }
2429  }
2430  image_view=DestroyCacheView(image_view);
2431  return(status);
2432 }
#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:923
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:2162
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:3881
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:1597
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:1876
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:1513
#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: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:487
MagickExport ThresholdMap * GetThresholdMap(const char *map_id, ExceptionInfo *exception)
Definition: threshold.c:1244
#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:1074
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1398
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:2615
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: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: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:2012
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:1205
static ThresholdMap * GetThresholdMapFile(const char *, const char *, const char *, ExceptionInfo *)
Definition: threshold.c:1307
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:1864
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:2302
MagickExport MagickBooleanType ClampImage(Image *image, ExceptionInfo *exception)
Definition: threshold.c:1083
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: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:1674
PixelTrait
Definition: pixel.h:135
static double TriangleThreshold(const double *histogram, ExceptionInfo *exception)
Definition: threshold.c:566
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:658
ssize_t * levels
Definition: threshold.c:112
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1179
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
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