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