2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Image Threshold Methods %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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/configure.h"
52 #include "MagickCore/constitute.h"
53 #include "MagickCore/decorate.h"
54 #include "MagickCore/draw.h"
55 #include "MagickCore/enhance.h"
56 #include "MagickCore/exception.h"
57 #include "MagickCore/exception-private.h"
58 #include "MagickCore/effect.h"
59 #include "MagickCore/fx.h"
60 #include "MagickCore/gem.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/monitor.h"
67 #include "MagickCore/monitor-private.h"
68 #include "MagickCore/montage.h"
69 #include "MagickCore/option.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/quantize.h"
72 #include "MagickCore/quantum.h"
73 #include "MagickCore/random_.h"
74 #include "MagickCore/random-private.h"
75 #include "MagickCore/resize.h"
76 #include "MagickCore/resource_.h"
77 #include "MagickCore/segment.h"
78 #include "MagickCore/shear.h"
79 #include "MagickCore/signature-private.h"
80 #include "MagickCore/string_.h"
81 #include "MagickCore/string-private.h"
82 #include "MagickCore/thread-private.h"
83 #include "MagickCore/threshold.h"
84 #include "MagickCore/transform.h"
85 #include "MagickCore/xml-tree.h"
86 #include "MagickCore/xml-tree-private.h"
91 #define ThresholdsFilename "thresholds.xml"
112 Forward declarations.
115 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 % A d a p t i v e T h r e s h o l d I m a g e %
126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
128 % AdaptiveThresholdImage() selects an individual threshold for each pixel
129 % based on the range of intensity values in its local neighborhood. This
130 % allows for thresholding of an image whose global intensity histogram
131 % doesn't contain distinctive peaks.
133 % The format of the AdaptiveThresholdImage method is:
135 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
136 % const size_t height,const double bias,ExceptionInfo *exception)
138 % A description of each parameter follows:
140 % o image: the image.
142 % o width: the width of the local neighborhood.
144 % o height: the height of the local neighborhood.
146 % o bias: the mean bias.
148 % o exception: return any errors or warnings in this structure.
151 MagickExport Image *AdaptiveThresholdImage(const Image *image,
152 const size_t width,const size_t height,const double bias,
153 ExceptionInfo *exception)
155 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
177 Initialize threshold image attributes.
179 assert(image != (Image *) NULL);
180 assert(image->signature == MagickSignature);
181 if (image->debug != MagickFalse)
182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
183 assert(exception != (ExceptionInfo *) NULL);
184 assert(exception->signature == MagickSignature);
185 if ((width % 2) == 0)
186 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
187 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
189 if (threshold_image == (Image *) NULL)
190 return((Image *) NULL);
191 status=SetImageStorageClass(threshold_image,DirectClass,exception);
192 if (status == MagickFalse)
194 threshold_image=DestroyImage(threshold_image);
195 return((Image *) NULL);
202 number_pixels=(MagickSizeType) width*height;
203 image_view=AcquireCacheView(image);
204 threshold_view=AcquireCacheView(threshold_image);
205 #if defined(MAGICKCORE_OPENMP_SUPPORT)
206 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
208 for (y=0; y < (ssize_t) image->rows; y++)
210 register const Quantum
222 if (status == MagickFalse)
224 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
225 (height/2L),image->columns+width,height,exception);
226 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
228 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
233 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
234 GetPixelChannels(image)*(width/2);
235 for (x=0; x < (ssize_t) image->columns; x++)
240 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
253 register const Quantum
262 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
263 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
264 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
265 if ((traits == UndefinedPixelTrait) ||
266 (threshold_traits == UndefinedPixelTrait))
268 if ((threshold_traits & CopyPixelTrait) != 0)
270 SetPixelChannel(threshold_image,channel,p[center+i],q);
275 for (v=0; v < (ssize_t) height; v++)
277 for (u=0; u < (ssize_t) width; u++)
280 pixels+=GetPixelChannels(image);
282 pixels+=image->columns*GetPixelChannels(image);
284 mean=(MagickRealType) (pixel/number_pixels+bias);
285 SetPixelChannel(threshold_image,channel,(Quantum) ((MagickRealType)
286 p[center+i] <= mean ? 0 : QuantumRange),q);
288 p+=GetPixelChannels(image);
289 q+=GetPixelChannels(threshold_image);
291 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
293 if (image->progress_monitor != (MagickProgressMonitor) NULL)
298 #if defined(MAGICKCORE_OPENMP_SUPPORT)
299 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
301 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
303 if (proceed == MagickFalse)
307 threshold_image->type=image->type;
308 threshold_view=DestroyCacheView(threshold_view);
309 image_view=DestroyCacheView(image_view);
310 if (status == MagickFalse)
311 threshold_image=DestroyImage(threshold_image);
312 return(threshold_image);
316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
320 % B i l e v e l I m a g e %
324 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326 % BilevelImage() changes the value of individual pixels based on the
327 % intensity of each pixel channel. The result is a high-contrast image.
329 % More precisely each channel value of the image is 'thresholded' so that if
330 % it is equal to or less than the given value it is set to zero, while any
331 % value greater than that give is set to it maximum or QuantumRange.
333 % This function is what is used to implement the "-threshold" operator for
334 % the command line API.
336 % If the default channel setting is given the image is thresholded using just
337 % the gray 'intensity' of the image, rather than the individual channels.
339 % The format of the BilevelImage method is:
341 % MagickBooleanType BilevelImage(Image *image,const double threshold)
343 % A description of each parameter follows:
345 % o image: the image.
347 % o threshold: define the threshold values.
349 % Aside: You can get the same results as operator using LevelImages()
350 % with the 'threshold' value for both the black_point and the white_point.
353 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
355 #define ThresholdImageTag "Threshold/Image"
372 assert(image != (Image *) NULL);
373 assert(image->signature == MagickSignature);
374 if (image->debug != MagickFalse)
375 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
376 exception=(&image->exception);
377 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
380 Bilevel threshold image.
384 image_view=AcquireCacheView(image);
385 #if defined(MAGICKCORE_OPENMP_SUPPORT)
386 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
388 for (y=0; y < (ssize_t) image->rows; y++)
396 if (status == MagickFalse)
398 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
399 if (q == (Quantum *) NULL)
404 for (x=0; x < (ssize_t) image->columns; x++)
409 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
414 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
415 if ((traits & UpdatePixelTrait) != 0)
416 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
419 q+=GetPixelChannels(image);
421 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
423 if (image->progress_monitor != (MagickProgressMonitor) NULL)
428 #if defined(MAGICKCORE_OPENMP_SUPPORT)
429 #pragma omp critical (MagickCore_BilevelImage)
431 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
433 if (proceed == MagickFalse)
437 image_view=DestroyCacheView(image_view);
442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
446 % B l a c k T h r e s h o l d I m a g e %
450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
452 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
453 % the threshold into black while leaving all pixels at or above the threshold
456 % The format of the BlackThresholdImage method is:
458 % MagickBooleanType BlackThresholdImage(Image *image,
459 % const char *threshold,ExceptionInfo *exception)
461 % A description of each parameter follows:
463 % o image: the image.
465 % o threshold: define the threshold value.
467 % o exception: return any errors or warnings in this structure.
470 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
471 const char *thresholds,ExceptionInfo *exception)
473 #define ThresholdImageTag "Threshold/Image"
499 assert(image != (Image *) NULL);
500 assert(image->signature == MagickSignature);
501 if (image->debug != MagickFalse)
502 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
503 if (thresholds == (const char *) NULL)
505 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
507 flags=ParseGeometry(thresholds,&geometry_info);
508 for (i=0; i < 5; i++)
509 threshold[i]=geometry_info.rho;
510 if ((flags & SigmaValue) != 0)
511 threshold[1]=geometry_info.sigma;
512 if ((flags & XiValue) != 0)
513 threshold[2]=geometry_info.xi;
514 if ((flags & PsiValue) != 0)
515 threshold[3]=geometry_info.psi;
516 if ((flags & ChiValue) != 0)
517 threshold[4]=geometry_info.chi;
518 if ((flags & PercentValue) != 0)
519 for (i=0; i < 5; i++)
520 threshold[i]*=(QuantumRange/100.0);
522 White threshold image.
526 image_view=AcquireCacheView(image);
527 #if defined(MAGICKCORE_OPENMP_SUPPORT)
528 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
530 for (y=0; y < (ssize_t) image->rows; y++)
538 if (status == MagickFalse)
540 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
541 if (q == (Quantum *) NULL)
546 for (x=0; x < (ssize_t) image->columns; x++)
555 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
560 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
561 if ((traits & UpdatePixelTrait) == 0)
563 if ((MagickRealType) q[i] < threshold[n++ % 5])
566 q+=GetPixelChannels(image);
568 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
570 if (image->progress_monitor != (MagickProgressMonitor) NULL)
575 #if defined(MAGICKCORE_OPENMP_SUPPORT)
576 #pragma omp critical (MagickCore_WhiteThresholdImage)
578 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
580 if (proceed == MagickFalse)
584 image_view=DestroyCacheView(image_view);
589 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
593 % C l a m p I m a g e %
597 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
599 % ClampImage() restricts the color range from 0 to the quantum depth.
601 % The format of the ClampImage method is:
603 % MagickBooleanType ClampImage(Image *image)
605 % A description of each parameter follows:
607 % o image: the image.
611 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
613 #if defined(MAGICKCORE_HDRI_SUPPORT)
616 if (quantum >= QuantumRange)
617 return(QuantumRange);
624 MagickExport MagickBooleanType ClampImage(Image *image)
626 #define ClampImageTag "Clamp/Image"
643 assert(image != (Image *) NULL);
644 assert(image->signature == MagickSignature);
645 if (image->debug != MagickFalse)
646 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
647 if (image->storage_class == PseudoClass)
656 for (i=0; i < (ssize_t) image->colors; i++)
658 q->red=ClampToUnsignedQuantum(q->red);
659 q->green=ClampToUnsignedQuantum(q->green);
660 q->blue=ClampToUnsignedQuantum(q->blue);
661 q->alpha=ClampToUnsignedQuantum(q->alpha);
664 return(SyncImage(image));
671 exception=(&image->exception);
672 image_view=AcquireCacheView(image);
673 #if defined(MAGICKCORE_OPENMP_SUPPORT)
674 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
676 for (y=0; y < (ssize_t) image->rows; y++)
684 if (status == MagickFalse)
686 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
687 if (q == (Quantum *) NULL)
692 for (x=0; x < (ssize_t) image->columns; x++)
697 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
702 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
703 if (traits == UndefinedPixelTrait)
705 q[i]=ClampToUnsignedQuantum(q[i]);
707 q+=GetPixelChannels(image);
709 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
711 if (image->progress_monitor != (MagickProgressMonitor) NULL)
716 #if defined(MAGICKCORE_OPENMP_SUPPORT)
717 #pragma omp critical (MagickCore_ClampImage)
719 proceed=SetImageProgress(image,ClampImageTag,progress++,
721 if (proceed == MagickFalse)
725 image_view=DestroyCacheView(image_view);
730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
734 % D e s t r o y T h r e s h o l d M a p %
738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740 % DestroyThresholdMap() de-allocate the given ThresholdMap
742 % The format of the ListThresholdMaps method is:
744 % ThresholdMap *DestroyThresholdMap(Threshold *map)
746 % A description of each parameter follows.
748 % o map: Pointer to the Threshold map to destroy
751 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
753 assert(map != (ThresholdMap *) NULL);
754 if (map->map_id != (char *) NULL)
755 map->map_id=DestroyString(map->map_id);
756 if (map->description != (char *) NULL)
757 map->description=DestroyString(map->description);
758 if (map->levels != (ssize_t *) NULL)
759 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
760 map=(ThresholdMap *) RelinquishMagickMemory(map);
765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769 % G e t T h r e s h o l d M a p %
773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
775 % GetThresholdMap() loads and searches one or more threshold map files for the
776 % map matching the given name or alias.
778 % The format of the GetThresholdMap method is:
780 % ThresholdMap *GetThresholdMap(const char *map_id,
781 % ExceptionInfo *exception)
783 % A description of each parameter follows.
785 % o map_id: ID of the map to look for.
787 % o exception: return any errors or warnings in this structure.
790 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
791 ExceptionInfo *exception)
802 map=(ThresholdMap *)NULL;
803 options=GetConfigureOptions(ThresholdsFilename,exception);
804 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
805 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
806 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
807 GetStringInfoPath(option),map_id,exception);
808 options=DestroyConfigureOptions(options);
813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817 + G e t T h r e s h o l d M a p F i l e %
821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
823 % GetThresholdMapFile() look for a given threshold map name or alias in the
824 % given XML file data, and return the allocated the map when found.
826 % The format of the ListThresholdMaps method is:
828 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
829 % const char *map_id,ExceptionInfo *exception)
831 % A description of each parameter follows.
833 % o xml: The threshold map list in XML format.
835 % o filename: The threshold map XML filename.
837 % o map_id: ID of the map to look for in XML list.
839 % o exception: return any errors or warnings in this structure.
842 static ThresholdMap *GetThresholdMapFile(const char *xml,
843 const char *filename,const char *map_id,ExceptionInfo *exception)
867 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
868 "Loading threshold map file \"%s\" ...",filename);
869 map=(ThresholdMap *) NULL;
870 thresholds=NewXMLTree(xml,exception);
871 if (thresholds == (XMLTreeInfo *) NULL)
873 for (threshold=GetXMLTreeChild(thresholds,"threshold");
874 threshold != (XMLTreeInfo *) NULL;
875 threshold=GetNextXMLTreeTag(threshold))
877 attribute=GetXMLTreeAttribute(threshold,"map");
878 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
880 attribute=GetXMLTreeAttribute(threshold,"alias");
881 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
884 if (threshold == (XMLTreeInfo *) NULL)
886 description=GetXMLTreeChild(threshold,"description");
887 if (description == (XMLTreeInfo *) NULL)
889 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
890 "XmlMissingElement", "<description>, map \"%s\"",map_id);
891 thresholds=DestroyXMLTree(thresholds);
894 levels=GetXMLTreeChild(threshold,"levels");
895 if (levels == (XMLTreeInfo *) NULL)
897 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
898 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
899 thresholds=DestroyXMLTree(thresholds);
902 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
903 if (map == (ThresholdMap *) NULL)
904 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
905 map->map_id=(char *) NULL;
906 map->description=(char *) NULL;
907 map->levels=(ssize_t *) NULL;
908 attribute=GetXMLTreeAttribute(threshold,"map");
909 if (attribute != (char *) NULL)
910 map->map_id=ConstantString(attribute);
911 content=GetXMLTreeContent(description);
912 if (content != (char *) NULL)
913 map->description=ConstantString(content);
914 attribute=GetXMLTreeAttribute(levels,"width");
915 if (attribute == (char *) NULL)
917 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
918 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
919 thresholds=DestroyXMLTree(thresholds);
920 map=DestroyThresholdMap(map);
923 map->width=StringToUnsignedLong(attribute);
926 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
927 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
928 thresholds=DestroyXMLTree(thresholds);
929 map=DestroyThresholdMap(map);
932 attribute=GetXMLTreeAttribute(levels,"height");
933 if (attribute == (char *) NULL)
935 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
936 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
937 thresholds=DestroyXMLTree(thresholds);
938 map=DestroyThresholdMap(map);
941 map->height=StringToUnsignedLong(attribute);
942 if (map->height == 0)
944 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
945 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
946 thresholds=DestroyXMLTree(thresholds);
947 map=DestroyThresholdMap(map);
950 attribute=GetXMLTreeAttribute(levels,"divisor");
951 if (attribute == (char *) NULL)
953 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
954 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
955 thresholds=DestroyXMLTree(thresholds);
956 map=DestroyThresholdMap(map);
959 map->divisor=(ssize_t) StringToLong(attribute);
960 if (map->divisor < 2)
962 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
963 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
964 thresholds=DestroyXMLTree(thresholds);
965 map=DestroyThresholdMap(map);
968 content=GetXMLTreeContent(levels);
969 if (content == (char *) NULL)
971 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
972 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
973 thresholds=DestroyXMLTree(thresholds);
974 map=DestroyThresholdMap(map);
977 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
978 sizeof(*map->levels));
979 if (map->levels == (ssize_t *) NULL)
980 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
981 for (i=0; i < (ssize_t) (map->width*map->height); i++)
983 map->levels[i]=(ssize_t) strtol(content,&p,10);
986 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
987 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
988 thresholds=DestroyXMLTree(thresholds);
989 map=DestroyThresholdMap(map);
992 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
994 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
995 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
996 (double) map->levels[i],map_id);
997 thresholds=DestroyXMLTree(thresholds);
998 map=DestroyThresholdMap(map);
1003 value=(double) strtol(content,&p,10);
1007 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1008 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1009 thresholds=DestroyXMLTree(thresholds);
1010 map=DestroyThresholdMap(map);
1013 thresholds=DestroyXMLTree(thresholds);
1018 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1022 + L i s t T h r e s h o l d M a p F i l e %
1026 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1028 % ListThresholdMapFile() lists the threshold maps and their descriptions
1029 % in the given XML file data.
1031 % The format of the ListThresholdMaps method is:
1033 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1034 % const char *filename,ExceptionInfo *exception)
1036 % A description of each parameter follows.
1038 % o file: An pointer to the output FILE.
1040 % o xml: The threshold map list in XML format.
1042 % o filename: The threshold map XML filename.
1044 % o exception: return any errors or warnings in this structure.
1047 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1048 const char *filename,ExceptionInfo *exception)
1060 assert( xml != (char *)NULL );
1061 assert( file != (FILE *)NULL );
1062 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1063 "Loading threshold map file \"%s\" ...",filename);
1064 thresholds=NewXMLTree(xml,exception);
1065 if ( thresholds == (XMLTreeInfo *)NULL )
1066 return(MagickFalse);
1067 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1068 (void) FormatLocaleFile(file,
1069 "----------------------------------------------------\n");
1070 threshold=GetXMLTreeChild(thresholds,"threshold");
1071 for ( ; threshold != (XMLTreeInfo *) NULL;
1072 threshold=GetNextXMLTreeTag(threshold))
1074 map=GetXMLTreeAttribute(threshold,"map");
1075 if (map == (char *) NULL)
1077 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1078 "XmlMissingAttribute", "<map>");
1079 thresholds=DestroyXMLTree(thresholds);
1080 return(MagickFalse);
1082 alias=GetXMLTreeAttribute(threshold,"alias");
1083 description=GetXMLTreeChild(threshold,"description");
1084 if (description == (XMLTreeInfo *) NULL)
1086 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1087 "XmlMissingElement", "<description>, map \"%s\"",map);
1088 thresholds=DestroyXMLTree(thresholds);
1089 return(MagickFalse);
1091 content=GetXMLTreeContent(description);
1092 if (content == (char *) NULL)
1094 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1095 "XmlMissingContent", "<description>, map \"%s\"", map);
1096 thresholds=DestroyXMLTree(thresholds);
1097 return(MagickFalse);
1099 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1102 thresholds=DestroyXMLTree(thresholds);
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1111 % L i s t T h r e s h o l d M a p s %
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117 % ListThresholdMaps() lists the threshold maps and their descriptions
1118 % as defined by "threshold.xml" to a file.
1120 % The format of the ListThresholdMaps method is:
1122 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1124 % A description of each parameter follows.
1126 % o file: An pointer to the output FILE.
1128 % o exception: return any errors or warnings in this structure.
1131 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1132 ExceptionInfo *exception)
1144 if (file == (FILE *) NULL)
1146 options=GetConfigureOptions(ThresholdsFilename,exception);
1147 (void) FormatLocaleFile(file,
1148 "\n Threshold Maps for Ordered Dither Operations\n");
1149 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1150 (const StringInfo *) NULL)
1152 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1153 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1154 GetStringInfoPath(option),exception);
1156 options=DestroyConfigureOptions(options);
1157 return(status != 0 ? MagickTrue : MagickFalse);
1161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1165 % O r d e r e d P o s t e r i z e I m a g e %
1169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171 % OrderedPosterizeImage() will perform a ordered dither based on a number
1172 % of pre-defined dithering threshold maps, but over multiple intensity
1173 % levels, which can be different for different channels, according to the
1176 % The format of the OrderedPosterizeImage method is:
1178 % MagickBooleanType OrderedPosterizeImage(Image *image,
1179 % const char *threshold_map,ExceptionInfo *exception)
1181 % A description of each parameter follows:
1183 % o image: the image.
1185 % o threshold_map: A string containing the name of the threshold dither
1186 % map to use, followed by zero or more numbers representing the number
1187 % of color levels tho dither between.
1189 % Any level number less than 2 will be equivalent to 2, and means only
1190 % binary dithering will be applied to each color channel.
1192 % No numbers also means a 2 level (bitmap) dither will be applied to all
1193 % channels, while a single number is the number of levels applied to each
1194 % channel in sequence. More numbers will be applied in turn to each of
1195 % the color channels.
1197 % For example: "o3x3,6" will generate a 6 level posterization of the
1198 % image with a ordered 3x3 diffused pixel dither being applied between
1199 % each level. While checker,8,8,4 will produce a 332 colormaped image
1200 % with only a single checkerboard hash pattern (50% grey) between each
1201 % color level, to basically double the number of color levels with
1202 % a bare minimim of dithering.
1204 % o exception: return any errors or warnings in this structure.
1207 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1208 const char *threshold_map,ExceptionInfo *exception)
1210 #define DitherImageTag "Dither/Image"
1230 assert(image != (Image *) NULL);
1231 assert(image->signature == MagickSignature);
1232 if (image->debug != MagickFalse)
1233 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1234 assert(exception != (ExceptionInfo *) NULL);
1235 assert(exception->signature == MagickSignature);
1236 if (threshold_map == (const char *) NULL)
1240 token[MaxTextExtent];
1245 p=(char *)threshold_map;
1246 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1250 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1252 if ((p-threshold_map) >= (MaxTextExtent-1))
1254 token[p-threshold_map]=(*p);
1257 token[p-threshold_map]='\0';
1258 map=GetThresholdMap(token, exception);
1259 if (map == (ThresholdMap *) NULL)
1261 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1262 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1263 return(MagickFalse);
1266 /* Set channel levels from extra comma separated arguments
1267 Default to 2, the single value given, or individual channel values
1270 { /* parse directly as a comma separated list of integers */
1273 p=strchr((char *) threshold_map,',');
1279 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
1280 levels.black=(unsigned int) strtoul(p, &p, 10);
1284 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1285 levels.red=levels.black;
1286 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1287 levels.green=levels.black;
1288 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1289 levels.blue=levels.black;
1290 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1291 (image->colorspace == CMYKColorspace))
1292 levels.black=levels.black;
1293 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1294 (image->matte != MagickFalse))
1295 levels.alpha=levels.black;
1297 If more than a single number, each channel has a separate value.
1299 if (p != (char *) NULL && *p == ',')
1301 p=strchr((char *) threshold_map,',');
1303 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1304 levels.red=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1305 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1306 levels.green=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1307 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1308 levels.blue=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1309 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
1310 (image->colorspace == CMYKColorspace))
1311 levels.black=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1312 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1313 levels.alpha=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1317 /* Parse level values as a geometry */
1319 * How to map GeometryInfo structure elements into
1320 * PixelLongPacket structure elements, but according to channel?
1321 * Note the channels list may skip elements!!!!
1322 * EG -channel BA -ordered-dither map,2,3
1323 * will need to map g.rho -> l.blue, and g.sigma -> l.alpha
1324 * A simpler way is needed, probably converting geometry to a temporary
1325 * array, then using channel to advance the index into ssize_t pixel packet.
1330 printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1331 levels.red, levels.green, levels.blue, levels.alpha, levels.index);
1334 { /* Do the posterized ordered dithering of the image */
1338 /* d=number of psuedo-level divisions added between color levels */
1341 /* reduce levels to levels - 1 */
1342 levels.red =levels.red ? levels.red-1 : 0;
1343 levels.green =levels.green ? levels.green-1 : 0;
1344 levels.blue =levels.blue ? levels.blue-1 : 0;
1345 levels.black =levels.black ? levels.black-1 : 0;
1346 levels.alpha=levels.alpha ? levels.alpha-1 : 0;
1348 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1349 return(MagickFalse);
1352 image_view=AcquireCacheView(image);
1353 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1354 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1356 for (y=0; y < (ssize_t) image->rows; y++)
1364 if (status == MagickFalse)
1366 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1367 if (q == (Quantum *) NULL)
1372 for (x=0; x < (ssize_t) image->columns; x++)
1380 Figure out the dither threshold for this pixel
1381 This must be a integer from 1 to map->divisor-1
1383 threshold=map->levels[(x%map->width) +map->width*(y%map->height)];
1385 /* Dither each channel in the image as appropriate
1386 Notes on the integer Math...
1387 total number of divisions=(levels-1)*(divisor-1)+1)
1388 t1=this colors psuedo_level =
1389 q->red * total_divisions / (QuantumRange+1)
1390 l=posterization level 0..levels
1391 t=dither threshold level 0..divisor-1 NB: 0 only on last
1392 Each color_level is of size QuantumRange / (levels-1)
1393 NB: All input levels and divisor are already had 1 subtracted
1394 Opacity is inverted so 'off' represents transparent.
1396 if (levels.red != 0) {
1397 t=(ssize_t) (QuantumScale*GetPixelRed(image,q)*(levels.red*d+1));
1399 SetPixelRed(image,RoundToQuantum((MagickRealType)
1400 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)),q);
1402 if (levels.green != 0) {
1403 t=(ssize_t) (QuantumScale*GetPixelGreen(image,q)*
1404 (levels.green*d+1));
1406 SetPixelGreen(image,RoundToQuantum((MagickRealType)
1407 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)),q);
1409 if (levels.blue != 0) {
1410 t=(ssize_t) (QuantumScale*GetPixelBlue(image,q)*
1413 SetPixelBlue(image,RoundToQuantum((MagickRealType)
1414 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)),q);
1416 if (levels.alpha != 0) {
1417 t=(ssize_t) ((1.0-QuantumScale*GetPixelAlpha(image,q))*
1418 (levels.alpha*d+1));
1420 SetPixelAlpha(image,RoundToQuantum((MagickRealType)
1421 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
1424 if (levels.black != 0) {
1425 t=(ssize_t) (QuantumScale*GetPixelBlack(image,q)*
1426 (levels.black*d+1));
1428 SetPixelBlack(image,RoundToQuantum((MagickRealType)
1429 ((l+(t>=threshold))*(MagickRealType) QuantumRange/levels.black)),q);
1431 q+=GetPixelChannels(image);
1433 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1435 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1440 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1441 #pragma omp critical (MagickCore_OrderedPosterizeImage)
1443 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1444 if (proceed == MagickFalse)
1448 image_view=DestroyCacheView(image_view);
1450 map=DestroyThresholdMap(map);
1455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1459 % R a n d o m T h r e s h o l d I m a g e %
1463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465 % RandomThresholdImage() changes the value of individual pixels based on the
1466 % intensity of each pixel compared to a random threshold. The result is a
1467 % low-contrast, two color image.
1469 % The format of the RandomThresholdImage method is:
1471 % MagickBooleanType RandomThresholdImage(Image *image,
1472 % const char *thresholds,ExceptionInfo *exception)
1474 % A description of each parameter follows:
1476 % o image: the image.
1478 % o thresholds: a geometry string containing low,high thresholds. If the
1479 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1480 % is performed instead.
1482 % o exception: return any errors or warnings in this structure.
1485 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1486 const char *thresholds,ExceptionInfo *exception)
1488 #define ThresholdImageTag "Threshold/Image"
1513 **restrict random_info;
1518 assert(image != (Image *) NULL);
1519 assert(image->signature == MagickSignature);
1520 if (image->debug != MagickFalse)
1521 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1522 assert(exception != (ExceptionInfo *) NULL);
1523 assert(exception->signature == MagickSignature);
1524 if (thresholds == (const char *) NULL)
1526 GetPixelInfo(image,&threshold);
1528 max_threshold=(MagickRealType) QuantumRange;
1529 flags=ParseGeometry(thresholds,&geometry_info);
1530 min_threshold=geometry_info.rho;
1531 max_threshold=geometry_info.sigma;
1532 if ((flags & SigmaValue) == 0)
1533 max_threshold=min_threshold;
1534 if (strchr(thresholds,'%') != (char *) NULL)
1536 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1537 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1540 Random threshold image.
1544 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1545 return(MagickFalse);
1546 random_info=AcquireRandomInfoThreadSet();
1547 image_view=AcquireCacheView(image);
1548 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1549 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1551 for (y=0; y < (ssize_t) image->rows; y++)
1554 id = GetOpenMPThreadId();
1562 if (status == MagickFalse)
1564 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1565 if (q == (Quantum *) NULL)
1570 for (x=0; x < (ssize_t) image->columns; x++)
1575 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1584 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1585 if ((traits & UpdatePixelTrait) == 0)
1587 if ((MagickRealType) q[i] < min_threshold)
1588 threshold=min_threshold;
1590 if ((MagickRealType) q[i] > max_threshold)
1591 threshold=max_threshold;
1593 threshold=(MagickRealType) (QuantumRange*
1594 GetPseudoRandomValue(random_info[id]));
1595 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
1598 q+=GetPixelChannels(image);
1600 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1602 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1607 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1608 #pragma omp critical (MagickCore_RandomThresholdImage)
1610 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1612 if (proceed == MagickFalse)
1616 image_view=DestroyCacheView(image_view);
1617 random_info=DestroyRandomInfoThreadSet(random_info);
1622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626 % W h i t e T h r e s h o l d I m a g e %
1630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1633 % the threshold into white while leaving all pixels at or below the threshold
1636 % The format of the WhiteThresholdImage method is:
1638 % MagickBooleanType WhiteThresholdImage(Image *image,
1639 % const char *threshold,ExceptionInfo *exception)
1641 % A description of each parameter follows:
1643 % o image: the image.
1645 % o threshold: Define the threshold value.
1647 % o exception: return any errors or warnings in this structure.
1650 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1651 const char *thresholds,ExceptionInfo *exception)
1653 #define ThresholdImageTag "Threshold/Image"
1679 assert(image != (Image *) NULL);
1680 assert(image->signature == MagickSignature);
1681 if (image->debug != MagickFalse)
1682 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1683 if (thresholds == (const char *) NULL)
1685 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1686 return(MagickFalse);
1687 flags=ParseGeometry(thresholds,&geometry_info);
1688 for (i=0; i < 5; i++)
1689 threshold[i]=geometry_info.rho;
1690 if ((flags & SigmaValue) != 0)
1691 threshold[1]=geometry_info.sigma;
1692 if ((flags & XiValue) != 0)
1693 threshold[2]=geometry_info.xi;
1694 if ((flags & PsiValue) != 0)
1695 threshold[3]=geometry_info.psi;
1696 if ((flags & ChiValue) != 0)
1697 threshold[4]=geometry_info.chi;
1698 if ((flags & PercentValue) != 0)
1699 for (i=0; i < 5; i++)
1700 threshold[i]*=(QuantumRange/100.0);
1702 White threshold image.
1706 image_view=AcquireCacheView(image);
1707 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1708 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1710 for (y=0; y < (ssize_t) image->rows; y++)
1718 if (status == MagickFalse)
1720 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1721 if (q == (Quantum *) NULL)
1726 for (x=0; x < (ssize_t) image->columns; x++)
1735 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1740 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1741 if ((traits & UpdatePixelTrait) == 0)
1743 if ((MagickRealType) q[i] > threshold[n++ % 5])
1746 q+=GetPixelChannels(image);
1748 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1750 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1755 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1756 #pragma omp critical (MagickCore_WhiteThresholdImage)
1758 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1760 if (proceed == MagickFalse)
1764 image_view=DestroyCacheView(image_view);