]> granicus.if.org Git - imagemagick/blob - MagickCore/threshold.c
(no commit message)
[imagemagick] / MagickCore / threshold.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %       TTTTT  H   H  RRRR   EEEEE  SSSSS  H   H   OOO   L      DDDD          %
7 %         T    H   H  R   R  E      SS     H   H  O   O  L      D   D         %
8 %         T    HHHHH  RRRR   EEE     SSS   HHHHH  O   O  L      D   D         %
9 %         T    H   H  R R    E         SS  H   H  O   O  L      D   D         %
10 %         T    H   H  R  R   EEEEE  SSSSS  H   H   OOO   LLLLL  DDDD          %
11 %                                                                             %
12 %                                                                             %
13 %                      MagickCore Image Threshold Methods                     %
14 %                                                                             %
15 %                               Software Design                               %
16 %                                 John Cristy                                 %
17 %                                 October 1996                                %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2012 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 %    http://www.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 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/property.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/geometry.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/monitor.h"
68 #include "MagickCore/monitor-private.h"
69 #include "MagickCore/montage.h"
70 #include "MagickCore/option.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/quantize.h"
73 #include "MagickCore/quantum.h"
74 #include "MagickCore/random_.h"
75 #include "MagickCore/random-private.h"
76 #include "MagickCore/resize.h"
77 #include "MagickCore/resource_.h"
78 #include "MagickCore/segment.h"
79 #include "MagickCore/shear.h"
80 #include "MagickCore/signature-private.h"
81 #include "MagickCore/string_.h"
82 #include "MagickCore/string-private.h"
83 #include "MagickCore/thread-private.h"
84 #include "MagickCore/threshold.h"
85 #include "MagickCore/token.h"
86 #include "MagickCore/transform.h"
87 #include "MagickCore/xml-tree.h"
88 #include "MagickCore/xml-tree-private.h"
89 \f
90 /*
91   Define declarations.
92 */
93 #define ThresholdsFilename  "thresholds.xml"
94 \f
95 /*
96   Typedef declarations.
97 */
98 struct _ThresholdMap
99 {
100   char
101     *map_id,
102     *description;
103
104   size_t
105     width,
106     height;
107
108   ssize_t
109     divisor,
110     *levels;
111 };
112 \f
113 /*
114   Forward declarations.
115 */
116 static ThresholdMap
117   *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
118 \f
119 /*
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 %                                                                             %
122 %                                                                             %
123 %                                                                             %
124 %     A d a p t i v e T h r e s h o l d I m a g e                             %
125 %                                                                             %
126 %                                                                             %
127 %                                                                             %
128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129 %
130 %  AdaptiveThresholdImage() selects an individual threshold for each pixel
131 %  based on the range of intensity values in its local neighborhood.  This
132 %  allows for thresholding of an image whose global intensity histogram
133 %  doesn't contain distinctive peaks.
134 %
135 %  The format of the AdaptiveThresholdImage method is:
136 %
137 %      Image *AdaptiveThresholdImage(const Image *image,const size_t width,
138 %        const size_t height,const double bias,ExceptionInfo *exception)
139 %
140 %  A description of each parameter follows:
141 %
142 %    o image: the image.
143 %
144 %    o width: the width of the local neighborhood.
145 %
146 %    o height: the height of the local neighborhood.
147 %
148 %    o bias: the mean bias.
149 %
150 %    o exception: return any errors or warnings in this structure.
151 %
152 */
153 MagickExport Image *AdaptiveThresholdImage(const Image *image,
154   const size_t width,const size_t height,const double bias,
155   ExceptionInfo *exception)
156 {
157 #define AdaptiveThresholdImageTag  "AdaptiveThreshold/Image"
158
159   CacheView
160     *image_view,
161     *threshold_view;
162
163   Image
164     *threshold_image;
165
166   MagickBooleanType
167     status;
168
169   MagickOffsetType
170     progress;
171
172   MagickSizeType
173     number_pixels;
174
175   ssize_t
176     y;
177
178   /*
179     Initialize threshold image attributes.
180   */
181   assert(image != (Image *) NULL);
182   assert(image->signature == MagickSignature);
183   if (image->debug != MagickFalse)
184     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185   assert(exception != (ExceptionInfo *) NULL);
186   assert(exception->signature == MagickSignature);
187   if ((width % 2) == 0)
188     ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
189   threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
190     exception);
191   if (threshold_image == (Image *) NULL)
192     return((Image *) NULL);
193   status=SetImageStorageClass(threshold_image,DirectClass,exception);
194   if (status == MagickFalse)
195     {
196       threshold_image=DestroyImage(threshold_image);
197       return((Image *) NULL);
198     }
199   /*
200     Threshold image.
201   */
202   status=MagickTrue;
203   progress=0;
204   number_pixels=(MagickSizeType) width*height;
205   image_view=AcquireVirtualCacheView(image,exception);
206   threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
207 #if defined(MAGICKCORE_OPENMP_SUPPORT)
208   #pragma omp parallel for schedule(static,4) shared(progress,status) \
209     dynamic_number_threads(image,image->columns,image->rows,1)
210 #endif
211   for (y=0; y < (ssize_t) image->rows; y++)
212   {
213     register const Quantum
214       *restrict p;
215
216     register Quantum
217       *restrict q;
218
219     register ssize_t
220       x;
221
222     ssize_t
223       center;
224
225     if (status == MagickFalse)
226       continue;
227     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
228       (height/2L),image->columns+width,height,exception);
229     q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
230       1,exception);
231     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
232       {
233         status=MagickFalse;
234         continue;
235       }
236     center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
237       GetPixelChannels(image)*(width/2);
238     for (x=0; x < (ssize_t) image->columns; x++)
239     {
240       register ssize_t
241         i;
242
243       if (GetPixelMask(image,p) != 0)
244         {
245           p+=GetPixelChannels(image);
246           q+=GetPixelChannels(threshold_image);
247           continue;
248         }
249       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
250       {
251         MagickRealType
252           mean,
253           pixel;
254
255         PixelChannel
256           channel;
257
258         PixelTrait
259           threshold_traits,
260           traits;
261
262         register const Quantum
263           *restrict pixels;
264
265         register ssize_t
266           u;
267
268         ssize_t
269           v;
270
271         channel=GetPixelChannelMapChannel(image,i);
272         traits=GetPixelChannelMapTraits(image,channel);
273         threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
274         if ((traits == UndefinedPixelTrait) ||
275             (threshold_traits == UndefinedPixelTrait))
276           continue;
277         if ((threshold_traits & CopyPixelTrait) != 0)
278           {
279             SetPixelChannel(threshold_image,channel,p[center+i],q);
280             continue;
281           }
282         pixels=p;
283         pixel=0.0;
284         for (v=0; v < (ssize_t) height; v++)
285         {
286           for (u=0; u < (ssize_t) width; u++)
287           {
288             pixel+=pixels[i];
289             pixels+=GetPixelChannels(image);
290           }
291           pixels+=image->columns*GetPixelChannels(image);
292         }
293         mean=(MagickRealType) (pixel/number_pixels+bias);
294         SetPixelChannel(threshold_image,channel,(Quantum) ((MagickRealType)
295           p[center+i] <= mean ? 0 : QuantumRange),q);
296       }
297       p+=GetPixelChannels(image);
298       q+=GetPixelChannels(threshold_image);
299     }
300     if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
301       status=MagickFalse;
302     if (image->progress_monitor != (MagickProgressMonitor) NULL)
303       {
304         MagickBooleanType
305           proceed;
306
307 #if defined(MAGICKCORE_OPENMP_SUPPORT)
308         #pragma omp critical (MagickCore_AdaptiveThresholdImage)
309 #endif
310         proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
311           image->rows);
312         if (proceed == MagickFalse)
313           status=MagickFalse;
314       }
315   }
316   threshold_image->type=image->type;
317   threshold_view=DestroyCacheView(threshold_view);
318   image_view=DestroyCacheView(image_view);
319   if (status == MagickFalse)
320     threshold_image=DestroyImage(threshold_image);
321   return(threshold_image);
322 }
323 \f
324 /*
325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326 %                                                                             %
327 %                                                                             %
328 %                                                                             %
329 %     B i l e v e l I m a g e                                                 %
330 %                                                                             %
331 %                                                                             %
332 %                                                                             %
333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334 %
335 %  BilevelImage() changes the value of individual pixels based on the
336 %  intensity of each pixel channel.  The result is a high-contrast image.
337 %
338 %  More precisely each channel value of the image is 'thresholded' so that if
339 %  it is equal to or less than the given value it is set to zero, while any
340 %  value greater than that give is set to it maximum or QuantumRange.
341 %
342 %  This function is what is used to implement the "-threshold" operator for
343 %  the command line API.
344 %
345 %  If the default channel setting is given the image is thresholded using just
346 %  the gray 'intensity' of the image, rather than the individual channels.
347 %
348 %  The format of the BilevelImage method is:
349 %
350 %      MagickBooleanType BilevelImage(Image *image,const double threshold,
351 %        ExceptionInfo *exception)
352 %
353 %  A description of each parameter follows:
354 %
355 %    o image: the image.
356 %
357 %    o threshold: define the threshold values.
358 %
359 %    o exception: return any errors or warnings in this structure.
360 %
361 %  Aside: You can get the same results as operator using LevelImages()
362 %  with the 'threshold' value for both the black_point and the white_point.
363 %
364 */
365 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
366   ExceptionInfo *exception)
367 {
368 #define ThresholdImageTag  "Threshold/Image"
369
370   CacheView
371     *image_view;
372
373   MagickBooleanType
374     status;
375
376   MagickOffsetType
377     progress;
378
379   ssize_t
380     y;
381
382   assert(image != (Image *) NULL);
383   assert(image->signature == MagickSignature);
384   if (image->debug != MagickFalse)
385     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
386   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
387     return(MagickFalse);
388   /*
389     Bilevel threshold image.
390   */
391   status=MagickTrue;
392   progress=0;
393   image_view=AcquireAuthenticCacheView(image,exception);
394 #if defined(MAGICKCORE_OPENMP_SUPPORT)
395   #pragma omp parallel for schedule(static,8) shared(progress,status) \
396     dynamic_number_threads(image,image->columns,image->rows,1)
397 #endif
398   for (y=0; y < (ssize_t) image->rows; y++)
399   {
400     register ssize_t
401       x;
402
403     register Quantum
404       *restrict q;
405
406     if (status == MagickFalse)
407       continue;
408     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
409     if (q == (Quantum *) NULL)
410       {
411         status=MagickFalse;
412         continue;
413       }
414     for (x=0; x < (ssize_t) image->columns; x++)
415     {
416       double
417         pixel;
418
419       register ssize_t
420         i;
421
422       if (GetPixelMask(image,q) != 0)
423         {
424           q+=GetPixelChannels(image);
425           continue;
426         }
427       pixel=(double) GetPixelIntensity(image,q);
428       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
429       {
430         PixelChannel
431           channel;
432
433         PixelTrait
434           traits;
435
436         channel=GetPixelChannelMapChannel(image,i);
437         traits=GetPixelChannelMapTraits(image,channel);
438         if ((traits & UpdatePixelTrait) == 0)
439           continue;
440         if (image->channel_mask != DefaultChannels)
441           pixel=q[i];
442         q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
443       }
444       q+=GetPixelChannels(image);
445     }
446     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
447       status=MagickFalse;
448     if (image->progress_monitor != (MagickProgressMonitor) NULL)
449       {
450         MagickBooleanType
451           proceed;
452
453 #if defined(MAGICKCORE_OPENMP_SUPPORT)
454         #pragma omp critical (MagickCore_BilevelImage)
455 #endif
456         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
457           image->rows);
458         if (proceed == MagickFalse)
459           status=MagickFalse;
460       }
461   }
462   image_view=DestroyCacheView(image_view);
463   return(status);
464 }
465 \f
466 /*
467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
468 %                                                                             %
469 %                                                                             %
470 %                                                                             %
471 %     B l a c k T h r e s h o l d I m a g e                                   %
472 %                                                                             %
473 %                                                                             %
474 %                                                                             %
475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
476 %
477 %  BlackThresholdImage() is like ThresholdImage() but forces all pixels below
478 %  the threshold into black while leaving all pixels at or above the threshold
479 %  unchanged.
480 %
481 %  The format of the BlackThresholdImage method is:
482 %
483 %      MagickBooleanType BlackThresholdImage(Image *image,
484 %        const char *threshold,ExceptionInfo *exception)
485 %
486 %  A description of each parameter follows:
487 %
488 %    o image: the image.
489 %
490 %    o threshold: define the threshold value.
491 %
492 %    o exception: return any errors or warnings in this structure.
493 %
494 */
495 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
496   const char *thresholds,ExceptionInfo *exception)
497 {
498 #define ThresholdImageTag  "Threshold/Image"
499
500   CacheView
501     *image_view;
502
503   GeometryInfo
504     geometry_info;
505
506   MagickBooleanType
507     status;
508
509   MagickOffsetType
510     progress;
511
512   PixelInfo
513     threshold;
514
515   MagickStatusType
516     flags;
517
518   ssize_t
519     y;
520
521   assert(image != (Image *) NULL);
522   assert(image->signature == MagickSignature);
523   if (image->debug != MagickFalse)
524     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
525   if (thresholds == (const char *) NULL)
526     return(MagickTrue);
527   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
528     return(MagickFalse);
529   if (IsGrayColorspace(image->colorspace) != MagickFalse)
530     (void) TransformImageColorspace(image,RGBColorspace,exception);
531   GetPixelInfo(image,&threshold);
532   flags=ParseGeometry(thresholds,&geometry_info);
533   threshold.red=geometry_info.rho;
534   threshold.green=geometry_info.rho;
535   threshold.blue=geometry_info.rho;
536   threshold.black=geometry_info.rho;
537   threshold.alpha=100.0;
538   if ((flags & SigmaValue) != 0)
539     threshold.green=geometry_info.sigma;
540   if ((flags & XiValue) != 0)
541     threshold.blue=geometry_info.xi;
542   if ((flags & PsiValue) != 0)
543     threshold.alpha=geometry_info.psi;
544   if (threshold.colorspace == CMYKColorspace)
545     {
546       if ((flags & PsiValue) != 0)
547         threshold.black=geometry_info.psi;
548       if ((flags & ChiValue) != 0)
549         threshold.alpha=geometry_info.chi;
550     }
551   if ((flags & PercentValue) != 0)
552     {
553       threshold.red*=(QuantumRange/100.0);
554       threshold.green*=(QuantumRange/100.0);
555       threshold.blue*=(QuantumRange/100.0);
556       threshold.black*=(QuantumRange/100.0);
557       threshold.alpha*=(QuantumRange/100.0);
558     }
559   /*
560     White threshold image.
561   */
562   status=MagickTrue;
563   progress=0;
564   image_view=AcquireAuthenticCacheView(image,exception);
565 #if defined(MAGICKCORE_OPENMP_SUPPORT)
566   #pragma omp parallel for schedule(static,8) shared(progress,status) \
567     dynamic_number_threads(image,image->columns,image->rows,1)
568 #endif
569   for (y=0; y < (ssize_t) image->rows; y++)
570   {
571     register ssize_t
572       x;
573
574     register Quantum
575       *restrict q;
576
577     if (status == MagickFalse)
578       continue;
579     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
580     if (q == (Quantum *) NULL)
581       {
582         status=MagickFalse;
583         continue;
584       }
585     for (x=0; x < (ssize_t) image->columns; x++)
586     {
587       register ssize_t
588         i;
589
590       if (GetPixelMask(image,q) != 0)
591         {
592           q+=GetPixelChannels(image);
593           continue;
594         }
595       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
596       {
597         PixelChannel
598           channel;
599
600         PixelTrait
601           traits;
602
603         channel=GetPixelChannelMapChannel(image,i);
604         traits=GetPixelChannelMapTraits(image,channel);
605         if ((traits & UpdatePixelTrait) == 0)
606           continue;
607         if ((double) q[i] <= GetPixelInfoChannel(&threshold,channel))
608           q[i]=0;
609       }
610       q+=GetPixelChannels(image);
611     }
612     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
613       status=MagickFalse;
614     if (image->progress_monitor != (MagickProgressMonitor) NULL)
615       {
616         MagickBooleanType
617           proceed;
618
619 #if defined(MAGICKCORE_OPENMP_SUPPORT)
620         #pragma omp critical (MagickCore_BlackThresholdImage)
621 #endif
622         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
623           image->rows);
624         if (proceed == MagickFalse)
625           status=MagickFalse;
626       }
627   }
628   image_view=DestroyCacheView(image_view);
629   return(status);
630 }
631 \f
632 /*
633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
634 %                                                                             %
635 %                                                                             %
636 %                                                                             %
637 %     C l a m p I m a g e                                                     %
638 %                                                                             %
639 %                                                                             %
640 %                                                                             %
641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
642 %
643 %  ClampImage() restricts the color range from 0 to the quantum depth.
644 %
645 %  The format of the ClampImage method is:
646 %
647 %      MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
648 %
649 %  A description of each parameter follows:
650 %
651 %    o image: the image.
652 %
653 %    o exception: return any errors or warnings in this structure.
654 %
655 */
656
657 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
658 {
659 #if defined(MAGICKCORE_HDRI_SUPPORT)
660   if (quantum <= 0)
661     return(0);
662   if (quantum >= QuantumRange)
663     return(QuantumRange);
664   return(quantum);
665 #else
666   return(quantum);
667 #endif
668 }
669
670 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
671 {
672 #define ClampImageTag  "Clamp/Image"
673
674   CacheView
675     *image_view;
676
677   MagickBooleanType
678     status;
679
680   MagickOffsetType
681     progress;
682
683   ssize_t
684     y;
685
686   assert(image != (Image *) NULL);
687   assert(image->signature == MagickSignature);
688   if (image->debug != MagickFalse)
689     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
690   if (image->storage_class == PseudoClass)
691     {
692       register ssize_t
693         i;
694
695       register PixelInfo
696         *restrict q;
697
698       q=image->colormap;
699       for (i=0; i < (ssize_t) image->colors; i++)
700       {
701         q->red=(double) ClampToUnsignedQuantum(ClampToQuantum(q->red));
702         q->green=(double) ClampToUnsignedQuantum(ClampToQuantum(q->green));
703         q->blue=(double) ClampToUnsignedQuantum(ClampToQuantum(q->blue));
704         q->alpha=(double) ClampToUnsignedQuantum(ClampToQuantum(q->alpha));
705         q++;
706       }
707       return(SyncImage(image,exception));
708     }
709   /*
710     Clamp image.
711   */
712   status=MagickTrue;
713   progress=0;
714   image_view=AcquireAuthenticCacheView(image,exception);
715 #if defined(MAGICKCORE_OPENMP_SUPPORT)
716   #pragma omp parallel for schedule(static,8) shared(progress,status) \
717     dynamic_number_threads(image,image->columns,image->rows,1)
718 #endif
719   for (y=0; y < (ssize_t) image->rows; y++)
720   {
721     register ssize_t
722       x;
723
724     register Quantum
725       *restrict q;
726
727     if (status == MagickFalse)
728       continue;
729     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
730     if (q == (Quantum *) NULL)
731       {
732         status=MagickFalse;
733         continue;
734       }
735     for (x=0; x < (ssize_t) image->columns; x++)
736     {
737       register ssize_t
738         i;
739
740       if (GetPixelMask(image,q) != 0)
741         {
742           q+=GetPixelChannels(image);
743           continue;
744         }
745       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
746       {
747         PixelChannel
748           channel;
749
750         PixelTrait
751           traits;
752
753         channel=GetPixelChannelMapChannel(image,i);
754         traits=GetPixelChannelMapTraits(image,channel);
755         if (traits == UndefinedPixelTrait)
756           continue;
757         q[i]=ClampToUnsignedQuantum(q[i]);
758       }
759       q+=GetPixelChannels(image);
760     }
761     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
762       status=MagickFalse;
763     if (image->progress_monitor != (MagickProgressMonitor) NULL)
764       {
765         MagickBooleanType
766           proceed;
767
768 #if defined(MAGICKCORE_OPENMP_SUPPORT)
769         #pragma omp critical (MagickCore_ClampImage)
770 #endif
771         proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
772         if (proceed == MagickFalse)
773           status=MagickFalse;
774       }
775   }
776   image_view=DestroyCacheView(image_view);
777   return(status);
778 }
779 \f
780 /*
781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
782 %                                                                             %
783 %                                                                             %
784 %                                                                             %
785 %  D e s t r o y T h r e s h o l d M a p                                      %
786 %                                                                             %
787 %                                                                             %
788 %                                                                             %
789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
790 %
791 %  DestroyThresholdMap() de-allocate the given ThresholdMap
792 %
793 %  The format of the ListThresholdMaps method is:
794 %
795 %      ThresholdMap *DestroyThresholdMap(Threshold *map)
796 %
797 %  A description of each parameter follows.
798 %
799 %    o map:    Pointer to the Threshold map to destroy
800 %
801 */
802 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
803 {
804   assert(map != (ThresholdMap *) NULL);
805   if (map->map_id != (char *) NULL)
806     map->map_id=DestroyString(map->map_id);
807   if (map->description != (char *) NULL)
808     map->description=DestroyString(map->description);
809   if (map->levels != (ssize_t *) NULL)
810     map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
811   map=(ThresholdMap *) RelinquishMagickMemory(map);
812   return(map);
813 }
814 \f
815 /*
816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817 %                                                                             %
818 %                                                                             %
819 %                                                                             %
820 %  G e t T h r e s h o l d M a p                                              %
821 %                                                                             %
822 %                                                                             %
823 %                                                                             %
824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
825 %
826 %  GetThresholdMap() loads and searches one or more threshold map files for the
827 %  map matching the given name or alias.
828 %
829 %  The format of the GetThresholdMap method is:
830 %
831 %      ThresholdMap *GetThresholdMap(const char *map_id,
832 %        ExceptionInfo *exception)
833 %
834 %  A description of each parameter follows.
835 %
836 %    o map_id:  ID of the map to look for.
837 %
838 %    o exception: return any errors or warnings in this structure.
839 %
840 */
841 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
842   ExceptionInfo *exception)
843 {
844   const StringInfo
845     *option;
846
847   LinkedListInfo
848     *options;
849
850   ThresholdMap
851     *map;
852
853   map=(ThresholdMap *)NULL;
854   options=GetConfigureOptions(ThresholdsFilename,exception);
855   while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
856          (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
857     map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
858       GetStringInfoPath(option),map_id,exception);
859   options=DestroyConfigureOptions(options);
860   return(map);
861 }
862 \f
863 /*
864 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
865 %                                                                             %
866 %                                                                             %
867 %                                                                             %
868 +  G e t T h r e s h o l d M a p F i l e                                      %
869 %                                                                             %
870 %                                                                             %
871 %                                                                             %
872 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
873 %
874 %  GetThresholdMapFile() look for a given threshold map name or alias in the
875 %  given XML file data, and return the allocated the map when found.
876 %
877 %  The format of the ListThresholdMaps method is:
878 %
879 %      ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
880 %         const char *map_id,ExceptionInfo *exception)
881 %
882 %  A description of each parameter follows.
883 %
884 %    o xml:  The threshold map list in XML format.
885 %
886 %    o filename:  The threshold map XML filename.
887 %
888 %    o map_id:  ID of the map to look for in XML list.
889 %
890 %    o exception: return any errors or warnings in this structure.
891 %
892 */
893 static ThresholdMap *GetThresholdMapFile(const char *xml,
894   const char *filename,const char *map_id,ExceptionInfo *exception)
895 {
896   char
897     *p;
898
899   const char
900     *attribute,
901     *content;
902
903   double
904     value;
905
906   register ssize_t
907     i;
908
909   ThresholdMap
910     *map;
911
912   XMLTreeInfo
913     *description,
914     *levels,
915     *threshold,
916     *thresholds;
917
918   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
919     "Loading threshold map file \"%s\" ...",filename);
920   map=(ThresholdMap *) NULL;
921   thresholds=NewXMLTree(xml,exception);
922   if (thresholds == (XMLTreeInfo *) NULL)
923     return(map);
924   for (threshold=GetXMLTreeChild(thresholds,"threshold");
925        threshold != (XMLTreeInfo *) NULL;
926        threshold=GetNextXMLTreeTag(threshold))
927   {
928     attribute=GetXMLTreeAttribute(threshold,"map");
929     if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
930       break;
931     attribute=GetXMLTreeAttribute(threshold,"alias");
932     if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
933       break;
934   }
935   if (threshold == (XMLTreeInfo *) NULL)
936     return(map);
937   description=GetXMLTreeChild(threshold,"description");
938   if (description == (XMLTreeInfo *) NULL)
939     {
940       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
941         "XmlMissingElement", "<description>, map \"%s\"",map_id);
942       thresholds=DestroyXMLTree(thresholds);
943       return(map);
944     }
945   levels=GetXMLTreeChild(threshold,"levels");
946   if (levels == (XMLTreeInfo *) NULL)
947     {
948       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
949         "XmlMissingElement", "<levels>, map \"%s\"", map_id);
950       thresholds=DestroyXMLTree(thresholds);
951       return(map);
952     }
953   map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
954   if (map == (ThresholdMap *) NULL)
955     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
956   map->map_id=(char *) NULL;
957   map->description=(char *) NULL;
958   map->levels=(ssize_t *) NULL;
959   attribute=GetXMLTreeAttribute(threshold,"map");
960   if (attribute != (char *) NULL)
961     map->map_id=ConstantString(attribute);
962   content=GetXMLTreeContent(description);
963   if (content != (char *) NULL)
964     map->description=ConstantString(content);
965   attribute=GetXMLTreeAttribute(levels,"width");
966   if (attribute == (char *) NULL)
967     {
968       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
969         "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
970       thresholds=DestroyXMLTree(thresholds);
971       map=DestroyThresholdMap(map);
972       return(map);
973     }
974   map->width=StringToUnsignedLong(attribute);
975   if (map->width == 0)
976     {
977       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
978        "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
979       thresholds=DestroyXMLTree(thresholds);
980       map=DestroyThresholdMap(map);
981       return(map);
982     }
983   attribute=GetXMLTreeAttribute(levels,"height");
984   if (attribute == (char *) NULL)
985     {
986       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
987         "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
988       thresholds=DestroyXMLTree(thresholds);
989       map=DestroyThresholdMap(map);
990       return(map);
991     }
992   map->height=StringToUnsignedLong(attribute);
993   if (map->height == 0)
994     {
995       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
996         "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
997       thresholds=DestroyXMLTree(thresholds);
998       map=DestroyThresholdMap(map);
999       return(map);
1000     }
1001   attribute=GetXMLTreeAttribute(levels,"divisor");
1002   if (attribute == (char *) NULL)
1003     {
1004       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1005         "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1006       thresholds=DestroyXMLTree(thresholds);
1007       map=DestroyThresholdMap(map);
1008       return(map);
1009     }
1010   map->divisor=(ssize_t) StringToLong(attribute);
1011   if (map->divisor < 2)
1012     {
1013       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1014         "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1015       thresholds=DestroyXMLTree(thresholds);
1016       map=DestroyThresholdMap(map);
1017       return(map);
1018     }
1019   content=GetXMLTreeContent(levels);
1020   if (content == (char *) NULL)
1021     {
1022       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1023         "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1024       thresholds=DestroyXMLTree(thresholds);
1025       map=DestroyThresholdMap(map);
1026       return(map);
1027     }
1028   map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1029     sizeof(*map->levels));
1030   if (map->levels == (ssize_t *) NULL)
1031     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1032   for (i=0; i < (ssize_t) (map->width*map->height); i++)
1033   {
1034     map->levels[i]=(ssize_t) strtol(content,&p,10);
1035     if (p == content)
1036       {
1037         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1038           "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1039         thresholds=DestroyXMLTree(thresholds);
1040         map=DestroyThresholdMap(map);
1041         return(map);
1042       }
1043     if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1044       {
1045         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1046           "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1047           (double) map->levels[i],map_id);
1048         thresholds=DestroyXMLTree(thresholds);
1049         map=DestroyThresholdMap(map);
1050         return(map);
1051       }
1052     content=p;
1053   }
1054   value=(double) strtol(content,&p,10);
1055   (void) value;
1056   if (p != content)
1057     {
1058       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1059         "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1060      thresholds=DestroyXMLTree(thresholds);
1061      map=DestroyThresholdMap(map);
1062      return(map);
1063    }
1064   thresholds=DestroyXMLTree(thresholds);
1065   return(map);
1066 }
1067 \f
1068 /*
1069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1070 %                                                                             %
1071 %                                                                             %
1072 %                                                                             %
1073 +  L i s t T h r e s h o l d M a p F i l e                                    %
1074 %                                                                             %
1075 %                                                                             %
1076 %                                                                             %
1077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1078 %
1079 %  ListThresholdMapFile() lists the threshold maps and their descriptions
1080 %  in the given XML file data.
1081 %
1082 %  The format of the ListThresholdMaps method is:
1083 %
1084 %      MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1085 %         const char *filename,ExceptionInfo *exception)
1086 %
1087 %  A description of each parameter follows.
1088 %
1089 %    o file:  An pointer to the output FILE.
1090 %
1091 %    o xml:  The threshold map list in XML format.
1092 %
1093 %    o filename:  The threshold map XML filename.
1094 %
1095 %    o exception: return any errors or warnings in this structure.
1096 %
1097 */
1098 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1099   const char *filename,ExceptionInfo *exception)
1100 {
1101   const char
1102     *alias,
1103     *content,
1104     *map;
1105
1106   XMLTreeInfo
1107     *description,
1108     *threshold,
1109     *thresholds;
1110
1111   assert( xml != (char *)NULL );
1112   assert( file != (FILE *)NULL );
1113   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1114     "Loading threshold map file \"%s\" ...",filename);
1115   thresholds=NewXMLTree(xml,exception);
1116   if ( thresholds == (XMLTreeInfo *)NULL )
1117     return(MagickFalse);
1118   (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1119   (void) FormatLocaleFile(file,
1120     "----------------------------------------------------\n");
1121   threshold=GetXMLTreeChild(thresholds,"threshold");
1122   for ( ; threshold != (XMLTreeInfo *) NULL;
1123           threshold=GetNextXMLTreeTag(threshold))
1124   {
1125     map=GetXMLTreeAttribute(threshold,"map");
1126     if (map == (char *) NULL)
1127       {
1128         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1129           "XmlMissingAttribute", "<map>");
1130         thresholds=DestroyXMLTree(thresholds);
1131         return(MagickFalse);
1132       }
1133     alias=GetXMLTreeAttribute(threshold,"alias");
1134     description=GetXMLTreeChild(threshold,"description");
1135     if (description == (XMLTreeInfo *) NULL)
1136       {
1137         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1138           "XmlMissingElement", "<description>, map \"%s\"",map);
1139         thresholds=DestroyXMLTree(thresholds);
1140         return(MagickFalse);
1141       }
1142     content=GetXMLTreeContent(description);
1143     if (content == (char *) NULL)
1144       {
1145         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1146           "XmlMissingContent", "<description>, map \"%s\"", map);
1147         thresholds=DestroyXMLTree(thresholds);
1148         return(MagickFalse);
1149       }
1150     (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1151       content);
1152   }
1153   thresholds=DestroyXMLTree(thresholds);
1154   return(MagickTrue);
1155 }
1156 \f
1157 /*
1158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1159 %                                                                             %
1160 %                                                                             %
1161 %                                                                             %
1162 %  L i s t T h r e s h o l d M a p s                                          %
1163 %                                                                             %
1164 %                                                                             %
1165 %                                                                             %
1166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1167 %
1168 %  ListThresholdMaps() lists the threshold maps and their descriptions
1169 %  as defined by "threshold.xml" to a file.
1170 %
1171 %  The format of the ListThresholdMaps method is:
1172 %
1173 %      MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1174 %
1175 %  A description of each parameter follows.
1176 %
1177 %    o file:  An pointer to the output FILE.
1178 %
1179 %    o exception: return any errors or warnings in this structure.
1180 %
1181 */
1182 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1183   ExceptionInfo *exception)
1184 {
1185   const StringInfo
1186     *option;
1187
1188   LinkedListInfo
1189     *options;
1190
1191   MagickStatusType
1192     status;
1193
1194   status=MagickFalse;
1195   if (file == (FILE *) NULL)
1196     file=stdout;
1197   options=GetConfigureOptions(ThresholdsFilename,exception);
1198   (void) FormatLocaleFile(file,
1199     "\n   Threshold Maps for Ordered Dither Operations\n");
1200   while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1201          (const StringInfo *) NULL)
1202   {
1203     (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1204     status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1205       GetStringInfoPath(option),exception);
1206   }
1207   options=DestroyConfigureOptions(options);
1208   return(status != 0 ? MagickTrue : MagickFalse);
1209 }
1210 \f
1211 /*
1212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1213 %                                                                             %
1214 %                                                                             %
1215 %                                                                             %
1216 %     O r d e r e d P o s t e r i z e I m a g e                               %
1217 %                                                                             %
1218 %                                                                             %
1219 %                                                                             %
1220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1221 %
1222 %  OrderedPosterizeImage() will perform a ordered dither based on a number
1223 %  of pre-defined dithering threshold maps, but over multiple intensity
1224 %  levels, which can be different for different channels, according to the
1225 %  input argument.
1226 %
1227 %  The format of the OrderedPosterizeImage method is:
1228 %
1229 %      MagickBooleanType OrderedPosterizeImage(Image *image,
1230 %        const char *threshold_map,ExceptionInfo *exception)
1231 %
1232 %  A description of each parameter follows:
1233 %
1234 %    o image: the image.
1235 %
1236 %    o threshold_map: A string containing the name of the threshold dither
1237 %      map to use, followed by zero or more numbers representing the number
1238 %      of color levels tho dither between.
1239 %
1240 %      Any level number less than 2 will be equivalent to 2, and means only
1241 %      binary dithering will be applied to each color channel.
1242 %
1243 %      No numbers also means a 2 level (bitmap) dither will be applied to all
1244 %      channels, while a single number is the number of levels applied to each
1245 %      channel in sequence.  More numbers will be applied in turn to each of
1246 %      the color channels.
1247 %
1248 %      For example: "o3x3,6" will generate a 6 level posterization of the
1249 %      image with a ordered 3x3 diffused pixel dither being applied between
1250 %      each level. While checker,8,8,4 will produce a 332 colormaped image
1251 %      with only a single checkerboard hash pattern (50% grey) between each
1252 %      color level, to basically double the number of color levels with
1253 %      a bare minimim of dithering.
1254 %
1255 %    o exception: return any errors or warnings in this structure.
1256 %
1257 */
1258 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1259   const char *threshold_map,ExceptionInfo *exception)
1260 {
1261 #define DitherImageTag  "Dither/Image"
1262
1263   CacheView
1264     *image_view;
1265
1266   char
1267     token[MaxTextExtent];
1268
1269   const char
1270     *p;
1271
1272   MagickBooleanType
1273     status;
1274
1275   MagickOffsetType
1276     progress;
1277
1278   MagickRealType
1279     levels[CompositePixelChannel];
1280
1281   register ssize_t
1282     i;
1283
1284   ssize_t
1285     y;
1286
1287   ThresholdMap
1288     *map;
1289
1290   assert(image != (Image *) NULL);
1291   assert(image->signature == MagickSignature);
1292   if (image->debug != MagickFalse)
1293     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1294   assert(exception != (ExceptionInfo *) NULL);
1295   assert(exception->signature == MagickSignature);
1296   if (threshold_map == (const char *) NULL)
1297     return(MagickTrue);
1298   p=(char *) threshold_map;
1299   while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1300          (*p != '\0'))
1301     p++;
1302   threshold_map=p;
1303   while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1304          (*p != '\0'))
1305   {
1306     if ((p-threshold_map) >= (MaxTextExtent-1))
1307       break;
1308     token[p-threshold_map]=(*p);
1309     p++;
1310   }
1311   token[p-threshold_map]='\0';
1312   map=GetThresholdMap(token,exception);
1313   if (map == (ThresholdMap *) NULL)
1314     {
1315       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1316         "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1317       return(MagickFalse);
1318     }
1319   for (i=0; i < MaxPixelChannels; i++)
1320     levels[i]=2.0;
1321   p=strchr((char *) threshold_map,',');
1322   if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1323     for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1324     {
1325       GetMagickToken(p,&p,token);
1326       if (*token == ',')
1327         GetMagickToken(p,&p,token);
1328       levels[i]=StringToDouble(token,(char **) NULL);
1329     }
1330   for (i=0; i < MaxPixelChannels; i++)
1331     if (fabs(levels[i]) >= 1)
1332       levels[i]-=1.0;
1333   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1334     return(MagickFalse);
1335   status=MagickTrue;
1336   progress=0;
1337   image_view=AcquireAuthenticCacheView(image,exception);
1338 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1339   #pragma omp parallel for schedule(static,8) shared(progress,status) \
1340     dynamic_number_threads(image,image->columns,image->rows,1)
1341 #endif
1342   for (y=0; y < (ssize_t) image->rows; y++)
1343   {
1344     register ssize_t
1345       x;
1346
1347     register Quantum
1348       *restrict q;
1349
1350     if (status == MagickFalse)
1351       continue;
1352     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1353     if (q == (Quantum *) NULL)
1354       {
1355         status=MagickFalse;
1356         continue;
1357       }
1358     for (x=0; x < (ssize_t) image->columns; x++)
1359     {
1360       register ssize_t
1361         i;
1362
1363       ssize_t
1364         n;
1365
1366       n=0;
1367       if (GetPixelMask(image,q) != 0)
1368         {
1369           q+=GetPixelChannels(image);
1370           continue;
1371         }
1372       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1373       {
1374         PixelChannel
1375           channel;
1376
1377         PixelTrait
1378           traits;
1379
1380         ssize_t
1381           level,
1382           threshold;
1383
1384         channel=GetPixelChannelMapChannel(image,i);
1385         traits=GetPixelChannelMapTraits(image,channel);
1386         if ((traits & UpdatePixelTrait) == 0)
1387           continue;
1388         if (fabs(levels[n++]) < MagickEpsilon)
1389           continue;
1390         threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1391         level=threshold/(map->divisor-1);
1392         threshold-=level*(map->divisor-1);
1393         q[i]=ClampToQuantum((double) (level+(threshold >=
1394           map->levels[(x % map->width)+map->width*(y % map->height)]))*
1395           QuantumRange/levels[n]);
1396         n++;
1397       }
1398       q+=GetPixelChannels(image);
1399     }
1400     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1401       status=MagickFalse;
1402     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1403       {
1404         MagickBooleanType
1405           proceed;
1406
1407 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1408         #pragma omp critical (MagickCore_OrderedPosterizeImage)
1409 #endif
1410         proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1411         if (proceed == MagickFalse)
1412           status=MagickFalse;
1413       }
1414   }
1415   image_view=DestroyCacheView(image_view);
1416   map=DestroyThresholdMap(map);
1417   return(MagickTrue);
1418 }
1419 \f
1420 /*
1421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422 %                                                                             %
1423 %                                                                             %
1424 %                                                                             %
1425 %     R a n d o m T h r e s h o l d I m a g e                                 %
1426 %                                                                             %
1427 %                                                                             %
1428 %                                                                             %
1429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430 %
1431 %  RandomThresholdImage() changes the value of individual pixels based on the
1432 %  intensity of each pixel compared to a random threshold.  The result is a
1433 %  low-contrast, two color image.
1434 %
1435 %  The format of the RandomThresholdImage method is:
1436 %
1437 %      MagickBooleanType RandomThresholdImage(Image *image,
1438 %        const char *thresholds,ExceptionInfo *exception)
1439 %
1440 %  A description of each parameter follows:
1441 %
1442 %    o image: the image.
1443 %
1444 %    o thresholds: a geometry string containing low,high thresholds.  If the
1445 %      string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1446 %      is performed instead.
1447 %
1448 %    o exception: return any errors or warnings in this structure.
1449 %
1450 */
1451 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1452   const char *thresholds,ExceptionInfo *exception)
1453 {
1454 #define ThresholdImageTag  "Threshold/Image"
1455
1456   CacheView
1457     *image_view;
1458
1459   GeometryInfo
1460     geometry_info;
1461
1462   MagickStatusType
1463     flags;
1464
1465   MagickBooleanType
1466     status;
1467
1468   MagickOffsetType
1469     progress;
1470
1471   PixelInfo
1472     threshold;
1473
1474   MagickRealType
1475     min_threshold,
1476     max_threshold;
1477
1478   RandomInfo
1479     **restrict random_info;
1480
1481   ssize_t
1482     y;
1483
1484   unsigned long
1485     key;
1486
1487   assert(image != (Image *) NULL);
1488   assert(image->signature == MagickSignature);
1489   if (image->debug != MagickFalse)
1490     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1491   assert(exception != (ExceptionInfo *) NULL);
1492   assert(exception->signature == MagickSignature);
1493   if (thresholds == (const char *) NULL)
1494     return(MagickTrue);
1495   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1496     return(MagickFalse);
1497   GetPixelInfo(image,&threshold);
1498   min_threshold=0.0;
1499   max_threshold=(MagickRealType) QuantumRange;
1500   flags=ParseGeometry(thresholds,&geometry_info);
1501   min_threshold=geometry_info.rho;
1502   max_threshold=geometry_info.sigma;
1503   if ((flags & SigmaValue) == 0)
1504     max_threshold=min_threshold;
1505   if (strchr(thresholds,'%') != (char *) NULL)
1506     {
1507       max_threshold*=(MagickRealType) (0.01*QuantumRange);
1508       min_threshold*=(MagickRealType) (0.01*QuantumRange);
1509     }
1510   /*
1511     Random threshold image.
1512   */
1513   status=MagickTrue;
1514   progress=0;
1515   random_info=AcquireRandomInfoThreadSet();
1516   key=GetRandomSecretKey(random_info[0]);
1517   image_view=AcquireAuthenticCacheView(image,exception);
1518 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1519   #pragma omp parallel for schedule(static,8) shared(progress,status) \
1520     dynamic_number_threads(image,image->columns,image->rows,key == ~0UL)
1521 #endif
1522   for (y=0; y < (ssize_t) image->rows; y++)
1523   {
1524     const int
1525       id = GetOpenMPThreadId();
1526
1527     register Quantum
1528       *restrict q;
1529
1530     register ssize_t
1531       x;
1532
1533     if (status == MagickFalse)
1534       continue;
1535     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1536     if (q == (Quantum *) NULL)
1537       {
1538         status=MagickFalse;
1539         continue;
1540       }
1541     for (x=0; x < (ssize_t) image->columns; x++)
1542     {
1543       register ssize_t
1544         i;
1545
1546       if (GetPixelMask(image,q) != 0)
1547         {
1548           q+=GetPixelChannels(image);
1549           continue;
1550         }
1551       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1552       {
1553         MagickRealType
1554           threshold;
1555
1556         PixelChannel
1557           channel;
1558
1559         PixelTrait
1560           traits;
1561
1562         channel=GetPixelChannelMapChannel(image,i);
1563         traits=GetPixelChannelMapTraits(image,channel);
1564         if ((traits & UpdatePixelTrait) == 0)
1565           continue;
1566         if ((MagickRealType) q[i] < min_threshold)
1567           threshold=min_threshold;
1568         else
1569           if ((MagickRealType) q[i] > max_threshold)
1570             threshold=max_threshold;
1571           else
1572             threshold=(MagickRealType) (QuantumRange*
1573               GetPseudoRandomValue(random_info[id]));
1574         q[i]=(MagickRealType) q[i] <= threshold ? 0 : QuantumRange;
1575       }
1576       q+=GetPixelChannels(image);
1577     }
1578     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1579       status=MagickFalse;
1580     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1581       {
1582         MagickBooleanType
1583           proceed;
1584
1585 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1586         #pragma omp critical (MagickCore_RandomThresholdImage)
1587 #endif
1588         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1589           image->rows);
1590         if (proceed == MagickFalse)
1591           status=MagickFalse;
1592       }
1593   }
1594   image_view=DestroyCacheView(image_view);
1595   random_info=DestroyRandomInfoThreadSet(random_info);
1596   return(status);
1597 }
1598 \f
1599 /*
1600 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1601 %                                                                             %
1602 %                                                                             %
1603 %                                                                             %
1604 %     W h i t e T h r e s h o l d I m a g e                                   %
1605 %                                                                             %
1606 %                                                                             %
1607 %                                                                             %
1608 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1609 %
1610 %  WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1611 %  the threshold into white while leaving all pixels at or below the threshold
1612 %  unchanged.
1613 %
1614 %  The format of the WhiteThresholdImage method is:
1615 %
1616 %      MagickBooleanType WhiteThresholdImage(Image *image,
1617 %        const char *threshold,ExceptionInfo *exception)
1618 %
1619 %  A description of each parameter follows:
1620 %
1621 %    o image: the image.
1622 %
1623 %    o threshold: Define the threshold value.
1624 %
1625 %    o exception: return any errors or warnings in this structure.
1626 %
1627 */
1628 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1629   const char *thresholds,ExceptionInfo *exception)
1630 {
1631 #define ThresholdImageTag  "Threshold/Image"
1632
1633   CacheView
1634     *image_view;
1635
1636   GeometryInfo
1637     geometry_info;
1638
1639   MagickBooleanType
1640     status;
1641
1642   MagickOffsetType
1643     progress;
1644
1645   PixelInfo
1646     threshold;
1647
1648   MagickStatusType
1649     flags;
1650
1651   ssize_t
1652     y;
1653
1654   assert(image != (Image *) NULL);
1655   assert(image->signature == MagickSignature);
1656   if (image->debug != MagickFalse)
1657     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1658   if (thresholds == (const char *) NULL)
1659     return(MagickTrue);
1660   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1661     return(MagickFalse);
1662   if (IsGrayColorspace(image->colorspace) != MagickFalse)
1663     (void) TransformImageColorspace(image,RGBColorspace,exception);
1664   GetPixelInfo(image,&threshold);
1665   flags=ParseGeometry(thresholds,&geometry_info);
1666   threshold.red=geometry_info.rho;
1667   threshold.green=geometry_info.rho;
1668   threshold.blue=geometry_info.rho;
1669   threshold.black=geometry_info.rho;
1670   threshold.alpha=100.0;
1671   if ((flags & SigmaValue) != 0)
1672     threshold.green=geometry_info.sigma;
1673   if ((flags & XiValue) != 0)
1674     threshold.blue=geometry_info.xi;
1675   if ((flags & PsiValue) != 0)
1676     threshold.alpha=geometry_info.psi;
1677   if (threshold.colorspace == CMYKColorspace)
1678     {
1679       if ((flags & PsiValue) != 0)
1680         threshold.black=geometry_info.psi;
1681       if ((flags & ChiValue) != 0)
1682         threshold.alpha=geometry_info.chi;
1683     }
1684   if ((flags & PercentValue) != 0)
1685     {
1686       threshold.red*=(QuantumRange/100.0);
1687       threshold.green*=(QuantumRange/100.0);
1688       threshold.blue*=(QuantumRange/100.0);
1689       threshold.black*=(QuantumRange/100.0);
1690       threshold.alpha*=(QuantumRange/100.0);
1691     }
1692   /*
1693     White threshold image.
1694   */
1695   status=MagickTrue;
1696   progress=0;
1697   image_view=AcquireAuthenticCacheView(image,exception);
1698 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1699   #pragma omp parallel for schedule(static,8) shared(progress,status) \
1700     dynamic_number_threads(image,image->columns,image->rows,1)
1701 #endif
1702   for (y=0; y < (ssize_t) image->rows; y++)
1703   {
1704     register ssize_t
1705       x;
1706
1707     register Quantum
1708       *restrict q;
1709
1710     if (status == MagickFalse)
1711       continue;
1712     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1713     if (q == (Quantum *) NULL)
1714       {
1715         status=MagickFalse;
1716         continue;
1717       }
1718     for (x=0; x < (ssize_t) image->columns; x++)
1719     {
1720       register ssize_t
1721         i;
1722
1723       if (GetPixelMask(image,q) != 0)
1724         {
1725           q+=GetPixelChannels(image);
1726           continue;
1727         }
1728       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1729       {
1730         PixelChannel
1731           channel;
1732
1733         PixelTrait
1734           traits;
1735
1736         channel=GetPixelChannelMapChannel(image,i);
1737         traits=GetPixelChannelMapTraits(image,channel);
1738         if ((traits & UpdatePixelTrait) == 0)
1739           continue;
1740         if ((double) q[i] > GetPixelInfoChannel(&threshold,channel))
1741           q[i]=QuantumRange;
1742       }
1743       q+=GetPixelChannels(image);
1744     }
1745     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1746       status=MagickFalse;
1747     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1748       {
1749         MagickBooleanType
1750           proceed;
1751
1752 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1753         #pragma omp critical (MagickCore_WhiteThresholdImage)
1754 #endif
1755         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1756           image->rows);
1757         if (proceed == MagickFalse)
1758           status=MagickFalse;
1759       }
1760   }
1761   image_view=DestroyCacheView(image_view);
1762   return(status);
1763 }