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