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