MagickCore  6.9.12-67
Convert, Edit, Or Compose Bitmap Images
 All Data Structures
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-2021 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 
40 /*
41  Include declarations.
42 */
43 #include "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+=r[u].red;
283  channel_bias.green+=r[u].green;
284  channel_bias.blue+=r[u].blue;
285  channel_bias.opacity+=r[u].opacity;
286  if (image->colorspace == CMYKColorspace)
287  channel_bias.index=(MagickRealType)
288  GetPixelIndex(indexes+(r-p)+u);
289  }
290  channel_sum.red+=r[u].red;
291  channel_sum.green+=r[u].green;
292  channel_sum.blue+=r[u].blue;
293  channel_sum.opacity+=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+=r[0].red;
315  channel_bias.green+=r[0].green;
316  channel_bias.blue+=r[0].blue;
317  channel_bias.opacity+=r[0].opacity;
318  if (image->colorspace == CMYKColorspace)
319  channel_bias.index=(MagickRealType) GetPixelIndex(indexes+x+(r-p)+0);
320  channel_sum.red+=r[width-1].red;
321  channel_sum.green+=r[width-1].green;
322  channel_sum.blue+=r[width-1].blue;
323  channel_sum.opacity+=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 entopy 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 Image *image,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  magick_unreferenced(image);
609  start=0; /* find start bin, first bin not zero count */
610  for (i=0; i <= (ssize_t) MaxIntensity; i++)
611  if (histogram[i] > 0.0)
612  {
613  start=i;
614  break;
615  }
616  end=0; /* find end bin, last bin not zero count */
617  for (i=(ssize_t) MaxIntensity; i >= 0; i--)
618  if (histogram[i] > 0.0)
619  {
620  end=i;
621  break;
622  }
623  max=0; /* find max bin, bin with largest count */
624  count=0.0;
625  for (i=0; i <= (ssize_t) MaxIntensity; i++)
626  if (histogram[i] > count)
627  {
628  max=i;
629  count=histogram[i];
630  }
631  /*
632  Compute threshold at split point.
633  */
634  x1=(double) max;
635  y1=histogram[max];
636  x2=(double) end;
637  if ((max-start) >= (end-max))
638  x2=(double) start;
639  y2=0.0;
640  a=y1-y2;
641  b=x2-x1;
642  c=(-1.0)*(a*x1+b*y1);
643  inverse_ratio=1.0/sqrt(a*a+b*b+c*c);
644  threshold=0;
645  max_distance=0.0;
646  if (x2 == (double) start)
647  for (i=start; i < max; i++)
648  {
649  segment=inverse_ratio*(a*i+b*histogram[i]+c);
650  distance=sqrt(segment*segment);
651  if ((distance > max_distance) && (segment > 0.0))
652  {
653  threshold=i;
654  max_distance=distance;
655  }
656  }
657  else
658  for (i=end; i > max; i--)
659  {
660  segment=inverse_ratio*(a*i+b*histogram[i]+c);
661  distance=sqrt(segment*segment);
662  if ((distance > max_distance) && (segment < 0.0))
663  {
664  threshold=i;
665  max_distance=distance;
666  }
667  }
668  return(100.0*threshold/MaxIntensity);
669 }
670 
671 MagickExport MagickBooleanType AutoThresholdImage(Image *image,
672  const AutoThresholdMethod method,ExceptionInfo *exception)
673 {
674  CacheView
675  *image_view;
676 
677  char
678  property[MagickPathExtent];
679 
680  const char
681  *artifact;
682 
683  double
684  gamma,
685  *histogram,
686  sum,
687  threshold;
688 
689  MagickBooleanType
690  status;
691 
692  ssize_t
693  i;
694 
695  ssize_t
696  y;
697 
698  /*
699  Form histogram.
700  */
701  assert(image != (Image *) NULL);
702  assert(image->signature == MagickCoreSignature);
703  if (IsEventLogging() != MagickFalse)
704  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
705  histogram=(double *) AcquireQuantumMemory(MaxIntensity+1UL,
706  sizeof(*histogram));
707  if (histogram == (double *) NULL)
708  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
709  image->filename);
710  status=MagickTrue;
711  (void) memset(histogram,0,(MaxIntensity+1UL)*sizeof(*histogram));
712  image_view=AcquireVirtualCacheView(image,exception);
713  for (y=0; y < (ssize_t) image->rows; y++)
714  {
715  const PixelPacket
716  *magick_restrict p;
717 
718  ssize_t
719  x;
720 
721  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
722  if (p == (const PixelPacket *) NULL)
723  break;
724  for (x=0; x < (ssize_t) image->columns; x++)
725  {
726  double intensity = GetPixelIntensity(image,p);
727  histogram[ScaleQuantumToChar(ClampToQuantum(intensity))]++;
728  p++;
729  }
730  }
731  image_view=DestroyCacheView(image_view);
732  /*
733  Normalize histogram.
734  */
735  sum=0.0;
736  for (i=0; i <= (ssize_t) MaxIntensity; i++)
737  sum+=histogram[i];
738  gamma=PerceptibleReciprocal(sum);
739  for (i=0; i <= (ssize_t) MaxIntensity; i++)
740  histogram[i]=gamma*histogram[i];
741  /*
742  Discover threshold from histogram.
743  */
744  switch (method)
745  {
746  case KapurThresholdMethod:
747  {
748  threshold=KapurThreshold(image,histogram,exception);
749  break;
750  }
751  case OTSUThresholdMethod:
752  default:
753  {
754  threshold=OTSUThreshold(image,histogram,exception);
755  break;
756  }
757  case TriangleThresholdMethod:
758  {
759  threshold=TriangleThreshold(image,histogram);
760  break;
761  }
762  }
763  histogram=(double *) RelinquishMagickMemory(histogram);
764  if (threshold < 0.0)
765  status=MagickFalse;
766  if (status == MagickFalse)
767  return(MagickFalse);
768  /*
769  Threshold image.
770  */
771  (void) FormatLocaleString(property,MagickPathExtent,"%g%%",threshold);
772  (void) SetImageProperty(image,"auto-threshold:threshold",property);
773  artifact=GetImageArtifact(image,"threshold:verbose");
774  if (IsStringTrue(artifact) != MagickFalse)
775  (void) FormatLocaleFile(stdout,"%.*g%%\n",GetMagickPrecision(),threshold);
776  return(BilevelImage(image,QuantumRange*threshold/100.0));
777 }
778 
779 /*
780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
781 % %
782 % %
783 % %
784 % B i l e v e l I m a g e %
785 % %
786 % %
787 % %
788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
789 %
790 % BilevelImage() changes the value of individual pixels based on the
791 % intensity of each pixel channel. The result is a high-contrast image.
792 %
793 % More precisely each channel value of the image is 'thresholded' so that if
794 % it is equal to or less than the given value it is set to zero, while any
795 % value greater than that give is set to it maximum or QuantumRange.
796 %
797 % This function is what is used to implement the "-threshold" operator for
798 % the command line API.
799 %
800 % If the default channel setting is given the image is thresholded using just
801 % the gray 'intensity' of the image, rather than the individual channels.
802 %
803 % The format of the BilevelImageChannel method is:
804 %
805 % MagickBooleanType BilevelImage(Image *image,const double threshold)
806 % MagickBooleanType BilevelImageChannel(Image *image,
807 % const ChannelType channel,const double threshold)
808 %
809 % A description of each parameter follows:
810 %
811 % o image: the image.
812 %
813 % o channel: the channel type.
814 %
815 % o threshold: define the threshold values.
816 %
817 % Aside: You can get the same results as operator using LevelImageChannels()
818 % with the 'threshold' value for both the black_point and the white_point.
819 %
820 */
821 
822 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
823 {
824  MagickBooleanType
825  status;
826 
827  status=BilevelImageChannel(image,DefaultChannels,threshold);
828  return(status);
829 }
830 
831 MagickExport MagickBooleanType BilevelImageChannel(Image *image,
832  const ChannelType channel,const double threshold)
833 {
834 #define ThresholdImageTag "Threshold/Image"
835 
836  CacheView
837  *image_view;
838 
840  *exception;
841 
842  MagickBooleanType
843  status;
844 
845  MagickOffsetType
846  progress;
847 
848  ssize_t
849  y;
850 
851  assert(image != (Image *) NULL);
852  assert(image->signature == MagickCoreSignature);
853  if (IsEventLogging() != MagickFalse)
854  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
855  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
856  return(MagickFalse);
857  if (IsGrayColorspace(image->colorspace) == MagickFalse)
858  (void) SetImageColorspace(image,sRGBColorspace);
859  /*
860  Bilevel threshold image.
861  */
862  status=MagickTrue;
863  progress=0;
864  exception=(&image->exception);
865  image_view=AcquireAuthenticCacheView(image,exception);
866 #if defined(MAGICKCORE_OPENMP_SUPPORT)
867  #pragma omp parallel for schedule(static) shared(progress,status) \
868  magick_number_threads(image,image,image->rows,1)
869 #endif
870  for (y=0; y < (ssize_t) image->rows; y++)
871  {
872  IndexPacket
873  *magick_restrict indexes;
874 
875  ssize_t
876  x;
877 
879  *magick_restrict q;
880 
881  if (status == MagickFalse)
882  continue;
883  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
884  if (q == (PixelPacket *) NULL)
885  {
886  status=MagickFalse;
887  continue;
888  }
889  indexes=GetCacheViewAuthenticIndexQueue(image_view);
890  if ((channel & SyncChannels) != 0)
891  {
892  for (x=0; x < (ssize_t) image->columns; x++)
893  {
894  SetPixelRed(q,GetPixelIntensity(image,q) <= threshold ? 0 :
895  QuantumRange);
896  SetPixelGreen(q,GetPixelRed(q));
897  SetPixelBlue(q,GetPixelRed(q));
898  q++;
899  }
900  }
901  else
902  for (x=0; x < (ssize_t) image->columns; x++)
903  {
904  if ((channel & RedChannel) != 0)
905  SetPixelRed(q,(MagickRealType) GetPixelRed(q) <= threshold ? 0 :
906  QuantumRange);
907  if ((channel & GreenChannel) != 0)
908  SetPixelGreen(q,(MagickRealType) GetPixelGreen(q) <= threshold ? 0 :
909  QuantumRange);
910  if ((channel & BlueChannel) != 0)
911  SetPixelBlue(q,(MagickRealType) GetPixelBlue(q) <= threshold ? 0 :
912  QuantumRange);
913  if ((channel & OpacityChannel) != 0)
914  {
915  if (image->matte == MagickFalse)
916  SetPixelOpacity(q,(MagickRealType) GetPixelOpacity(q) <=
917  threshold ? 0 : QuantumRange);
918  else
919  SetPixelAlpha(q,(MagickRealType) GetPixelAlpha(q) <= threshold ?
920  OpaqueOpacity : TransparentOpacity);
921  }
922  if (((channel & IndexChannel) != 0) &&
923  (image->colorspace == CMYKColorspace))
924  SetPixelIndex(indexes+x,(MagickRealType) GetPixelIndex(indexes+x) <=
925  threshold ? 0 : QuantumRange);
926  q++;
927  }
928  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
929  status=MagickFalse;
930  if (image->progress_monitor != (MagickProgressMonitor) NULL)
931  {
932  MagickBooleanType
933  proceed;
934 
935 #if defined(MAGICKCORE_OPENMP_SUPPORT)
936  #pragma omp atomic
937 #endif
938  progress++;
939  proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
940  if (proceed == MagickFalse)
941  status=MagickFalse;
942  }
943  }
944  image_view=DestroyCacheView(image_view);
945  return(status);
946 }
947 
948 /*
949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
950 % %
951 % %
952 % %
953 % B l a c k T h r e s h o l d I m a g e %
954 % %
955 % %
956 % %
957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
958 %
959 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
960 % the threshold into black while leaving all pixels at or above the threshold
961 % unchanged.
962 %
963 % The format of the BlackThresholdImage method is:
964 %
965 % MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
966 % MagickBooleanType BlackThresholdImageChannel(Image *image,
967 % const ChannelType channel,const char *threshold,
968 % ExceptionInfo *exception)
969 %
970 % A description of each parameter follows:
971 %
972 % o image: the image.
973 %
974 % o channel: the channel or channels to be thresholded.
975 %
976 % o threshold: Define the threshold value.
977 %
978 % o exception: return any errors or warnings in this structure.
979 %
980 */
981 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
982  const char *threshold)
983 {
984  MagickBooleanType
985  status;
986 
987  status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
988  &image->exception);
989  return(status);
990 }
991 
992 MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
993  const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
994 {
995 #define ThresholdImageTag "Threshold/Image"
996 
997  CacheView
998  *image_view;
999 
1000  GeometryInfo
1001  geometry_info;
1002 
1003  MagickBooleanType
1004  status;
1005 
1006  MagickOffsetType
1007  progress;
1008 
1010  threshold;
1011 
1012  MagickStatusType
1013  flags;
1014 
1015  ssize_t
1016  y;
1017 
1018  assert(image != (Image *) NULL);
1019  assert(image->signature == MagickCoreSignature);
1020  if (IsEventLogging() != MagickFalse)
1021  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1022  if (thresholds == (const char *) NULL)
1023  return(MagickTrue);
1024  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1025  return(MagickFalse);
1026  GetMagickPixelPacket(image,&threshold);
1027  flags=ParseGeometry(thresholds,&geometry_info);
1028  threshold.red=geometry_info.rho;
1029  threshold.green=geometry_info.sigma;
1030  if ((flags & SigmaValue) == 0)
1031  threshold.green=threshold.red;
1032  threshold.blue=geometry_info.xi;
1033  if ((flags & XiValue) == 0)
1034  threshold.blue=threshold.red;
1035  threshold.opacity=geometry_info.psi;
1036  if ((flags & PsiValue) == 0)
1037  threshold.opacity=threshold.red;
1038  threshold.index=geometry_info.chi;
1039  if ((flags & ChiValue) == 0)
1040  threshold.index=threshold.red;
1041  if ((flags & PercentValue) != 0)
1042  {
1043  threshold.red*=(MagickRealType) (QuantumRange/100.0);
1044  threshold.green*=(MagickRealType) (QuantumRange/100.0);
1045  threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1046  threshold.opacity*=(MagickRealType) (QuantumRange/100.0);
1047  threshold.index*=(MagickRealType) (QuantumRange/100.0);
1048  }
1049  if ((IsMagickGray(&threshold) == MagickFalse) &&
1050  (IsGrayColorspace(image->colorspace) != MagickFalse))
1051  (void) SetImageColorspace(image,sRGBColorspace);
1052  /*
1053  Black threshold image.
1054  */
1055  status=MagickTrue;
1056  progress=0;
1057  image_view=AcquireAuthenticCacheView(image,exception);
1058 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1059  #pragma omp parallel for schedule(static) shared(progress,status) \
1060  magick_number_threads(image,image,image->rows,1)
1061 #endif
1062  for (y=0; y < (ssize_t) image->rows; y++)
1063  {
1064  IndexPacket
1065  *magick_restrict indexes;
1066 
1067  ssize_t
1068  x;
1069 
1070  PixelPacket
1071  *magick_restrict q;
1072 
1073  if (status == MagickFalse)
1074  continue;
1075  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1076  if (q == (PixelPacket *) NULL)
1077  {
1078  status=MagickFalse;
1079  continue;
1080  }
1081  indexes=GetCacheViewAuthenticIndexQueue(image_view);
1082  for (x=0; x < (ssize_t) image->columns; x++)
1083  {
1084  if (((channel & RedChannel) != 0) &&
1085  ((MagickRealType) GetPixelRed(q) < threshold.red))
1086  SetPixelRed(q,0);
1087  if (((channel & GreenChannel) != 0) &&
1088  ((MagickRealType) GetPixelGreen(q) < threshold.green))
1089  SetPixelGreen(q,0);
1090  if (((channel & BlueChannel) != 0) &&
1091  ((MagickRealType) GetPixelBlue(q) < threshold.blue))
1092  SetPixelBlue(q,0);
1093  if (((channel & OpacityChannel) != 0) &&
1094  ((MagickRealType) GetPixelOpacity(q) < threshold.opacity))
1095  SetPixelOpacity(q,0);
1096  if (((channel & IndexChannel) != 0) &&
1097  (image->colorspace == CMYKColorspace) &&
1098  ((MagickRealType) GetPixelIndex(indexes+x) < threshold.index))
1099  SetPixelIndex(indexes+x,0);
1100  q++;
1101  }
1102  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1103  status=MagickFalse;
1104  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1105  {
1106  MagickBooleanType
1107  proceed;
1108 
1109 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1110  #pragma omp atomic
1111 #endif
1112  progress++;
1113  proceed=SetImageProgress(image,ThresholdImageTag,progress,image->rows);
1114  if (proceed == MagickFalse)
1115  status=MagickFalse;
1116  }
1117  }
1118  image_view=DestroyCacheView(image_view);
1119  return(status);
1120 }
1121 
1122 /*
1123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124 % %
1125 % %
1126 % %
1127 % C l a m p I m a g e %
1128 % %
1129 % %
1130 % %
1131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1132 %
1133 % ClampImage() set each pixel whose value is below zero to zero and any the
1134 % pixel whose value is above the quantum range to the quantum range (e.g.
1135 % 65535) otherwise the pixel value remains unchanged.
1136 %
1137 % The format of the ClampImageChannel method is:
1138 %
1139 % MagickBooleanType ClampImage(Image *image)
1140 % MagickBooleanType ClampImageChannel(Image *image,
1141 % const ChannelType channel)
1142 %
1143 % A description of each parameter follows:
1144 %
1145 % o image: the image.
1146 %
1147 % o channel: the channel type.
1148 %
1149 */
1150 
1151 MagickExport MagickBooleanType ClampImage(Image *image)
1152 {
1153  MagickBooleanType
1154  status;
1155 
1156  status=ClampImageChannel(image,DefaultChannels);
1157  return(status);
1158 }
1159 
1160 MagickExport MagickBooleanType ClampImageChannel(Image *image,
1161  const ChannelType channel)
1162 {
1163 #define ClampImageTag "Clamp/Image"
1164 
1165  CacheView
1166  *image_view;
1167 
1169  *exception;
1170 
1171  MagickBooleanType
1172  status;
1173 
1174  MagickOffsetType
1175  progress;
1176 
1177  ssize_t
1178  y;
1179 
1180  assert(image != (Image *) NULL);
1181  assert(image->signature == MagickCoreSignature);
1182  if (IsEventLogging() != MagickFalse)
1183  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1184  if (image->storage_class == PseudoClass)
1185  {
1186  ssize_t
1187  i;
1188 
1189  PixelPacket
1190  *magick_restrict q;
1191 
1192  q=image->colormap;
1193  for (i=0; i < (ssize_t) image->colors; i++)
1194  {
1195  SetPixelRed(q,ClampPixel((MagickRealType) GetPixelRed(q)));
1196  SetPixelGreen(q,ClampPixel((MagickRealType) GetPixelGreen(q)));
1197  SetPixelBlue(q,ClampPixel((MagickRealType) GetPixelBlue(q)));
1198  SetPixelOpacity(q,ClampPixel((MagickRealType) GetPixelOpacity(q)));
1199  q++;
1200  }
1201  return(SyncImage(image));
1202  }
1203  /*
1204  Clamp image.
1205  */
1206  status=MagickTrue;
1207  progress=0;
1208  exception=(&image->exception);
1209  image_view=AcquireAuthenticCacheView(image,exception);
1210 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1211  #pragma omp parallel for schedule(static) shared(progress,status) \
1212  magick_number_threads(image,image,image->rows,1)
1213 #endif
1214  for (y=0; y < (ssize_t) image->rows; y++)
1215  {
1216  IndexPacket
1217  *magick_restrict indexes;
1218 
1219  ssize_t
1220  x;
1221 
1222  PixelPacket
1223  *magick_restrict q;
1224 
1225  if (status == MagickFalse)
1226  continue;
1227  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1228  if (q == (PixelPacket *) NULL)
1229  {
1230  status=MagickFalse;
1231  continue;
1232  }
1233  indexes=GetCacheViewAuthenticIndexQueue(image_view);
1234  for (x=0; x < (ssize_t) image->columns; x++)
1235  {
1236  if ((channel & RedChannel) != 0)
1237  SetPixelRed(q,ClampPixel((MagickRealType) GetPixelRed(q)));
1238  if ((channel & GreenChannel) != 0)
1239  SetPixelGreen(q,ClampPixel((MagickRealType) GetPixelGreen(q)));
1240  if ((channel & BlueChannel) != 0)
1241  SetPixelBlue(q,ClampPixel((MagickRealType) GetPixelBlue(q)));
1242  if ((channel & OpacityChannel) != 0)
1243  SetPixelOpacity(q,ClampPixel((MagickRealType) GetPixelOpacity(q)));
1244  if (((channel & IndexChannel) != 0) &&
1245  (image->colorspace == CMYKColorspace))
1246  SetPixelIndex(indexes+x,ClampPixel((MagickRealType) GetPixelIndex(
1247  indexes+x)));
1248  q++;
1249  }
1250  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1251  status=MagickFalse;
1252  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1253  {
1254  MagickBooleanType
1255  proceed;
1256 
1257 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1258  #pragma omp atomic
1259 #endif
1260  progress++;
1261  proceed=SetImageProgress(image,ClampImageTag,progress,image->rows);
1262  if (proceed == MagickFalse)
1263  status=MagickFalse;
1264  }
1265  }
1266  image_view=DestroyCacheView(image_view);
1267  return(status);
1268 }
1269 
1270 /*
1271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1272 % %
1273 % %
1274 % %
1275 % D e s t r o y T h r e s h o l d M a p %
1276 % %
1277 % %
1278 % %
1279 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1280 %
1281 % DestroyThresholdMap() de-allocate the given ThresholdMap
1282 %
1283 % The format of the ListThresholdMaps method is:
1284 %
1285 % ThresholdMap *DestroyThresholdMap(Threshold *map)
1286 %
1287 % A description of each parameter follows.
1288 %
1289 % o map: Pointer to the Threshold map to destroy
1290 %
1291 */
1292 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
1293 {
1294  assert(map != (ThresholdMap *) NULL);
1295  if (map->map_id != (char *) NULL)
1296  map->map_id=DestroyString(map->map_id);
1297  if (map->description != (char *) NULL)
1298  map->description=DestroyString(map->description);
1299  if (map->levels != (ssize_t *) NULL)
1300  map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
1301  map=(ThresholdMap *) RelinquishMagickMemory(map);
1302  return(map);
1303 }
1304 
1305 /*
1306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1307 % %
1308 % %
1309 % %
1310 + G e t T h r e s h o l d M a p F i l e %
1311 % %
1312 % %
1313 % %
1314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1315 %
1316 % GetThresholdMapFile() look for a given threshold map name or alias in the
1317 % given XML file data, and return the allocated the map when found.
1318 %
1319 % The format of the ListThresholdMaps method is:
1320 %
1321 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
1322 % const char *map_id,ExceptionInfo *exception)
1323 %
1324 % A description of each parameter follows.
1325 %
1326 % o xml: The threshold map list in XML format.
1327 %
1328 % o filename: The threshold map XML filename.
1329 %
1330 % o map_id: ID of the map to look for in XML list.
1331 %
1332 % o exception: return any errors or warnings in this structure.
1333 %
1334 */
1335 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
1336  const char *filename,const char *map_id,ExceptionInfo *exception)
1337 {
1338  const char
1339  *attribute,
1340  *content;
1341 
1342  double
1343  value;
1344 
1345  ThresholdMap
1346  *map;
1347 
1348  XMLTreeInfo
1349  *description,
1350  *levels,
1351  *threshold,
1352  *thresholds;
1353 
1354  map = (ThresholdMap *) NULL;
1355  (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1356  "Loading threshold map file \"%s\" ...",filename);
1357  thresholds=NewXMLTree(xml,exception);
1358  if ( thresholds == (XMLTreeInfo *) NULL )
1359  return(map);
1360  for (threshold = GetXMLTreeChild(thresholds,"threshold");
1361  threshold != (XMLTreeInfo *) NULL;
1362  threshold = GetNextXMLTreeTag(threshold) )
1363  {
1364  attribute=GetXMLTreeAttribute(threshold, "map");
1365  if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1366  break;
1367  attribute=GetXMLTreeAttribute(threshold, "alias");
1368  if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
1369  break;
1370  }
1371  if (threshold == (XMLTreeInfo *) NULL)
1372  {
1373  thresholds=DestroyXMLTree(thresholds);
1374  return(map);
1375  }
1376  description=GetXMLTreeChild(threshold,"description");
1377  if (description == (XMLTreeInfo *) NULL)
1378  {
1379  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1380  "XmlMissingElement", "<description>, map \"%s\"", map_id);
1381  thresholds=DestroyXMLTree(thresholds);
1382  return(map);
1383  }
1384  levels=GetXMLTreeChild(threshold,"levels");
1385  if (levels == (XMLTreeInfo *) NULL)
1386  {
1387  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1388  "XmlMissingElement", "<levels>, map \"%s\"", map_id);
1389  thresholds=DestroyXMLTree(thresholds);
1390  return(map);
1391  }
1392  /*
1393  The map has been found -- allocate a Threshold Map to return
1394  */
1395  map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
1396  if (map == (ThresholdMap *) NULL)
1397  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1398  map->map_id=(char *) NULL;
1399  map->description=(char *) NULL;
1400  map->levels=(ssize_t *) NULL;
1401  /*
1402  Assign basic attributeibutes.
1403  */
1404  attribute=GetXMLTreeAttribute(threshold,"map");
1405  if (attribute != (char *) NULL)
1406  map->map_id=ConstantString(attribute);
1407  content=GetXMLTreeContent(description);
1408  if (content != (char *) NULL)
1409  map->description=ConstantString(content);
1410  attribute=GetXMLTreeAttribute(levels,"width");
1411  if (attribute == (char *) NULL)
1412  {
1413  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1414  "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1415  thresholds=DestroyXMLTree(thresholds);
1416  map=DestroyThresholdMap(map);
1417  return(map);
1418  }
1419  map->width=StringToUnsignedLong(attribute);
1420  if (map->width == 0)
1421  {
1422  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1423  "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
1424  thresholds=DestroyXMLTree(thresholds);
1425  map=DestroyThresholdMap(map);
1426  return(map);
1427  }
1428  attribute=GetXMLTreeAttribute(levels,"height");
1429  if (attribute == (char *) NULL)
1430  {
1431  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1432  "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
1433  thresholds=DestroyXMLTree(thresholds);
1434  map=DestroyThresholdMap(map);
1435  return(map);
1436  }
1437  map->height=StringToUnsignedLong(attribute);
1438  if (map->height == 0)
1439  {
1440  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1441  "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
1442  thresholds=DestroyXMLTree(thresholds);
1443  map=DestroyThresholdMap(map);
1444  return(map);
1445  }
1446  attribute=GetXMLTreeAttribute(levels, "divisor");
1447  if (attribute == (char *) NULL)
1448  {
1449  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1450  "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
1451  thresholds=DestroyXMLTree(thresholds);
1452  map=DestroyThresholdMap(map);
1453  return(map);
1454  }
1455  map->divisor=(ssize_t) StringToLong(attribute);
1456  if (map->divisor < 2)
1457  {
1458  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1459  "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
1460  thresholds=DestroyXMLTree(thresholds);
1461  map=DestroyThresholdMap(map);
1462  return(map);
1463  }
1464  /*
1465  Allocate theshold levels array.
1466  */
1467  content=GetXMLTreeContent(levels);
1468  if (content == (char *) NULL)
1469  {
1470  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1471  "XmlMissingContent", "<levels>, map \"%s\"", map_id);
1472  thresholds=DestroyXMLTree(thresholds);
1473  map=DestroyThresholdMap(map);
1474  return(map);
1475  }
1476  map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1477  sizeof(*map->levels));
1478  if (map->levels == (ssize_t *) NULL)
1479  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1480  {
1481  char
1482  *p;
1483 
1484  ssize_t
1485  i;
1486 
1487  /*
1488  Parse levels into integer array.
1489  */
1490  for (i=0; i< (ssize_t) (map->width*map->height); i++)
1491  {
1492  map->levels[i]=(ssize_t) strtol(content,&p,10);
1493  if (p == content)
1494  {
1495  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1496  "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1497  thresholds=DestroyXMLTree(thresholds);
1498  map=DestroyThresholdMap(map);
1499  return(map);
1500  }
1501  if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1502  {
1503  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1504  "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1505  (double) map->levels[i],map_id);
1506  thresholds=DestroyXMLTree(thresholds);
1507  map=DestroyThresholdMap(map);
1508  return(map);
1509  }
1510  content=p;
1511  }
1512  value=(double) strtol(content,&p,10);
1513  (void) value;
1514  if (p != content)
1515  {
1516  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1517  "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
1518  thresholds=DestroyXMLTree(thresholds);
1519  map=DestroyThresholdMap(map);
1520  return(map);
1521  }
1522  }
1523  thresholds=DestroyXMLTree(thresholds);
1524  return(map);
1525 }
1526 
1527 /*
1528 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1529 % %
1530 % %
1531 % %
1532 % G e t T h r e s h o l d M a p %
1533 % %
1534 % %
1535 % %
1536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1537 %
1538 % GetThresholdMap() load and search one or more threshold map files for the
1539 % a map matching the given name or aliase.
1540 %
1541 % The format of the GetThresholdMap method is:
1542 %
1543 % ThresholdMap *GetThresholdMap(const char *map_id,
1544 % ExceptionInfo *exception)
1545 %
1546 % A description of each parameter follows.
1547 %
1548 % o map_id: ID of the map to look for.
1549 %
1550 % o exception: return any errors or warnings in this structure.
1551 %
1552 */
1553 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1554  ExceptionInfo *exception)
1555 {
1556  const StringInfo
1557  *option;
1558 
1560  *options;
1561 
1562  ThresholdMap
1563  *map;
1564 
1565  map=GetThresholdMapFile(BuiltinMap,"built-in",map_id,exception);
1566  if (map != (ThresholdMap *) NULL)
1567  return(map);
1568  options=GetConfigureOptions(ThresholdsFilename,exception);
1569  option=(const StringInfo *) GetNextValueInLinkedList(options);
1570  while (option != (const StringInfo *) NULL)
1571  {
1572  map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1573  GetStringInfoPath(option),map_id,exception);
1574  if (map != (ThresholdMap *) NULL)
1575  break;
1576  option=(const StringInfo *) GetNextValueInLinkedList(options);
1577  }
1578  options=DestroyConfigureOptions(options);
1579  return(map);
1580 }
1581 
1582 /*
1583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584 % %
1585 % %
1586 % %
1587 + L i s t T h r e s h o l d M a p F i l e %
1588 % %
1589 % %
1590 % %
1591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1592 %
1593 % ListThresholdMapFile() lists the threshold maps and their descriptions
1594 % in the given XML file data.
1595 %
1596 % The format of the ListThresholdMaps method is:
1597 %
1598 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1599 % const char *filename,ExceptionInfo *exception)
1600 %
1601 % A description of each parameter follows.
1602 %
1603 % o file: An pointer to the output FILE.
1604 %
1605 % o xml: The threshold map list in XML format.
1606 %
1607 % o filename: The threshold map XML filename.
1608 %
1609 % o exception: return any errors or warnings in this structure.
1610 %
1611 */
1612 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1613  const char *filename,ExceptionInfo *exception)
1614 {
1615  XMLTreeInfo *thresholds,*threshold,*description;
1616  const char *map,*alias,*content;
1617 
1618  assert( xml != (char *) NULL );
1619  assert( file != (FILE *) NULL );
1620 
1621  (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1622  "Loading threshold map file \"%s\" ...",filename);
1623  thresholds=NewXMLTree(xml,exception);
1624  if ( thresholds == (XMLTreeInfo *) NULL )
1625  return(MagickFalse);
1626 
1627  (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1628  (void) FormatLocaleFile(file,
1629  "----------------------------------------------------\n");
1630 
1631  for( threshold = GetXMLTreeChild(thresholds,"threshold");
1632  threshold != (XMLTreeInfo *) NULL;
1633  threshold = GetNextXMLTreeTag(threshold) )
1634  {
1635  map = GetXMLTreeAttribute(threshold, "map");
1636  if (map == (char *) NULL) {
1637  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1638  "XmlMissingAttribute", "<map>");
1639  thresholds=DestroyXMLTree(thresholds);
1640  return(MagickFalse);
1641  }
1642  alias = GetXMLTreeAttribute(threshold, "alias");
1643  /* alias is optional, no if test needed */
1644  description=GetXMLTreeChild(threshold,"description");
1645  if ( description == (XMLTreeInfo *) NULL ) {
1646  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1647  "XmlMissingElement", "<description>, map \"%s\"", map);
1648  thresholds=DestroyXMLTree(thresholds);
1649  return(MagickFalse);
1650  }
1651  content=GetXMLTreeContent(description);
1652  if ( content == (char *) NULL ) {
1653  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1654  "XmlMissingContent", "<description>, map \"%s\"", map);
1655  thresholds=DestroyXMLTree(thresholds);
1656  return(MagickFalse);
1657  }
1658  (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1659  content);
1660  }
1661  thresholds=DestroyXMLTree(thresholds);
1662  return(MagickTrue);
1663 }
1664 
1665 /*
1666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1667 % %
1668 % %
1669 % %
1670 % L i s t T h r e s h o l d M a p s %
1671 % %
1672 % %
1673 % %
1674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1675 %
1676 % ListThresholdMaps() lists the threshold maps and their descriptions
1677 % as defined by "threshold.xml" to a file.
1678 %
1679 % The format of the ListThresholdMaps method is:
1680 %
1681 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1682 %
1683 % A description of each parameter follows.
1684 %
1685 % o file: An pointer to the output FILE.
1686 %
1687 % o exception: return any errors or warnings in this structure.
1688 %
1689 */
1690 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1691  ExceptionInfo *exception)
1692 {
1693  const StringInfo
1694  *option;
1695 
1697  *options;
1698 
1699  MagickStatusType
1700  status;
1701 
1702  status=MagickTrue;
1703  if (file == (FILE *) NULL)
1704  file=stdout;
1705  options=GetConfigureOptions(ThresholdsFilename,exception);
1706  (void) FormatLocaleFile(file,
1707  "\n Threshold Maps for Ordered Dither Operations\n");
1708  option=(const StringInfo *) GetNextValueInLinkedList(options);
1709  while (option != (const StringInfo *) NULL)
1710  {
1711  (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1712  status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1713  GetStringInfoPath(option),exception);
1714  option=(const StringInfo *) GetNextValueInLinkedList(options);
1715  }
1716  options=DestroyConfigureOptions(options);
1717  return(status != 0 ? MagickTrue : MagickFalse);
1718 }
1719 
1720 /*
1721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1722 % %
1723 % %
1724 % %
1725 % O r d e r e d D i t h e r I m a g e %
1726 % %
1727 % %
1728 % %
1729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1730 %
1731 % OrderedDitherImage() uses the ordered dithering technique of reducing color
1732 % images to monochrome using positional information to retain as much
1733 % information as possible.
1734 %
1735 % WARNING: This function is deprecated, and is now just a call to
1736 % the more more powerful OrderedPosterizeImage(); function.
1737 %
1738 % The format of the OrderedDitherImage method is:
1739 %
1740 % MagickBooleanType OrderedDitherImage(Image *image)
1741 % MagickBooleanType OrderedDitherImageChannel(Image *image,
1742 % const ChannelType channel,ExceptionInfo *exception)
1743 %
1744 % A description of each parameter follows:
1745 %
1746 % o image: the image.
1747 %
1748 % o channel: the channel or channels to be thresholded.
1749 %
1750 % o exception: return any errors or warnings in this structure.
1751 %
1752 */
1753 
1754 MagickExport MagickBooleanType OrderedDitherImage(Image *image)
1755 {
1756  MagickBooleanType
1757  status;
1758 
1759  status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
1760  return(status);
1761 }
1762 
1763 MagickExport MagickBooleanType OrderedDitherImageChannel(Image *image,
1764  const ChannelType channel,ExceptionInfo *exception)
1765 {
1766  MagickBooleanType
1767  status;
1768 
1769  /*
1770  Call the augumented function OrderedPosterizeImage()
1771  */
1772  status=OrderedPosterizeImageChannel(image,channel,"o8x8",exception);
1773  return(status);
1774 }
1775 
1776 /*
1777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1778 % %
1779 % %
1780 % %
1781 % O r d e r e d P o s t e r i z e I m a g e %
1782 % %
1783 % %
1784 % %
1785 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1786 %
1787 % OrderedPosterizeImage() will perform a ordered dither based on a number
1788 % of pre-defined dithering threshold maps, but over multiple intensity
1789 % levels, which can be different for different channels, according to the
1790 % input argument.
1791 %
1792 % The format of the OrderedPosterizeImage method is:
1793 %
1794 % MagickBooleanType OrderedPosterizeImage(Image *image,
1795 % const char *threshold_map,ExceptionInfo *exception)
1796 % MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1797 % const ChannelType channel,const char *threshold_map,
1798 % ExceptionInfo *exception)
1799 %
1800 % A description of each parameter follows:
1801 %
1802 % o image: the image.
1803 %
1804 % o channel: the channel or channels to be thresholded.
1805 %
1806 % o threshold_map: A string containing the name of the threshold dither
1807 % map to use, followed by zero or more numbers representing the number
1808 % of color levels tho dither between.
1809 %
1810 % Any level number less than 2 will be equivalent to 2, and means only
1811 % binary dithering will be applied to each color channel.
1812 %
1813 % No numbers also means a 2 level (bitmap) dither will be applied to all
1814 % channels, while a single number is the number of levels applied to each
1815 % channel in sequence. More numbers will be applied in turn to each of
1816 % the color channels.
1817 %
1818 % For example: "o3x3,6" will generate a 6 level posterization of the
1819 % image with a ordered 3x3 diffused pixel dither being applied between
1820 % each level. While checker,8,8,4 will produce a 332 colormaped image
1821 % with only a single checkerboard hash pattern (50% grey) between each
1822 % color level, to basically double the number of color levels with
1823 % a bare minimim of dithering.
1824 %
1825 % o exception: return any errors or warnings in this structure.
1826 %
1827 */
1828 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1829  const char *threshold_map,ExceptionInfo *exception)
1830 {
1831  MagickBooleanType
1832  status;
1833 
1834  status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1835  exception);
1836  return(status);
1837 }
1838 
1839 MagickExport MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1840  const ChannelType channel,const char *threshold_map,ExceptionInfo *exception)
1841 {
1842 #define DitherImageTag "Dither/Image"
1843 
1844  CacheView
1845  *image_view;
1846 
1848  levels;
1849 
1850  MagickBooleanType
1851  status;
1852 
1853  MagickOffsetType
1854  progress;
1855 
1856  ssize_t
1857  y;
1858 
1859  ThresholdMap
1860  *map;
1861 
1862  assert(image != (Image *) NULL);
1863  assert(image->signature == MagickCoreSignature);
1864  assert(exception != (ExceptionInfo *) NULL);
1865  assert(exception->signature == MagickCoreSignature);
1866  if (IsEventLogging() != MagickFalse)
1867  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1868  if (threshold_map == (const char *) NULL)
1869  return(MagickTrue);
1870  {
1871  char
1872  token[MaxTextExtent];
1873 
1874  const char
1875  *p;
1876 
1877  p=(char *)threshold_map;
1878  while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1879  (*p != '\0'))
1880  p++;
1881  threshold_map=p;
1882  while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1883  (*p != '\0')) {
1884  if ((p-threshold_map) >= (MaxTextExtent-1))
1885  break;
1886  token[p-threshold_map] = *p;
1887  p++;
1888  }
1889  token[p-threshold_map] = '\0';
1890  map = GetThresholdMap(token, exception);
1891  if ( map == (ThresholdMap *) NULL ) {
1892  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1893  "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1894  return(MagickFalse);
1895  }
1896  }
1897  /* Set channel levels from extra comma separated arguments
1898  Default to 2, the single value given, or individual channel values
1899  */
1900 #if 1
1901  { /* parse directly as a comma separated list of integers */
1902  char *p;
1903 
1904  p = strchr((char *) threshold_map,',');
1905  if ( p != (char *) NULL && isdigit((int) ((unsigned char) *(++p))) )
1906  levels.index = (unsigned int) strtoul(p, &p, 10);
1907  else
1908  levels.index = 2;
1909 
1910  levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
1911  levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
1912  levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
1913  levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
1914  levels.index = ((channel & IndexChannel) != 0
1915  && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
1916 
1917  /* if more than a single number, each channel has a separate value */
1918  if ( p != (char *) NULL && *p == ',' ) {
1919  p=strchr((char *) threshold_map,',');
1920  p++;
1921  if ((channel & RedChannel) != 0)
1922  levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1923  if ((channel & GreenChannel) != 0)
1924  levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1925  if ((channel & BlueChannel) != 0)
1926  levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1927  if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
1928  levels.index=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1929  if ((channel & OpacityChannel) != 0)
1930  levels.opacity = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1931  }
1932  }
1933 #else
1934  /* Parse level values as a geometry */
1935  /* This difficult!
1936  * How to map GeometryInfo structure elements into
1937  * LongPixelPacket structure elements, but according to channel?
1938  * Note the channels list may skip elements!!!!
1939  * EG -channel BA -ordered-dither map,2,3
1940  * will need to map g.rho -> l.blue, and g.sigma -> l.opacity
1941  * A simpler way is needed, probably converting geometry to a temporary
1942  * array, then using channel to advance the index into ssize_t pixel packet.
1943  */
1944 #endif
1945 
1946 #if 0
1947 printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1948  levels.red, levels.green, levels.blue, levels.opacity, levels.index);
1949 #endif
1950 
1951  { /* Do the posterized ordered dithering of the image */
1952  ssize_t
1953  d;
1954 
1955  /* d = number of psuedo-level divisions added between color levels */
1956  d = map->divisor-1;
1957 
1958  /* reduce levels to levels - 1 */
1959  levels.red = levels.red ? levels.red-1 : 0;
1960  levels.green = levels.green ? levels.green-1 : 0;
1961  levels.blue = levels.blue ? levels.blue-1 : 0;
1962  levels.opacity = levels.opacity ? levels.opacity-1 : 0;
1963  levels.index = levels.index ? levels.index-1 : 0;
1964 
1965  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1966  {
1967  InheritException(exception,&image->exception);
1968  return(MagickFalse);
1969  }
1970  status=MagickTrue;
1971  progress=0;
1972  image_view=AcquireAuthenticCacheView(image,exception);
1973 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1974  #pragma omp parallel for schedule(static) shared(progress,status) \
1975  magick_number_threads(image,image,image->rows,1)
1976 #endif
1977  for (y=0; y < (ssize_t) image->rows; y++)
1978  {
1979  IndexPacket
1980  *magick_restrict indexes;
1981 
1982  ssize_t
1983  x;
1984 
1985  PixelPacket
1986  *magick_restrict q;
1987 
1988  if (status == MagickFalse)
1989  continue;
1990  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1991  if (q == (PixelPacket *) NULL)
1992  {
1993  status=MagickFalse;
1994  continue;
1995  }
1996  indexes=GetCacheViewAuthenticIndexQueue(image_view);
1997  for (x=0; x < (ssize_t) image->columns; x++)
1998  {
1999  ssize_t
2000  threshold,
2001  t,
2002  l;
2003 
2004  /*
2005  Figure out the dither threshold for this pixel
2006  This must be a integer from 1 to map->divisor-1
2007  */
2008  threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
2009 
2010  /* Dither each channel in the image as appropriate
2011  Notes on the integer Math...
2012  total number of divisions = (levels-1)*(divisor-1)+1)
2013  t1 = this colors psuedo_level =
2014  q->red * total_divisions / (QuantumRange+1)
2015  l = posterization level 0..levels
2016  t = dither threshold level 0..divisor-1 NB: 0 only on last
2017  Each color_level is of size QuantumRange / (levels-1)
2018  NB: All input levels and divisor are already had 1 subtracted
2019  Opacity is inverted so 'off' represents transparent.
2020  */
2021  if (levels.red) {
2022  t = (ssize_t) (QuantumScale*GetPixelRed(q)*(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*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*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*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*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*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*=(MagickRealType) (0.01*QuantumRange);
2349  min_threshold*=(MagickRealType) (0.01*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  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) (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) (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) (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) (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) (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,1)
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:152