MagickCore  7.0.11
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 
1916  ssize_t
1917  y;
1918 
1919  ThresholdMap
1920  *map;
1921 
1922  assert(image != (Image *) NULL);
1923  assert(image->signature == MagickCoreSignature);
1924  if (image->debug != MagickFalse)
1925  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1926  assert(exception != (ExceptionInfo *) NULL);
1927  assert(exception->signature == MagickCoreSignature);
1928  if (threshold_map == (const char *) NULL)
1929  return(MagickTrue);
1930  p=(char *) threshold_map;
1931  while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1932  (*p != '\0'))
1933  p++;
1934  threshold_map=p;
1935  while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1936  (*p != '\0'))
1937  {
1938  if ((p-threshold_map) >= (MagickPathExtent-1))
1939  break;
1940  token[p-threshold_map]=(*p);
1941  p++;
1942  }
1943  token[p-threshold_map]='\0';
1944  map=GetThresholdMap(token,exception);
1945  if (map == (ThresholdMap *) NULL)
1946  {
1948  "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1949  return(MagickFalse);
1950  }
1951  for (i=0; i < MaxPixelChannels; i++)
1952  levels[i]=2.0;
1953  p=strchr((char *) threshold_map,',');
1954  if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1955  {
1956  (void) GetNextToken(p,&p,MagickPathExtent,token);
1957  for (i=0; (i < MaxPixelChannels); i++)
1958  levels[i]=StringToDouble(token,(char **) NULL);
1959  for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1960  {
1961  (void) GetNextToken(p,&p,MagickPathExtent,token);
1962  if (*token == ',')
1963  (void) GetNextToken(p,&p,MagickPathExtent,token);
1964  levels[i]=StringToDouble(token,(char **) NULL);
1965  }
1966  }
1967  for (i=0; i < MaxPixelChannels; i++)
1968  if (fabs(levels[i]) >= 1)
1969  levels[i]-=1.0;
1970  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1971  return(MagickFalse);
1972  status=MagickTrue;
1973  progress=0;
1974  image_view=AcquireAuthenticCacheView(image,exception);
1975 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1976  #pragma omp parallel for schedule(static) shared(progress,status) \
1977  magick_number_threads(image,image,image->rows,1)
1978 #endif
1979  for (y=0; y < (ssize_t) image->rows; y++)
1980  {
1981  ssize_t
1982  x;
1983 
1984  Quantum
1985  *magick_restrict q;
1986 
1987  if (status == MagickFalse)
1988  continue;
1989  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1990  if (q == (Quantum *) NULL)
1991  {
1992  status=MagickFalse;
1993  continue;
1994  }
1995  for (x=0; x < (ssize_t) image->columns; x++)
1996  {
1997  ssize_t
1998  i;
1999 
2000  ssize_t
2001  n;
2002 
2003  n=0;
2004  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2005  {
2006  ssize_t
2007  level,
2008  threshold;
2009 
2010  PixelChannel channel = GetPixelChannelChannel(image,i);
2011  PixelTrait traits = GetPixelChannelTraits(image,channel);
2012  if ((traits & UpdatePixelTrait) == 0)
2013  continue;
2014  if (fabs(levels[n]) < MagickEpsilon)
2015  {
2016  n++;
2017  continue;
2018  }
2019  threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
2020  level=threshold/(map->divisor-1);
2021  threshold-=level*(map->divisor-1);
2022  q[i]=ClampToQuantum((double) (level+(threshold >=
2023  map->levels[(x % map->width)+map->width*(y % map->height)]))*
2024  QuantumRange/levels[n]);
2025  n++;
2026  }
2027  q+=GetPixelChannels(image);
2028  }
2029  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2030  status=MagickFalse;
2031  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2032  {
2034  proceed;
2035 
2036 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2037  #pragma omp atomic
2038 #endif
2039  progress++;
2040  proceed=SetImageProgress(image,DitherImageTag,progress,image->rows);
2041  if (proceed == MagickFalse)
2042  status=MagickFalse;
2043  }
2044  }
2045  image_view=DestroyCacheView(image_view);
2046  map=DestroyThresholdMap(map);
2047  return(MagickTrue);
2048 }
2049 
2050 /*
2051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2052 % %
2053 % %
2054 % %
2055 % P e r c e p t i b l e I m a g e %
2056 % %
2057 % %
2058 % %
2059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2060 %
2061 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
2062 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
2063 % unchanged.
2064 %
2065 % The format of the PerceptibleImage method is:
2066 %
2067 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
2068 % ExceptionInfo *exception)
2069 %
2070 % A description of each parameter follows:
2071 %
2072 % o image: the image.
2073 %
2074 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
2075 %
2076 % o exception: return any errors or warnings in this structure.
2077 %
2078 */
2079 
2080 static inline Quantum PerceptibleThreshold(const Quantum quantum,
2081  const double epsilon)
2082 {
2083  double
2084  sign;
2085 
2086  sign=(double) quantum < 0.0 ? -1.0 : 1.0;
2087  if ((sign*quantum) >= epsilon)
2088  return(quantum);
2089  return((Quantum) (sign*epsilon));
2090 }
2091 
2093  const double epsilon,ExceptionInfo *exception)
2094 {
2095 #define PerceptibleImageTag "Perceptible/Image"
2096 
2097  CacheView
2098  *image_view;
2099 
2101  status;
2102 
2104  progress;
2105 
2106  ssize_t
2107  y;
2108 
2109  assert(image != (Image *) NULL);
2110  assert(image->signature == MagickCoreSignature);
2111  if (image->debug != MagickFalse)
2112  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2113  if (image->storage_class == PseudoClass)
2114  {
2115  ssize_t
2116  i;
2117 
2118  PixelInfo
2119  *magick_restrict q;
2120 
2121  q=image->colormap;
2122  for (i=0; i < (ssize_t) image->colors; i++)
2123  {
2124  q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
2125  epsilon);
2126  q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
2127  epsilon);
2128  q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
2129  epsilon);
2130  q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
2131  epsilon);
2132  q++;
2133  }
2134  return(SyncImage(image,exception));
2135  }
2136  /*
2137  Perceptible image.
2138  */
2139  status=MagickTrue;
2140  progress=0;
2141  image_view=AcquireAuthenticCacheView(image,exception);
2142 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2143  #pragma omp parallel for schedule(static) shared(progress,status) \
2144  magick_number_threads(image,image,image->rows,1)
2145 #endif
2146  for (y=0; y < (ssize_t) image->rows; y++)
2147  {
2148  ssize_t
2149  x;
2150 
2151  Quantum
2152  *magick_restrict q;
2153 
2154  if (status == MagickFalse)
2155  continue;
2156  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2157  if (q == (Quantum *) NULL)
2158  {
2159  status=MagickFalse;
2160  continue;
2161  }
2162  for (x=0; x < (ssize_t) image->columns; x++)
2163  {
2164  ssize_t
2165  i;
2166 
2167  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2168  {
2169  PixelChannel channel = GetPixelChannelChannel(image,i);
2170  PixelTrait traits = GetPixelChannelTraits(image,channel);
2171  if (traits == UndefinedPixelTrait)
2172  continue;
2173  q[i]=PerceptibleThreshold(q[i],epsilon);
2174  }
2175  q+=GetPixelChannels(image);
2176  }
2177  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2178  status=MagickFalse;
2179  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2180  {
2182  proceed;
2183 
2184 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2185  #pragma omp atomic
2186 #endif
2187  progress++;
2188  proceed=SetImageProgress(image,PerceptibleImageTag,progress,
2189  image->rows);
2190  if (proceed == MagickFalse)
2191  status=MagickFalse;
2192  }
2193  }
2194  image_view=DestroyCacheView(image_view);
2195  return(status);
2196 }
2197 
2198 /*
2199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2200 % %
2201 % %
2202 % %
2203 % R a n d o m T h r e s h o l d I m a g e %
2204 % %
2205 % %
2206 % %
2207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2208 %
2209 % RandomThresholdImage() changes the value of individual pixels based on the
2210 % intensity of each pixel compared to a random threshold. The result is a
2211 % low-contrast, two color image.
2212 %
2213 % The format of the RandomThresholdImage method is:
2214 %
2215 % MagickBooleanType RandomThresholdImage(Image *image,
2216 % const char *thresholds,ExceptionInfo *exception)
2217 %
2218 % A description of each parameter follows:
2219 %
2220 % o image: the image.
2221 %
2222 % o low,high: Specify the high and low thresholds. These values range from
2223 % 0 to QuantumRange.
2224 %
2225 % o exception: return any errors or warnings in this structure.
2226 %
2227 */
2229  const double min_threshold, const double max_threshold,ExceptionInfo *exception)
2230 {
2231 #define ThresholdImageTag "Threshold/Image"
2232 
2233  CacheView
2234  *image_view;
2235 
2237  status;
2238 
2240  progress;
2241 
2242  PixelInfo
2243  threshold;
2244 
2245  RandomInfo
2247 
2248  ssize_t
2249  y;
2250 
2251 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2252  unsigned long
2253  key;
2254 #endif
2255 
2256  assert(image != (Image *) NULL);
2257  assert(image->signature == MagickCoreSignature);
2258  if (image->debug != MagickFalse)
2259  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2260  assert(exception != (ExceptionInfo *) NULL);
2261  assert(exception->signature == MagickCoreSignature);
2262  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2263  return(MagickFalse);
2264  GetPixelInfo(image,&threshold);
2265  /*
2266  Random threshold image.
2267  */
2268  status=MagickTrue;
2269  progress=0;
2271  image_view=AcquireAuthenticCacheView(image,exception);
2272 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2274  #pragma omp parallel for schedule(static) shared(progress,status) \
2275  magick_number_threads(image,image,image->rows,key == ~0UL)
2276 #endif
2277  for (y=0; y < (ssize_t) image->rows; y++)
2278  {
2279  const int
2280  id = GetOpenMPThreadId();
2281 
2282  Quantum
2283  *magick_restrict q;
2284 
2285  ssize_t
2286  x;
2287 
2288  if (status == MagickFalse)
2289  continue;
2290  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2291  if (q == (Quantum *) NULL)
2292  {
2293  status=MagickFalse;
2294  continue;
2295  }
2296  for (x=0; x < (ssize_t) image->columns; x++)
2297  {
2298  ssize_t
2299  i;
2300 
2301  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2302  {
2303  double
2304  threshold;
2305 
2306  PixelChannel channel = GetPixelChannelChannel(image,i);
2307  PixelTrait traits = GetPixelChannelTraits(image,channel);
2308  if ((traits & UpdatePixelTrait) == 0)
2309  continue;
2310  if ((double) q[i] < min_threshold)
2311  threshold=min_threshold;
2312  else
2313  if ((double) q[i] > max_threshold)
2314  threshold=max_threshold;
2315  else
2316  threshold=(double) (QuantumRange*
2318  q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
2319  }
2320  q+=GetPixelChannels(image);
2321  }
2322  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2323  status=MagickFalse;
2324  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2325  {
2327  proceed;
2328 
2329 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2330  #pragma omp atomic
2331 #endif
2332  progress++;
2333  proceed=SetImageProgress(image,ThresholdImageTag,progress,
2334  image->rows);
2335  if (proceed == MagickFalse)
2336  status=MagickFalse;
2337  }
2338  }
2339  image_view=DestroyCacheView(image_view);
2341  return(status);
2342 }
2343 
2344 /*
2345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2346 % %
2347 % %
2348 % %
2349 % R a n g e T h r e s h o l d I m a g e %
2350 % %
2351 % %
2352 % %
2353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2354 %
2355 % RangeThresholdImage() applies soft and hard thresholding.
2356 %
2357 % The format of the RangeThresholdImage method is:
2358 %
2359 % MagickBooleanType RangeThresholdImage(Image *image,
2360 % const double low_black,const double low_white,const double high_white,
2361 % const double high_black,ExceptionInfo *exception)
2362 %
2363 % A description of each parameter follows:
2364 %
2365 % o image: the image.
2366 %
2367 % o low_black: Define the minimum black threshold value.
2368 %
2369 % o low_white: Define the minimum white threshold value.
2370 %
2371 % o high_white: Define the maximum white threshold value.
2372 %
2373 % o high_black: Define the maximum black threshold value.
2374 %
2375 % o exception: return any errors or warnings in this structure.
2376 %
2377 */
2379  const double low_black,const double low_white,const double high_white,
2380  const double high_black,ExceptionInfo *exception)
2381 {
2382 #define ThresholdImageTag "Threshold/Image"
2383 
2384  CacheView
2385  *image_view;
2386 
2388  status;
2389 
2391  progress;
2392 
2393  ssize_t
2394  y;
2395 
2396  assert(image != (Image *) NULL);
2397  assert(image->signature == MagickCoreSignature);
2398  if (image->debug != MagickFalse)
2399  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2400  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2401  return(MagickFalse);
2402  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2403  (void) TransformImageColorspace(image,sRGBColorspace,exception);
2404  /*
2405  Range threshold image.
2406  */
2407  status=MagickTrue;
2408  progress=0;
2409  image_view=AcquireAuthenticCacheView(image,exception);
2410 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2411  #pragma omp parallel for schedule(static) shared(progress,status) \
2412  magick_number_threads(image,image,image->rows,1)
2413 #endif
2414  for (y=0; y < (ssize_t) image->rows; y++)
2415  {
2416  ssize_t
2417  x;
2418 
2419  Quantum
2420  *magick_restrict q;
2421 
2422  if (status == MagickFalse)
2423  continue;
2424  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2425  if (q == (Quantum *) NULL)
2426  {
2427  status=MagickFalse;
2428  continue;
2429  }
2430  for (x=0; x < (ssize_t) image->columns; x++)
2431  {
2432  double
2433  pixel;
2434 
2435  ssize_t
2436  i;
2437 
2438  pixel=GetPixelIntensity(image,q);
2439  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2440  {
2441  PixelChannel channel = GetPixelChannelChannel(image,i);
2442  PixelTrait traits = GetPixelChannelTraits(image,channel);
2443  if ((traits & UpdatePixelTrait) == 0)
2444  continue;
2445  if (image->channel_mask != DefaultChannels)
2446  pixel=(double) q[i];
2447  if (pixel < low_black)
2448  q[i]=(Quantum) 0;
2449  else
2450  if ((pixel >= low_black) && (pixel < low_white))
2452  PerceptibleReciprocal(low_white-low_black)*(pixel-low_black));
2453  else
2454  if ((pixel >= low_white) && (pixel <= high_white))
2455  q[i]=QuantumRange;
2456  else
2457  if ((pixel > high_white) && (pixel <= high_black))
2459  high_black-high_white)*(high_black-pixel));
2460  else
2461  if (pixel > high_black)
2462  q[i]=(Quantum) 0;
2463  else
2464  q[i]=(Quantum) 0;
2465  }
2466  q+=GetPixelChannels(image);
2467  }
2468  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2469  status=MagickFalse;
2470  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2471  {
2473  proceed;
2474 
2475 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2476  #pragma omp atomic
2477 #endif
2478  progress++;
2479  proceed=SetImageProgress(image,ThresholdImageTag,progress,
2480  image->rows);
2481  if (proceed == MagickFalse)
2482  status=MagickFalse;
2483  }
2484  }
2485  image_view=DestroyCacheView(image_view);
2486  return(status);
2487 }
2488 
2489 /*
2490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2491 % %
2492 % %
2493 % %
2494 % W h i t e T h r e s h o l d I m a g e %
2495 % %
2496 % %
2497 % %
2498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2499 %
2500 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
2501 % the threshold into white while leaving all pixels at or below the threshold
2502 % unchanged.
2503 %
2504 % The format of the WhiteThresholdImage method is:
2505 %
2506 % MagickBooleanType WhiteThresholdImage(Image *image,
2507 % const char *threshold,ExceptionInfo *exception)
2508 %
2509 % A description of each parameter follows:
2510 %
2511 % o image: the image.
2512 %
2513 % o threshold: Define the threshold value.
2514 %
2515 % o exception: return any errors or warnings in this structure.
2516 %
2517 */
2519  const char *thresholds,ExceptionInfo *exception)
2520 {
2521 #define ThresholdImageTag "Threshold/Image"
2522 
2523  CacheView
2524  *image_view;
2525 
2526  GeometryInfo
2527  geometry_info;
2528 
2530  status;
2531 
2533  progress;
2534 
2535  PixelInfo
2536  threshold;
2537 
2539  flags;
2540 
2541  ssize_t
2542  y;
2543 
2544  assert(image != (Image *) NULL);
2545  assert(image->signature == MagickCoreSignature);
2546  if (image->debug != MagickFalse)
2547  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2548  if (thresholds == (const char *) NULL)
2549  return(MagickTrue);
2550  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2551  return(MagickFalse);
2552  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2553  (void) TransformImageColorspace(image,sRGBColorspace,exception);
2554  GetPixelInfo(image,&threshold);
2555  flags=ParseGeometry(thresholds,&geometry_info);
2556  threshold.red=geometry_info.rho;
2557  threshold.green=geometry_info.rho;
2558  threshold.blue=geometry_info.rho;
2559  threshold.black=geometry_info.rho;
2560  threshold.alpha=100.0;
2561  if ((flags & SigmaValue) != 0)
2562  threshold.green=geometry_info.sigma;
2563  if ((flags & XiValue) != 0)
2564  threshold.blue=geometry_info.xi;
2565  if ((flags & PsiValue) != 0)
2566  threshold.alpha=geometry_info.psi;
2567  if (threshold.colorspace == CMYKColorspace)
2568  {
2569  if ((flags & PsiValue) != 0)
2570  threshold.black=geometry_info.psi;
2571  if ((flags & ChiValue) != 0)
2572  threshold.alpha=geometry_info.chi;
2573  }
2574  if ((flags & PercentValue) != 0)
2575  {
2576  threshold.red*=(MagickRealType) (QuantumRange/100.0);
2577  threshold.green*=(MagickRealType) (QuantumRange/100.0);
2578  threshold.blue*=(MagickRealType) (QuantumRange/100.0);
2579  threshold.black*=(MagickRealType) (QuantumRange/100.0);
2580  threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
2581  }
2582  /*
2583  White threshold image.
2584  */
2585  status=MagickTrue;
2586  progress=0;
2587  image_view=AcquireAuthenticCacheView(image,exception);
2588 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2589  #pragma omp parallel for schedule(static) shared(progress,status) \
2590  magick_number_threads(image,image,image->rows,1)
2591 #endif
2592  for (y=0; y < (ssize_t) image->rows; y++)
2593  {
2594  ssize_t
2595  x;
2596 
2597  Quantum
2598  *magick_restrict q;
2599 
2600  if (status == MagickFalse)
2601  continue;
2602  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2603  if (q == (Quantum *) NULL)
2604  {
2605  status=MagickFalse;
2606  continue;
2607  }
2608  for (x=0; x < (ssize_t) image->columns; x++)
2609  {
2610  double
2611  pixel;
2612 
2613  ssize_t
2614  i;
2615 
2616  pixel=GetPixelIntensity(image,q);
2617  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2618  {
2619  PixelChannel channel = GetPixelChannelChannel(image,i);
2620  PixelTrait traits = GetPixelChannelTraits(image,channel);
2621  if ((traits & UpdatePixelTrait) == 0)
2622  continue;
2623  if (image->channel_mask != DefaultChannels)
2624  pixel=(double) q[i];
2625  if (pixel > GetPixelInfoChannel(&threshold,channel))
2626  q[i]=QuantumRange;
2627  }
2628  q+=GetPixelChannels(image);
2629  }
2630  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2631  status=MagickFalse;
2632  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2633  {
2635  proceed;
2636 
2637 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2638  #pragma omp atomic
2639 #endif
2640  progress++;
2641  proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
2642  if (proceed == MagickFalse)
2643  status=MagickFalse;
2644  }
2645  }
2646  image_view=DestroyCacheView(image_view);
2647  return(status);
2648 }
#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:2378
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:1611
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:3052
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 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: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:467
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:2092
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:1168
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:372
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: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:4238
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:1378
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:1403
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:1255
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:2228
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:776
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 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:2080
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:2518
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:666
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