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