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