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-2013 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/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"
93 #define ThresholdsFilename "thresholds.xml"
114 Forward declarations.
117 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
124 % A d a p t i v e T h r e s h o l d I m a g e %
128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
135 % The format of the AdaptiveThresholdImage method is:
137 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
138 % const size_t height,const double bias,ExceptionInfo *exception)
140 % A description of each parameter follows:
142 % o image: the image.
144 % o width: the width of the local neighborhood.
146 % o height: the height of the local neighborhood.
148 % o bias: the mean bias.
150 % o exception: return any errors or warnings in this structure.
153 MagickExport Image *AdaptiveThresholdImage(const Image *image,
154 const size_t width,const size_t height,const double bias,
155 ExceptionInfo *exception)
157 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
179 Initialize threshold image attributes.
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 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=AcquireVirtualCacheView(image,exception);
204 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
205 #if defined(MAGICKCORE_OPENMP_SUPPORT)
206 #pragma omp parallel for schedule(static,4) shared(progress,status) \
207 magick_threads(image,threshold_image,image->rows,1)
209 for (y=0; y < (ssize_t) image->rows; y++)
212 channel_bias[MaxPixelChannels],
213 channel_sum[MaxPixelChannels];
215 register const Quantum
231 if (status == MagickFalse)
233 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
234 (height/2L),image->columns+width,height,exception);
235 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
237 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
242 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
243 GetPixelChannels(image)*(width/2);
244 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
246 PixelChannel channel=GetPixelChannelChannel(image,i);
247 PixelTrait traits=GetPixelChannelTraits(image,channel);
248 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
250 if ((traits == UndefinedPixelTrait) ||
251 (threshold_traits == UndefinedPixelTrait))
253 if (((threshold_traits & CopyPixelTrait) != 0) ||
254 (GetPixelMask(image,p) != 0))
256 SetPixelChannel(threshold_image,channel,p[center+i],q);
260 channel_bias[channel]=0.0;
261 channel_sum[channel]=0.0;
262 for (v=0; v < (ssize_t) height; v++)
264 for (u=0; u < (ssize_t) width; u++)
266 if (u == (ssize_t) (width-1))
267 channel_bias[channel]+=pixels[i];
268 channel_sum[channel]+=pixels[i];
269 pixels+=GetPixelChannels(image);
271 pixels+=image->columns*GetPixelChannels(image);
274 for (x=0; x < (ssize_t) image->columns; x++)
276 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
281 PixelChannel channel=GetPixelChannelChannel(image,i);
282 PixelTrait traits=GetPixelChannelTraits(image,channel);
283 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
285 if ((traits == UndefinedPixelTrait) ||
286 (threshold_traits == UndefinedPixelTrait))
288 if (((threshold_traits & CopyPixelTrait) != 0) ||
289 (GetPixelMask(image,p) != 0))
291 SetPixelChannel(threshold_image,channel,p[center+i],q);
294 channel_sum[channel]-=channel_bias[channel];
295 channel_bias[channel]=0.0;
297 for (v=0; v < (ssize_t) height; v++)
299 channel_bias[channel]+=pixels[i];
300 pixels+=(width-1)*GetPixelChannels(image);
301 channel_sum[channel]+=pixels[i];
302 pixels+=(image->columns+1)*GetPixelChannels(image);
304 mean=(double) (channel_sum[channel]/number_pixels+bias);
305 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
306 p[center+i] <= mean ? 0 : QuantumRange),q);
308 p+=GetPixelChannels(image);
309 q+=GetPixelChannels(threshold_image);
311 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
313 if (image->progress_monitor != (MagickProgressMonitor) NULL)
318 #if defined(MAGICKCORE_OPENMP_SUPPORT)
319 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
321 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
323 if (proceed == MagickFalse)
327 threshold_image->type=image->type;
328 threshold_view=DestroyCacheView(threshold_view);
329 image_view=DestroyCacheView(image_view);
330 if (status == MagickFalse)
331 threshold_image=DestroyImage(threshold_image);
332 return(threshold_image);
336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340 % B i l e v e l I m a g e %
344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346 % BilevelImage() changes the value of individual pixels based on the
347 % intensity of each pixel channel. The result is a high-contrast image.
349 % More precisely each channel value of the image is 'thresholded' so that if
350 % it is equal to or less than the given value it is set to zero, while any
351 % value greater than that give is set to it maximum or QuantumRange.
353 % This function is what is used to implement the "-threshold" operator for
354 % the command line API.
356 % If the default channel setting is given the image is thresholded using just
357 % the gray 'intensity' of the image, rather than the individual channels.
359 % The format of the BilevelImage method is:
361 % MagickBooleanType BilevelImage(Image *image,const double threshold,
362 % ExceptionInfo *exception)
364 % A description of each parameter follows:
366 % o image: the image.
368 % o threshold: define the threshold values.
370 % o exception: return any errors or warnings in this structure.
372 % Aside: You can get the same results as operator using LevelImages()
373 % with the 'threshold' value for both the black_point and the white_point.
376 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
377 ExceptionInfo *exception)
379 #define ThresholdImageTag "Threshold/Image"
393 assert(image != (Image *) NULL);
394 assert(image->signature == MagickSignature);
395 if (image->debug != MagickFalse)
396 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
397 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
399 if (IsGrayColorspace(image->colorspace) != MagickFalse)
400 (void) TransformImageColorspace(image,RGBColorspace,exception);
402 Bilevel threshold image.
406 image_view=AcquireAuthenticCacheView(image,exception);
407 #if defined(MAGICKCORE_OPENMP_SUPPORT)
408 #pragma omp parallel for schedule(static,4) shared(progress,status) \
409 magick_threads(image,image,image->rows,1)
411 for (y=0; y < (ssize_t) image->rows; y++)
419 if (status == MagickFalse)
421 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
422 if (q == (Quantum *) NULL)
427 for (x=0; x < (ssize_t) image->columns; x++)
435 if (GetPixelMask(image,q) != 0)
437 q+=GetPixelChannels(image);
440 pixel=GetPixelIntensity(image,q);
441 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
443 PixelChannel channel=GetPixelChannelChannel(image,i);
444 PixelTrait traits=GetPixelChannelTraits(image,channel);
445 if ((traits & UpdatePixelTrait) == 0)
447 if (image->channel_mask != DefaultChannels)
449 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
451 q+=GetPixelChannels(image);
453 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
455 if (image->progress_monitor != (MagickProgressMonitor) NULL)
460 #if defined(MAGICKCORE_OPENMP_SUPPORT)
461 #pragma omp critical (MagickCore_BilevelImage)
463 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
465 if (proceed == MagickFalse)
469 image_view=DestroyCacheView(image_view);
474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478 % B l a c k T h r e s h o l d I m a g e %
482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
484 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
485 % the threshold into black while leaving all pixels at or above the threshold
488 % The format of the BlackThresholdImage method is:
490 % MagickBooleanType BlackThresholdImage(Image *image,
491 % const char *threshold,ExceptionInfo *exception)
493 % A description of each parameter follows:
495 % o image: the image.
497 % o threshold: define the threshold value.
499 % o exception: return any errors or warnings in this structure.
502 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
503 const char *thresholds,ExceptionInfo *exception)
505 #define ThresholdImageTag "Threshold/Image"
528 assert(image != (Image *) NULL);
529 assert(image->signature == MagickSignature);
530 if (image->debug != MagickFalse)
531 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
532 if (thresholds == (const char *) NULL)
534 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
536 if (IsGrayColorspace(image->colorspace) != MagickFalse)
537 (void) TransformImageColorspace(image,RGBColorspace,exception);
538 GetPixelInfo(image,&threshold);
539 flags=ParseGeometry(thresholds,&geometry_info);
540 threshold.red=geometry_info.rho;
541 threshold.green=geometry_info.rho;
542 threshold.blue=geometry_info.rho;
543 threshold.black=geometry_info.rho;
544 threshold.alpha=100.0;
545 if ((flags & SigmaValue) != 0)
546 threshold.green=geometry_info.sigma;
547 if ((flags & XiValue) != 0)
548 threshold.blue=geometry_info.xi;
549 if ((flags & PsiValue) != 0)
550 threshold.alpha=geometry_info.psi;
551 if (threshold.colorspace == CMYKColorspace)
553 if ((flags & PsiValue) != 0)
554 threshold.black=geometry_info.psi;
555 if ((flags & ChiValue) != 0)
556 threshold.alpha=geometry_info.chi;
558 if ((flags & PercentValue) != 0)
560 threshold.red*=(MagickRealType) (QuantumRange/100.0);
561 threshold.green*=(MagickRealType) (QuantumRange/100.0);
562 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
563 threshold.black*=(MagickRealType) (QuantumRange/100.0);
564 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
567 White threshold image.
571 image_view=AcquireAuthenticCacheView(image,exception);
572 #if defined(MAGICKCORE_OPENMP_SUPPORT)
573 #pragma omp parallel for schedule(static,4) shared(progress,status) \
574 magick_threads(image,image,image->rows,1)
576 for (y=0; y < (ssize_t) image->rows; y++)
584 if (status == MagickFalse)
586 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
587 if (q == (Quantum *) NULL)
592 for (x=0; x < (ssize_t) image->columns; x++)
600 if (GetPixelMask(image,q) != 0)
602 q+=GetPixelChannels(image);
605 pixel=GetPixelIntensity(image,q);
606 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
608 PixelChannel channel=GetPixelChannelChannel(image,i);
609 PixelTrait traits=GetPixelChannelTraits(image,channel);
610 if ((traits & UpdatePixelTrait) == 0)
612 if (image->channel_mask != DefaultChannels)
614 if (pixel <= GetPixelInfoChannel(&threshold,channel))
617 q+=GetPixelChannels(image);
619 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
621 if (image->progress_monitor != (MagickProgressMonitor) NULL)
626 #if defined(MAGICKCORE_OPENMP_SUPPORT)
627 #pragma omp critical (MagickCore_BlackThresholdImage)
629 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
631 if (proceed == MagickFalse)
635 image_view=DestroyCacheView(image_view);
640 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
644 % C l a m p I m a g e %
648 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
650 % ClampImage() set each pixel whose value is below zero to zero and any the
651 % pixel whose value is above the quantum range to the quantum range (e.g.
652 % 65535) otherwise the pixel value remains unchanged.
654 % The format of the ClampImage method is:
656 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
658 % A description of each parameter follows:
660 % o image: the image.
662 % o exception: return any errors or warnings in this structure.
666 static inline Quantum ClampPixel(const MagickRealType value)
668 #if !defined(MAGICKCORE_HDRI_SUPPORT)
669 return((Quantum) value);
673 if (value >= (MagickRealType) QuantumRange)
674 return((Quantum) QuantumRange);
679 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
681 #define ClampImageTag "Clamp/Image"
695 assert(image != (Image *) NULL);
696 assert(image->signature == MagickSignature);
697 if (image->debug != MagickFalse)
698 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
699 if (image->storage_class == PseudoClass)
708 for (i=0; i < (ssize_t) image->colors; i++)
710 q->red=(double) ClampPixel(q->red);
711 q->green=(double) ClampPixel(q->green);
712 q->blue=(double) ClampPixel(q->blue);
713 q->alpha=(double) ClampPixel(q->alpha);
716 return(SyncImage(image,exception));
723 image_view=AcquireAuthenticCacheView(image,exception);
724 #if defined(MAGICKCORE_OPENMP_SUPPORT)
725 #pragma omp parallel for schedule(static,4) shared(progress,status) \
726 magick_threads(image,image,image->rows,1)
728 for (y=0; y < (ssize_t) image->rows; y++)
736 if (status == MagickFalse)
738 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
739 if (q == (Quantum *) NULL)
744 for (x=0; x < (ssize_t) image->columns; x++)
749 if (GetPixelMask(image,q) != 0)
751 q+=GetPixelChannels(image);
754 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
756 PixelChannel channel=GetPixelChannelChannel(image,i);
757 PixelTrait traits=GetPixelChannelTraits(image,channel);
758 if (traits == UndefinedPixelTrait)
760 q[i]=ClampPixel(q[i]);
762 q+=GetPixelChannels(image);
764 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
766 if (image->progress_monitor != (MagickProgressMonitor) NULL)
771 #if defined(MAGICKCORE_OPENMP_SUPPORT)
772 #pragma omp critical (MagickCore_ClampImage)
774 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
775 if (proceed == MagickFalse)
779 image_view=DestroyCacheView(image_view);
784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788 % D e s t r o y T h r e s h o l d M a p %
792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
794 % DestroyThresholdMap() de-allocate the given ThresholdMap
796 % The format of the ListThresholdMaps method is:
798 % ThresholdMap *DestroyThresholdMap(Threshold *map)
800 % A description of each parameter follows.
802 % o map: Pointer to the Threshold map to destroy
805 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
807 assert(map != (ThresholdMap *) NULL);
808 if (map->map_id != (char *) NULL)
809 map->map_id=DestroyString(map->map_id);
810 if (map->description != (char *) NULL)
811 map->description=DestroyString(map->description);
812 if (map->levels != (ssize_t *) NULL)
813 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
814 map=(ThresholdMap *) RelinquishMagickMemory(map);
819 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
823 % G e t T h r e s h o l d M a p %
827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
829 % GetThresholdMap() loads and searches one or more threshold map files for the
830 % map matching the given name or alias.
832 % The format of the GetThresholdMap method is:
834 % ThresholdMap *GetThresholdMap(const char *map_id,
835 % ExceptionInfo *exception)
837 % A description of each parameter follows.
839 % o map_id: ID of the map to look for.
841 % o exception: return any errors or warnings in this structure.
844 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
845 ExceptionInfo *exception)
856 map=(ThresholdMap *)NULL;
857 options=GetConfigureOptions(ThresholdsFilename,exception);
858 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
859 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
860 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
861 GetStringInfoPath(option),map_id,exception);
862 options=DestroyConfigureOptions(options);
867 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
871 + G e t T h r e s h o l d M a p F i l e %
875 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
877 % GetThresholdMapFile() look for a given threshold map name or alias in the
878 % given XML file data, and return the allocated the map when found.
880 % The format of the ListThresholdMaps method is:
882 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
883 % const char *map_id,ExceptionInfo *exception)
885 % A description of each parameter follows.
887 % o xml: The threshold map list in XML format.
889 % o filename: The threshold map XML filename.
891 % o map_id: ID of the map to look for in XML list.
893 % o exception: return any errors or warnings in this structure.
896 static ThresholdMap *GetThresholdMapFile(const char *xml,
897 const char *filename,const char *map_id,ExceptionInfo *exception)
921 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
922 "Loading threshold map file \"%s\" ...",filename);
923 map=(ThresholdMap *) NULL;
924 thresholds=NewXMLTree(xml,exception);
925 if (thresholds == (XMLTreeInfo *) NULL)
927 for (threshold=GetXMLTreeChild(thresholds,"threshold");
928 threshold != (XMLTreeInfo *) NULL;
929 threshold=GetNextXMLTreeTag(threshold))
931 attribute=GetXMLTreeAttribute(threshold,"map");
932 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
934 attribute=GetXMLTreeAttribute(threshold,"alias");
935 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
938 if (threshold == (XMLTreeInfo *) NULL)
940 description=GetXMLTreeChild(threshold,"description");
941 if (description == (XMLTreeInfo *) NULL)
943 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
944 "XmlMissingElement", "<description>, map \"%s\"",map_id);
945 thresholds=DestroyXMLTree(thresholds);
948 levels=GetXMLTreeChild(threshold,"levels");
949 if (levels == (XMLTreeInfo *) NULL)
951 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
952 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
953 thresholds=DestroyXMLTree(thresholds);
956 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
957 if (map == (ThresholdMap *) NULL)
958 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
959 map->map_id=(char *) NULL;
960 map->description=(char *) NULL;
961 map->levels=(ssize_t *) NULL;
962 attribute=GetXMLTreeAttribute(threshold,"map");
963 if (attribute != (char *) NULL)
964 map->map_id=ConstantString(attribute);
965 content=GetXMLTreeContent(description);
966 if (content != (char *) NULL)
967 map->description=ConstantString(content);
968 attribute=GetXMLTreeAttribute(levels,"width");
969 if (attribute == (char *) NULL)
971 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
972 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
973 thresholds=DestroyXMLTree(thresholds);
974 map=DestroyThresholdMap(map);
977 map->width=StringToUnsignedLong(attribute);
980 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
981 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
982 thresholds=DestroyXMLTree(thresholds);
983 map=DestroyThresholdMap(map);
986 attribute=GetXMLTreeAttribute(levels,"height");
987 if (attribute == (char *) NULL)
989 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
990 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
991 thresholds=DestroyXMLTree(thresholds);
992 map=DestroyThresholdMap(map);
995 map->height=StringToUnsignedLong(attribute);
996 if (map->height == 0)
998 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
999 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1000 thresholds=DestroyXMLTree(thresholds);
1001 map=DestroyThresholdMap(map);
1004 attribute=GetXMLTreeAttribute(levels,"divisor");
1005 if (attribute == (char *) NULL)
1007 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1008 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1009 thresholds=DestroyXMLTree(thresholds);
1010 map=DestroyThresholdMap(map);
1013 map->divisor=(ssize_t) StringToLong(attribute);
1014 if (map->divisor < 2)
1016 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1017 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1018 thresholds=DestroyXMLTree(thresholds);
1019 map=DestroyThresholdMap(map);
1022 content=GetXMLTreeContent(levels);
1023 if (content == (char *) NULL)
1025 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1026 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1027 thresholds=DestroyXMLTree(thresholds);
1028 map=DestroyThresholdMap(map);
1031 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1032 sizeof(*map->levels));
1033 if (map->levels == (ssize_t *) NULL)
1034 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1035 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1037 map->levels[i]=(ssize_t) strtol(content,&p,10);
1040 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1041 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1042 thresholds=DestroyXMLTree(thresholds);
1043 map=DestroyThresholdMap(map);
1046 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1048 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1049 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1050 (double) map->levels[i],map_id);
1051 thresholds=DestroyXMLTree(thresholds);
1052 map=DestroyThresholdMap(map);
1057 value=(double) strtol(content,&p,10);
1061 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1062 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1063 thresholds=DestroyXMLTree(thresholds);
1064 map=DestroyThresholdMap(map);
1067 thresholds=DestroyXMLTree(thresholds);
1072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1076 + L i s t T h r e s h o l d M a p F i l e %
1080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1082 % ListThresholdMapFile() lists the threshold maps and their descriptions
1083 % in the given XML file data.
1085 % The format of the ListThresholdMaps method is:
1087 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1088 % const char *filename,ExceptionInfo *exception)
1090 % A description of each parameter follows.
1092 % o file: An pointer to the output FILE.
1094 % o xml: The threshold map list in XML format.
1096 % o filename: The threshold map XML filename.
1098 % o exception: return any errors or warnings in this structure.
1101 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1102 const char *filename,ExceptionInfo *exception)
1114 assert( xml != (char *)NULL );
1115 assert( file != (FILE *)NULL );
1116 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1117 "Loading threshold map file \"%s\" ...",filename);
1118 thresholds=NewXMLTree(xml,exception);
1119 if ( thresholds == (XMLTreeInfo *)NULL )
1120 return(MagickFalse);
1121 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1122 (void) FormatLocaleFile(file,
1123 "----------------------------------------------------\n");
1124 threshold=GetXMLTreeChild(thresholds,"threshold");
1125 for ( ; threshold != (XMLTreeInfo *) NULL;
1126 threshold=GetNextXMLTreeTag(threshold))
1128 map=GetXMLTreeAttribute(threshold,"map");
1129 if (map == (char *) NULL)
1131 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1132 "XmlMissingAttribute", "<map>");
1133 thresholds=DestroyXMLTree(thresholds);
1134 return(MagickFalse);
1136 alias=GetXMLTreeAttribute(threshold,"alias");
1137 description=GetXMLTreeChild(threshold,"description");
1138 if (description == (XMLTreeInfo *) NULL)
1140 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1141 "XmlMissingElement", "<description>, map \"%s\"",map);
1142 thresholds=DestroyXMLTree(thresholds);
1143 return(MagickFalse);
1145 content=GetXMLTreeContent(description);
1146 if (content == (char *) NULL)
1148 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1149 "XmlMissingContent", "<description>, map \"%s\"", map);
1150 thresholds=DestroyXMLTree(thresholds);
1151 return(MagickFalse);
1153 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1156 thresholds=DestroyXMLTree(thresholds);
1161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1165 % L i s t T h r e s h o l d M a p s %
1169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171 % ListThresholdMaps() lists the threshold maps and their descriptions
1172 % as defined by "threshold.xml" to a file.
1174 % The format of the ListThresholdMaps method is:
1176 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1178 % A description of each parameter follows.
1180 % o file: An pointer to the output FILE.
1182 % o exception: return any errors or warnings in this structure.
1185 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1186 ExceptionInfo *exception)
1198 if (file == (FILE *) NULL)
1200 options=GetConfigureOptions(ThresholdsFilename,exception);
1201 (void) FormatLocaleFile(file,
1202 "\n Threshold Maps for Ordered Dither Operations\n");
1203 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1204 (const StringInfo *) NULL)
1206 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1207 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1208 GetStringInfoPath(option),exception);
1210 options=DestroyConfigureOptions(options);
1211 return(status != 0 ? MagickTrue : MagickFalse);
1215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1219 % O r d e r e d P o s t e r i z e I m a g e %
1223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1225 % OrderedPosterizeImage() will perform a ordered dither based on a number
1226 % of pre-defined dithering threshold maps, but over multiple intensity
1227 % levels, which can be different for different channels, according to the
1230 % The format of the OrderedPosterizeImage method is:
1232 % MagickBooleanType OrderedPosterizeImage(Image *image,
1233 % const char *threshold_map,ExceptionInfo *exception)
1235 % A description of each parameter follows:
1237 % o image: the image.
1239 % o threshold_map: A string containing the name of the threshold dither
1240 % map to use, followed by zero or more numbers representing the number
1241 % of color levels tho dither between.
1243 % Any level number less than 2 will be equivalent to 2, and means only
1244 % binary dithering will be applied to each color channel.
1246 % No numbers also means a 2 level (bitmap) dither will be applied to all
1247 % channels, while a single number is the number of levels applied to each
1248 % channel in sequence. More numbers will be applied in turn to each of
1249 % the color channels.
1251 % For example: "o3x3,6" will generate a 6 level posterization of the
1252 % image with a ordered 3x3 diffused pixel dither being applied between
1253 % each level. While checker,8,8,4 will produce a 332 colormaped image
1254 % with only a single checkerboard hash pattern (50% grey) between each
1255 % color level, to basically double the number of color levels with
1256 % a bare minimim of dithering.
1258 % o exception: return any errors or warnings in this structure.
1261 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1262 const char *threshold_map,ExceptionInfo *exception)
1264 #define DitherImageTag "Dither/Image"
1270 token[MaxTextExtent];
1282 levels[CompositePixelChannel];
1293 assert(image != (Image *) NULL);
1294 assert(image->signature == MagickSignature);
1295 if (image->debug != MagickFalse)
1296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1297 assert(exception != (ExceptionInfo *) NULL);
1298 assert(exception->signature == MagickSignature);
1299 if (threshold_map == (const char *) NULL)
1301 p=(char *) threshold_map;
1302 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1306 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1309 if ((p-threshold_map) >= (MaxTextExtent-1))
1311 token[p-threshold_map]=(*p);
1314 token[p-threshold_map]='\0';
1315 map=GetThresholdMap(token,exception);
1316 if (map == (ThresholdMap *) NULL)
1318 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1319 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1320 return(MagickFalse);
1322 for (i=0; i < MaxPixelChannels; i++)
1324 p=strchr((char *) threshold_map,',');
1325 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1326 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1328 GetMagickToken(p,&p,token);
1330 GetMagickToken(p,&p,token);
1331 levels[i]=StringToDouble(token,(char **) NULL);
1333 for (i=0; i < MaxPixelChannels; i++)
1334 if (fabs(levels[i]) >= 1)
1336 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1337 return(MagickFalse);
1340 image_view=AcquireAuthenticCacheView(image,exception);
1341 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1342 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1343 magick_threads(image,image,image->rows,1)
1345 for (y=0; y < (ssize_t) image->rows; y++)
1353 if (status == MagickFalse)
1355 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1356 if (q == (Quantum *) NULL)
1361 for (x=0; x < (ssize_t) image->columns; x++)
1370 if (GetPixelMask(image,q) != 0)
1372 q+=GetPixelChannels(image);
1375 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1381 PixelChannel channel=GetPixelChannelChannel(image,i);
1382 PixelTrait traits=GetPixelChannelTraits(image,channel);
1383 if ((traits & UpdatePixelTrait) == 0)
1385 if (fabs(levels[n++]) < MagickEpsilon)
1387 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1388 level=threshold/(map->divisor-1);
1389 threshold-=level*(map->divisor-1);
1390 q[i]=ClampToQuantum((double) (level+(threshold >=
1391 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1392 QuantumRange/levels[n]);
1395 q+=GetPixelChannels(image);
1397 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1399 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1404 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1405 #pragma omp critical (MagickCore_OrderedPosterizeImage)
1407 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1408 if (proceed == MagickFalse)
1412 image_view=DestroyCacheView(image_view);
1413 map=DestroyThresholdMap(map);
1418 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422 % P e r c e p t i b l e I m a g e %
1426 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1428 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1429 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1432 % The format of the PerceptibleImage method is:
1434 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1435 % ExceptionInfo *exception)
1437 % A description of each parameter follows:
1439 % o image: the image.
1441 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1443 % o exception: return any errors or warnings in this structure.
1447 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1448 const double epsilon)
1453 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1454 if ((sign*quantum) >= epsilon)
1456 return((Quantum) (sign*epsilon));
1459 MagickExport MagickBooleanType PerceptibleImage(Image *image,
1460 const double epsilon,ExceptionInfo *exception)
1462 #define PerceptibleImageTag "Perceptible/Image"
1476 assert(image != (Image *) NULL);
1477 assert(image->signature == MagickSignature);
1478 if (image->debug != MagickFalse)
1479 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1480 if (image->storage_class == PseudoClass)
1489 for (i=0; i < (ssize_t) image->colors; i++)
1491 q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1493 q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1495 q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1497 q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1501 return(SyncImage(image,exception));
1508 image_view=AcquireAuthenticCacheView(image,exception);
1509 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1510 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1511 magick_threads(image,image,image->rows,1)
1513 for (y=0; y < (ssize_t) image->rows; y++)
1521 if (status == MagickFalse)
1523 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1524 if (q == (Quantum *) NULL)
1529 for (x=0; x < (ssize_t) image->columns; x++)
1534 if (GetPixelMask(image,q) != 0)
1536 q+=GetPixelChannels(image);
1539 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1541 PixelChannel channel=GetPixelChannelChannel(image,i);
1542 PixelTrait traits=GetPixelChannelTraits(image,channel);
1543 if (traits == UndefinedPixelTrait)
1545 q[i]=PerceptibleThreshold(q[i],epsilon);
1547 q+=GetPixelChannels(image);
1549 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1551 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1556 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1557 #pragma omp critical (MagickCore_PerceptibleImage)
1559 proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1560 if (proceed == MagickFalse)
1564 image_view=DestroyCacheView(image_view);
1569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1573 % R a n d o m T h r e s h o l d I m a g e %
1577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1579 % RandomThresholdImage() changes the value of individual pixels based on the
1580 % intensity of each pixel compared to a random threshold. The result is a
1581 % low-contrast, two color image.
1583 % The format of the RandomThresholdImage method is:
1585 % MagickBooleanType RandomThresholdImage(Image *image,
1586 % const char *thresholds,ExceptionInfo *exception)
1588 % A description of each parameter follows:
1590 % o image: the image.
1592 % o thresholds: a geometry string containing low,high thresholds. If the
1593 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1594 % is performed instead.
1596 % o exception: return any errors or warnings in this structure.
1599 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1600 const char *thresholds,ExceptionInfo *exception)
1602 #define ThresholdImageTag "Threshold/Image"
1627 **restrict random_info;
1632 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1637 assert(image != (Image *) NULL);
1638 assert(image->signature == MagickSignature);
1639 if (image->debug != MagickFalse)
1640 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1641 assert(exception != (ExceptionInfo *) NULL);
1642 assert(exception->signature == MagickSignature);
1643 if (thresholds == (const char *) NULL)
1645 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1646 return(MagickFalse);
1647 GetPixelInfo(image,&threshold);
1649 max_threshold=(double) QuantumRange;
1650 flags=ParseGeometry(thresholds,&geometry_info);
1651 min_threshold=geometry_info.rho;
1652 max_threshold=geometry_info.sigma;
1653 if ((flags & SigmaValue) == 0)
1654 max_threshold=min_threshold;
1655 if (strchr(thresholds,'%') != (char *) NULL)
1657 max_threshold*=(double) (0.01*QuantumRange);
1658 min_threshold*=(double) (0.01*QuantumRange);
1661 Random threshold image.
1665 random_info=AcquireRandomInfoThreadSet();
1666 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1667 key=GetRandomSecretKey(random_info[0]);
1669 image_view=AcquireAuthenticCacheView(image,exception);
1670 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1671 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1672 magick_threads(image,image,image->rows,key == ~0UL)
1674 for (y=0; y < (ssize_t) image->rows; y++)
1677 id = GetOpenMPThreadId();
1685 if (status == MagickFalse)
1687 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1688 if (q == (Quantum *) NULL)
1693 for (x=0; x < (ssize_t) image->columns; x++)
1698 if (GetPixelMask(image,q) != 0)
1700 q+=GetPixelChannels(image);
1703 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1708 PixelChannel channel=GetPixelChannelChannel(image,i);
1709 PixelTrait traits=GetPixelChannelTraits(image,channel);
1710 if ((traits & UpdatePixelTrait) == 0)
1712 if ((double) q[i] < min_threshold)
1713 threshold=min_threshold;
1715 if ((double) q[i] > max_threshold)
1716 threshold=max_threshold;
1718 threshold=(double) (QuantumRange*
1719 GetPseudoRandomValue(random_info[id]));
1720 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
1722 q+=GetPixelChannels(image);
1724 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1726 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1731 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1732 #pragma omp critical (MagickCore_RandomThresholdImage)
1734 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1736 if (proceed == MagickFalse)
1740 image_view=DestroyCacheView(image_view);
1741 random_info=DestroyRandomInfoThreadSet(random_info);
1746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1750 % W h i t e T h r e s h o l d I m a g e %
1754 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1756 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1757 % the threshold into white while leaving all pixels at or below the threshold
1760 % The format of the WhiteThresholdImage method is:
1762 % MagickBooleanType WhiteThresholdImage(Image *image,
1763 % const char *threshold,ExceptionInfo *exception)
1765 % A description of each parameter follows:
1767 % o image: the image.
1769 % o threshold: Define the threshold value.
1771 % o exception: return any errors or warnings in this structure.
1774 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1775 const char *thresholds,ExceptionInfo *exception)
1777 #define ThresholdImageTag "Threshold/Image"
1800 assert(image != (Image *) NULL);
1801 assert(image->signature == MagickSignature);
1802 if (image->debug != MagickFalse)
1803 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1804 if (thresholds == (const char *) NULL)
1806 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1807 return(MagickFalse);
1808 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1809 (void) TransformImageColorspace(image,RGBColorspace,exception);
1810 GetPixelInfo(image,&threshold);
1811 flags=ParseGeometry(thresholds,&geometry_info);
1812 threshold.red=geometry_info.rho;
1813 threshold.green=geometry_info.rho;
1814 threshold.blue=geometry_info.rho;
1815 threshold.black=geometry_info.rho;
1816 threshold.alpha=100.0;
1817 if ((flags & SigmaValue) != 0)
1818 threshold.green=geometry_info.sigma;
1819 if ((flags & XiValue) != 0)
1820 threshold.blue=geometry_info.xi;
1821 if ((flags & PsiValue) != 0)
1822 threshold.alpha=geometry_info.psi;
1823 if (threshold.colorspace == CMYKColorspace)
1825 if ((flags & PsiValue) != 0)
1826 threshold.black=geometry_info.psi;
1827 if ((flags & ChiValue) != 0)
1828 threshold.alpha=geometry_info.chi;
1830 if ((flags & PercentValue) != 0)
1832 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1833 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1834 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1835 threshold.black*=(MagickRealType) (QuantumRange/100.0);
1836 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
1839 White threshold image.
1843 image_view=AcquireAuthenticCacheView(image,exception);
1844 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1845 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1846 magick_threads(image,image,image->rows,1)
1848 for (y=0; y < (ssize_t) image->rows; y++)
1856 if (status == MagickFalse)
1858 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1859 if (q == (Quantum *) NULL)
1864 for (x=0; x < (ssize_t) image->columns; x++)
1872 if (GetPixelMask(image,q) != 0)
1874 q+=GetPixelChannels(image);
1877 pixel=GetPixelIntensity(image,q);
1878 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1880 PixelChannel channel=GetPixelChannelChannel(image,i);
1881 PixelTrait traits=GetPixelChannelTraits(image,channel);
1882 if ((traits & UpdatePixelTrait) == 0)
1884 if (image->channel_mask != DefaultChannels)
1885 pixel=(double) q[i];
1886 if (pixel > GetPixelInfoChannel(&threshold,channel))
1889 q+=GetPixelChannels(image);
1891 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1893 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1898 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1899 #pragma omp critical (MagickCore_WhiteThresholdImage)
1901 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1903 if (proceed == MagickFalse)
1907 image_view=DestroyCacheView(image_view);