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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116 % A d a p t i v e T h r e s h o l d I m a g e %
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 % AdaptiveThresholdImage() selects an individual threshold for each pixel
123 % based on the range of intensity values in its local neighborhood. This
124 % allows for thresholding of an image whose global intensity histogram
125 % doesn't contain distinctive peaks.
127 % The format of the AdaptiveThresholdImage method is:
129 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
130 % const size_t height,const double bias,ExceptionInfo *exception)
132 % A description of each parameter follows:
134 % o image: the image.
136 % o width: the width of the local neighborhood.
138 % o height: the height of the local neighborhood.
140 % o bias: the mean bias.
142 % o exception: return any errors or warnings in this structure.
145 MagickExport Image *AdaptiveThresholdImage(const Image *image,
146 const size_t width,const size_t height,const double bias,
147 ExceptionInfo *exception)
149 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
171 Initialize threshold image attributes.
173 assert(image != (Image *) NULL);
174 assert(image->signature == MagickSignature);
175 if (image->debug != MagickFalse)
176 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
177 assert(exception != (ExceptionInfo *) NULL);
178 assert(exception->signature == MagickSignature);
179 if ((width % 2) == 0)
180 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
181 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
183 if (threshold_image == (Image *) NULL)
184 return((Image *) NULL);
185 if (SetImageStorageClass(threshold_image,DirectClass,exception) == MagickFalse)
187 threshold_image=DestroyImage(threshold_image);
188 return((Image *) NULL);
195 number_pixels=(MagickSizeType) width*height;
196 image_view=AcquireCacheView(image);
197 threshold_view=AcquireCacheView(threshold_image);
198 #if defined(MAGICKCORE_OPENMP_SUPPORT)
199 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
201 for (y=0; y < (ssize_t) image->rows; y++)
203 register const Quantum
215 if (status == MagickFalse)
217 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
218 (height/2L),image->columns+width,height,exception);
219 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
221 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
226 center=GetPixelChannels(image)*(image->columns+width)*(height/2L)+
227 GetPixelChannels(image)*(width/2);
228 for (x=0; x < (ssize_t) image->columns; x++)
233 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
246 register const Quantum
255 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
256 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
257 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
258 if ((traits == UndefinedPixelTrait) ||
259 (threshold_traits == UndefinedPixelTrait))
261 if ((threshold_traits & CopyPixelTrait) != 0)
263 q[channel]=p[center+i];
268 for (v=0; v < (ssize_t) height; v++)
270 for (u=0; u < (ssize_t) width; u++)
273 pixels+=GetPixelChannels(image);
275 pixels+=image->columns*GetPixelChannels(image);
277 mean=pixel/number_pixels+bias;
278 q[channel]=(Quantum) (((MagickRealType) p[center+i] <= mean) ? 0 :
281 p+=GetPixelChannels(image);
282 q+=GetPixelChannels(threshold_image);
284 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
286 if (image->progress_monitor != (MagickProgressMonitor) NULL)
291 #if defined(MAGICKCORE_OPENMP_SUPPORT)
292 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
294 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
296 if (proceed == MagickFalse)
300 threshold_image->type=image->type;
301 threshold_view=DestroyCacheView(threshold_view);
302 image_view=DestroyCacheView(image_view);
303 if (status == MagickFalse)
304 threshold_image=DestroyImage(threshold_image);
305 return(threshold_image);
309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313 % B i l e v e l I m a g e %
317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319 % BilevelImage() changes the value of individual pixels based on the
320 % intensity of each pixel channel. The result is a high-contrast image.
322 % More precisely each channel value of the image is 'thresholded' so that if
323 % it is equal to or less than the given value it is set to zero, while any
324 % value greater than that give is set to it maximum or QuantumRange.
326 % This function is what is used to implement the "-threshold" operator for
327 % the command line API.
329 % If the default channel setting is given the image is thresholded using just
330 % the gray 'intensity' of the image, rather than the individual channels.
332 % The format of the BilevelImage method is:
334 % MagickBooleanType BilevelImage(Image *image,const double threshold)
336 % A description of each parameter follows:
338 % o image: the image.
340 % o threshold: define the threshold values.
342 % Aside: You can get the same results as operator using LevelImages()
343 % with the 'threshold' value for both the black_point and the white_point.
346 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
348 #define ThresholdImageTag "Threshold/Image"
365 assert(image != (Image *) NULL);
366 assert(image->signature == MagickSignature);
367 if (image->debug != MagickFalse)
368 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
369 exception=(&image->exception);
370 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
373 Bilevel threshold image.
377 image_view=AcquireCacheView(image);
378 #if defined(MAGICKCORE_OPENMP_SUPPORT)
379 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
381 for (y=0; y < (ssize_t) image->rows; y++)
389 if (status == MagickFalse)
391 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
392 if (q == (Quantum *) NULL)
397 for (x=0; x < (ssize_t) image->columns; x++)
402 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
407 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
408 if ((traits & UpdatePixelTrait) != 0)
409 q[i]=q[i] <= threshold ? 0 : QuantumRange;
411 q+=GetPixelChannels(image);
413 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
415 if (image->progress_monitor != (MagickProgressMonitor) NULL)
420 #if defined(MAGICKCORE_OPENMP_SUPPORT)
421 #pragma omp critical (MagickCore_BilevelImage)
423 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
425 if (proceed == MagickFalse)
429 image_view=DestroyCacheView(image_view);
434 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438 % B l a c k T h r e s h o l d I m a g e %
442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
445 % the threshold into black while leaving all pixels at or above the threshold
448 % The format of the BlackThresholdImage method is:
450 % MagickBooleanType BlackThresholdImage(Image *image,
451 % const char *threshold,ExceptionInfo *exception)
453 % A description of each parameter follows:
455 % o image: the image.
457 % o threshold: Define the threshold value.
459 % o exception: return any errors or warnings in this structure.
462 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
463 const char *thresholds,ExceptionInfo *exception)
465 #define ThresholdImageTag "Threshold/Image"
488 assert(image != (Image *) NULL);
489 assert(image->signature == MagickSignature);
490 if (image->debug != MagickFalse)
491 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
492 if (thresholds == (const char *) NULL)
494 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
496 GetPixelInfo(image,&threshold);
497 flags=ParseGeometry(thresholds,&geometry_info);
498 threshold.red=geometry_info.rho;
499 threshold.green=geometry_info.sigma;
500 if ((flags & SigmaValue) == 0)
501 threshold.green=threshold.red;
502 threshold.blue=geometry_info.xi;
503 if ((flags & XiValue) == 0)
504 threshold.blue=threshold.red;
505 threshold.alpha=geometry_info.psi;
506 if ((flags & PsiValue) == 0)
507 threshold.alpha=threshold.red;
508 threshold.black=geometry_info.chi;
509 if ((flags & ChiValue) == 0)
510 threshold.black=threshold.red;
511 if ((flags & PercentValue) != 0)
513 threshold.red*=(QuantumRange/100.0);
514 threshold.green*=(QuantumRange/100.0);
515 threshold.blue*=(QuantumRange/100.0);
516 threshold.alpha*=(QuantumRange/100.0);
517 threshold.black*=(QuantumRange/100.0);
520 Black threshold image.
524 image_view=AcquireCacheView(image);
525 #if defined(MAGICKCORE_OPENMP_SUPPORT)
526 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
528 for (y=0; y < (ssize_t) image->rows; y++)
536 if (status == MagickFalse)
538 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
539 if (q == (Quantum *) NULL)
544 for (x=0; x < (ssize_t) image->columns; x++)
546 if (image->sync != MagickFalse)
548 if (GetPixelIntensity(image,q) < GetPixelInfoIntensity(&threshold))
550 SetPixelRed(image,0,q);
551 SetPixelGreen(image,0,q);
552 SetPixelBlue(image,0,q);
553 if (image->colorspace == CMYKColorspace)
554 SetPixelBlack(image,0,q);
559 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
560 ((MagickRealType) GetPixelRed(image,q) < threshold.red))
561 SetPixelRed(image,0,q);
562 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
563 ((MagickRealType) GetPixelGreen(image,q) < threshold.green))
564 SetPixelGreen(image,0,q);
565 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
566 ((MagickRealType) GetPixelBlue(image,q) < threshold.blue))
567 SetPixelBlue(image,0,q);
568 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
569 (image->colorspace == CMYKColorspace) &&
570 ((MagickRealType) GetPixelBlack(image,q) < threshold.black))
571 SetPixelBlack(image,0,q);
572 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
573 ((MagickRealType) GetPixelAlpha(image,q) < threshold.alpha))
574 SetPixelAlpha(image,0,q);
576 q+=GetPixelChannels(image);
578 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
580 if (image->progress_monitor != (MagickProgressMonitor) NULL)
585 #if defined(MAGICKCORE_OPENMP_SUPPORT)
586 #pragma omp critical (MagickCore_BlackThresholdImage)
588 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
590 if (proceed == MagickFalse)
594 image_view=DestroyCacheView(image_view);
599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
603 % C l a m p I m a g e %
607 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609 % ClampImage() restricts the color range from 0 to the quantum depth.
611 % The format of the ClampImage method is:
613 % MagickBooleanType ClampImage(Image *image)
615 % A description of each parameter follows:
617 % o image: the image.
621 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
623 #if defined(MAGICKCORE_HDRI_SUPPORT)
626 if (quantum >= QuantumRange)
627 return(QuantumRange);
634 MagickExport MagickBooleanType ClampImage(Image *image)
636 #define ClampImageTag "Clamp/Image"
653 assert(image != (Image *) NULL);
654 assert(image->signature == MagickSignature);
655 if (image->debug != MagickFalse)
656 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
657 if (image->storage_class == PseudoClass)
666 for (i=0; i < (ssize_t) image->colors; i++)
668 q->red=ClampToUnsignedQuantum(q->red);
669 q->green=ClampToUnsignedQuantum(q->green);
670 q->blue=ClampToUnsignedQuantum(q->blue);
671 q->alpha=ClampToUnsignedQuantum(q->alpha);
674 return(SyncImage(image));
681 exception=(&image->exception);
682 image_view=AcquireCacheView(image);
683 #if defined(MAGICKCORE_OPENMP_SUPPORT)
684 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
686 for (y=0; y < (ssize_t) image->rows; y++)
694 if (status == MagickFalse)
696 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
697 if (q == (Quantum *) NULL)
702 for (x=0; x < (ssize_t) image->columns; x++)
704 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
705 SetPixelRed(image,ClampToUnsignedQuantum(GetPixelRed(image,q)),q);
706 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
707 SetPixelGreen(image,ClampToUnsignedQuantum(GetPixelGreen(image,q)),q);
708 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
709 SetPixelBlue(image,ClampToUnsignedQuantum(GetPixelBlue(image,q)),q);
710 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
711 (image->colorspace == CMYKColorspace))
712 SetPixelBlack(image,ClampToUnsignedQuantum(GetPixelBlack(image,q)),q);
713 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
714 SetPixelAlpha(image,ClampToUnsignedQuantum(GetPixelAlpha(image,q)),q);
715 q+=GetPixelChannels(image);
717 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
719 if (image->progress_monitor != (MagickProgressMonitor) NULL)
724 #if defined(MAGICKCORE_OPENMP_SUPPORT)
725 #pragma omp critical (MagickCore_ClampImage)
727 proceed=SetImageProgress(image,ClampImageTag,progress++,
729 if (proceed == MagickFalse)
733 image_view=DestroyCacheView(image_view);
738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
742 % D e s t r o y T h r e s h o l d M a p %
746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748 % DestroyThresholdMap() de-allocate the given ThresholdMap
750 % The format of the ListThresholdMaps method is:
752 % ThresholdMap *DestroyThresholdMap(Threshold *map)
754 % A description of each parameter follows.
756 % o map: Pointer to the Threshold map to destroy
759 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
761 assert(map != (ThresholdMap *) NULL);
762 if (map->map_id != (char *) NULL)
763 map->map_id=DestroyString(map->map_id);
764 if (map->description != (char *) NULL)
765 map->description=DestroyString(map->description);
766 if (map->levels != (ssize_t *) NULL)
767 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
768 map=(ThresholdMap *) RelinquishMagickMemory(map);
773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777 + G e t T h r e s h o l d M a p F i l e %
781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
783 % GetThresholdMapFile() look for a given threshold map name or alias in the
784 % given XML file data, and return the allocated the map when found.
786 % The format of the ListThresholdMaps method is:
788 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
789 % const char *map_id,ExceptionInfo *exception)
791 % A description of each parameter follows.
793 % o xml: The threshold map list in XML format.
795 % o filename: The threshold map XML filename.
797 % o map_id: ID of the map to look for in XML list.
799 % o exception: return any errors or warnings in this structure.
802 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
803 const char *filename,const char *map_id,ExceptionInfo *exception)
821 map = (ThresholdMap *)NULL;
822 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
823 "Loading threshold map file \"%s\" ...",filename);
824 thresholds=NewXMLTree(xml,exception);
825 if ( thresholds == (XMLTreeInfo *)NULL )
828 for( threshold = GetXMLTreeChild(thresholds,"threshold");
829 threshold != (XMLTreeInfo *)NULL;
830 threshold = GetNextXMLTreeTag(threshold) ) {
831 attr = GetXMLTreeAttribute(threshold, "map");
832 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
834 attr = GetXMLTreeAttribute(threshold, "alias");
835 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
838 if ( threshold == (XMLTreeInfo *)NULL ) {
841 description = GetXMLTreeChild(threshold,"description");
842 if ( description == (XMLTreeInfo *)NULL ) {
843 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
844 "XmlMissingElement", "<description>, map \"%s\"", map_id);
845 thresholds = DestroyXMLTree(thresholds);
848 levels = GetXMLTreeChild(threshold,"levels");
849 if ( levels == (XMLTreeInfo *)NULL ) {
850 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
851 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
852 thresholds = DestroyXMLTree(thresholds);
856 /* The map has been found -- Allocate a Threshold Map to return */
857 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
858 if ( map == (ThresholdMap *)NULL )
859 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
860 map->map_id = (char *)NULL;
861 map->description = (char *)NULL;
862 map->levels = (ssize_t *) NULL;
864 /* Assign Basic Attributes */
865 attr = GetXMLTreeAttribute(threshold, "map");
866 if ( attr != (char *)NULL )
867 map->map_id = ConstantString(attr);
869 content = GetXMLTreeContent(description);
870 if ( content != (char *)NULL )
871 map->description = ConstantString(content);
873 attr = GetXMLTreeAttribute(levels, "width");
874 if ( attr == (char *)NULL ) {
875 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
876 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
877 thresholds = DestroyXMLTree(thresholds);
878 map = DestroyThresholdMap(map);
881 map->width = StringToUnsignedLong(attr);
882 if ( map->width == 0 ) {
883 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
884 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
885 thresholds = DestroyXMLTree(thresholds);
886 map = DestroyThresholdMap(map);
890 attr = GetXMLTreeAttribute(levels, "height");
891 if ( attr == (char *)NULL ) {
892 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
893 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
894 thresholds = DestroyXMLTree(thresholds);
895 map = DestroyThresholdMap(map);
898 map->height = StringToUnsignedLong(attr);
899 if ( map->height == 0 ) {
900 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
901 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
902 thresholds = DestroyXMLTree(thresholds);
903 map = DestroyThresholdMap(map);
907 attr = GetXMLTreeAttribute(levels, "divisor");
908 if ( attr == (char *)NULL ) {
909 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
910 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
911 thresholds = DestroyXMLTree(thresholds);
912 map = DestroyThresholdMap(map);
915 map->divisor = (ssize_t) StringToLong(attr);
916 if ( map->divisor < 2 ) {
917 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
918 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
919 thresholds = DestroyXMLTree(thresholds);
920 map = DestroyThresholdMap(map);
924 /* Allocate theshold levels array */
925 content = GetXMLTreeContent(levels);
926 if ( content == (char *)NULL ) {
927 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
928 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
929 thresholds = DestroyXMLTree(thresholds);
930 map = DestroyThresholdMap(map);
933 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
934 sizeof(*map->levels));
935 if ( map->levels == (ssize_t *)NULL )
936 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
937 { /* parse levels into integer array */
940 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
941 map->levels[i] = (ssize_t)strtol(content, &p, 10);
942 if ( p == content ) {
943 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
944 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
945 thresholds = DestroyXMLTree(thresholds);
946 map = DestroyThresholdMap(map);
949 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
950 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
951 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
952 (double) map->levels[i],map_id);
953 thresholds = DestroyXMLTree(thresholds);
954 map = DestroyThresholdMap(map);
959 value=(double) strtol(content,&p,10);
963 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
964 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
965 thresholds=DestroyXMLTree(thresholds);
966 map=DestroyThresholdMap(map);
971 thresholds = DestroyXMLTree(thresholds);
976 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
980 % G e t T h r e s h o l d M a p %
984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
986 % GetThresholdMap() load and search one or more threshold map files for the
987 % a map matching the given name or aliase.
989 % The format of the GetThresholdMap method is:
991 % ThresholdMap *GetThresholdMap(const char *map_id,
992 % ExceptionInfo *exception)
994 % A description of each parameter follows.
996 % o map_id: ID of the map to look for.
998 % o exception: return any errors or warnings in this structure.
1001 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1002 ExceptionInfo *exception)
1013 map=(ThresholdMap *)NULL;
1014 options=GetConfigureOptions(ThresholdsFilename,exception);
1015 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1016 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1017 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1018 GetStringInfoPath(option),map_id,exception);
1019 options=DestroyConfigureOptions(options);
1024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1028 + L i s t T h r e s h o l d M a p F i l e %
1032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1034 % ListThresholdMapFile() lists the threshold maps and their descriptions
1035 % in the given XML file data.
1037 % The format of the ListThresholdMaps method is:
1039 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1040 % const char *filename,ExceptionInfo *exception)
1042 % A description of each parameter follows.
1044 % o file: An pointer to the output FILE.
1046 % o xml: The threshold map list in XML format.
1048 % o filename: The threshold map XML filename.
1050 % o exception: return any errors or warnings in this structure.
1053 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1054 const char *filename,ExceptionInfo *exception)
1056 XMLTreeInfo *thresholds,*threshold,*description;
1057 const char *map,*alias,*content;
1059 assert( xml != (char *)NULL );
1060 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);
1068 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1069 (void) FormatLocaleFile(file,
1070 "----------------------------------------------------\n");
1072 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1073 threshold != (XMLTreeInfo *)NULL;
1074 threshold = GetNextXMLTreeTag(threshold) )
1076 map = GetXMLTreeAttribute(threshold, "map");
1077 if (map == (char *) NULL) {
1078 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1079 "XmlMissingAttribute", "<map>");
1080 thresholds=DestroyXMLTree(thresholds);
1081 return(MagickFalse);
1083 alias = GetXMLTreeAttribute(threshold, "alias");
1084 /* alias is optional, no if test needed */
1085 description=GetXMLTreeChild(threshold,"description");
1086 if ( description == (XMLTreeInfo *)NULL ) {
1087 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1088 "XmlMissingElement", "<description>, map \"%s\"", map);
1089 thresholds=DestroyXMLTree(thresholds);
1090 return(MagickFalse);
1092 content=GetXMLTreeContent(description);
1093 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);
1148 (void) FormatLocaleFile(file,
1149 "\n Threshold Maps for Ordered Dither Operations\n");
1150 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1151 != (const StringInfo *) NULL)
1153 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1154 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1155 GetStringInfoPath(option),exception);
1157 options=DestroyConfigureOptions(options);
1158 return(status != 0 ? MagickTrue : MagickFalse);
1162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1166 % O r d e r e d P o s t e r i z e I m a g e %
1170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1172 % OrderedPosterizeImage() will perform a ordered dither based on a number
1173 % of pre-defined dithering threshold maps, but over multiple intensity
1174 % levels, which can be different for different channels, according to the
1177 % The format of the OrderedPosterizeImage method is:
1179 % MagickBooleanType OrderedPosterizeImage(Image *image,
1180 % const char *threshold_map,ExceptionInfo *exception)
1182 % A description of each parameter follows:
1184 % o image: the image.
1186 % o threshold_map: A string containing the name of the threshold dither
1187 % map to use, followed by zero or more numbers representing the number
1188 % of color levels tho dither between.
1190 % Any level number less than 2 will be equivalent to 2, and means only
1191 % binary dithering will be applied to each color channel.
1193 % No numbers also means a 2 level (bitmap) dither will be applied to all
1194 % channels, while a single number is the number of levels applied to each
1195 % channel in sequence. More numbers will be applied in turn to each of
1196 % the color channels.
1198 % For example: "o3x3,6" will generate a 6 level posterization of the
1199 % image with a ordered 3x3 diffused pixel dither being applied between
1200 % each level. While checker,8,8,4 will produce a 332 colormaped image
1201 % with only a single checkerboard hash pattern (50% grey) between each
1202 % color level, to basically double the number of color levels with
1203 % a bare minimim of dithering.
1205 % o exception: return any errors or warnings in this structure.
1208 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1209 const char *threshold_map,ExceptionInfo *exception)
1211 #define DitherImageTag "Dither/Image"
1231 assert(image != (Image *) NULL);
1232 assert(image->signature == MagickSignature);
1233 if (image->debug != MagickFalse)
1234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1235 assert(exception != (ExceptionInfo *) NULL);
1236 assert(exception->signature == MagickSignature);
1237 if (threshold_map == (const char *) NULL)
1241 token[MaxTextExtent];
1246 p=(char *)threshold_map;
1247 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1251 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1253 if ((p-threshold_map) >= (MaxTextExtent-1))
1255 token[p-threshold_map] = *p;
1258 token[p-threshold_map] = '\0';
1259 map = GetThresholdMap(token, exception);
1260 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 */
1298 if ( p != (char *) NULL && *p == ',' ) {
1299 p=strchr((char *) threshold_map,',');
1301 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1302 levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1303 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1304 levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1305 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1306 levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1307 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
1308 (image->colorspace == CMYKColorspace))
1309 levels.black=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1310 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1311 levels.alpha = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1315 /* Parse level values as a geometry */
1317 * How to map GeometryInfo structure elements into
1318 * PixelLongPacket structure elements, but according to channel?
1319 * Note the channels list may skip elements!!!!
1320 * EG -channel BA -ordered-dither map,2,3
1321 * will need to map g.rho -> l.blue, and g.sigma -> l.alpha
1322 * A simpler way is needed, probably converting geometry to a temporary
1323 * array, then using channel to advance the index into ssize_t pixel packet.
1328 printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1329 levels.red, levels.green, levels.blue, levels.alpha, levels.index);
1332 { /* Do the posterized ordered dithering of the image */
1336 /* d = number of psuedo-level divisions added between color levels */
1339 /* reduce levels to levels - 1 */
1340 levels.red = levels.red ? levels.red-1 : 0;
1341 levels.green = levels.green ? levels.green-1 : 0;
1342 levels.blue = levels.blue ? levels.blue-1 : 0;
1343 levels.black = levels.black ? levels.black-1 : 0;
1344 levels.alpha = levels.alpha ? levels.alpha-1 : 0;
1346 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1347 return(MagickFalse);
1350 image_view=AcquireCacheView(image);
1351 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1352 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1354 for (y=0; y < (ssize_t) image->rows; y++)
1362 if (status == MagickFalse)
1364 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1365 if (q == (Quantum *) NULL)
1370 for (x=0; x < (ssize_t) image->columns; x++)
1378 Figure out the dither threshold for this pixel
1379 This must be a integer from 1 to map->divisor-1
1381 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1383 /* Dither each channel in the image as appropriate
1384 Notes on the integer Math...
1385 total number of divisions = (levels-1)*(divisor-1)+1)
1386 t1 = this colors psuedo_level =
1387 q->red * total_divisions / (QuantumRange+1)
1388 l = posterization level 0..levels
1389 t = dither threshold level 0..divisor-1 NB: 0 only on last
1390 Each color_level is of size QuantumRange / (levels-1)
1391 NB: All input levels and divisor are already had 1 subtracted
1392 Opacity is inverted so 'off' represents transparent.
1394 if (levels.red != 0) {
1395 t = (ssize_t) (QuantumScale*GetPixelRed(image,q)*(levels.red*d+1));
1397 SetPixelRed(image,RoundToQuantum((MagickRealType)
1398 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)),q);
1400 if (levels.green != 0) {
1401 t = (ssize_t) (QuantumScale*GetPixelGreen(image,q)*
1402 (levels.green*d+1));
1404 SetPixelGreen(image,RoundToQuantum((MagickRealType)
1405 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)),q);
1407 if (levels.blue != 0) {
1408 t = (ssize_t) (QuantumScale*GetPixelBlue(image,q)*
1411 SetPixelBlue(image,RoundToQuantum((MagickRealType)
1412 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)),q);
1414 if (levels.alpha != 0) {
1415 t = (ssize_t) ((1.0-QuantumScale*GetPixelAlpha(image,q))*
1416 (levels.alpha*d+1));
1418 SetPixelAlpha(image,RoundToQuantum((MagickRealType)
1419 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
1422 if (levels.black != 0) {
1423 t = (ssize_t) (QuantumScale*GetPixelBlack(image,q)*
1424 (levels.black*d+1));
1426 SetPixelBlack(image,RoundToQuantum((MagickRealType)
1427 ((l+(t>=threshold))*(MagickRealType) QuantumRange/levels.black)),q);
1429 q+=GetPixelChannels(image);
1431 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1433 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1438 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1439 #pragma omp critical (MagickCore_OrderedPosterizeImage)
1441 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1442 if (proceed == MagickFalse)
1446 image_view=DestroyCacheView(image_view);
1448 map=DestroyThresholdMap(map);
1453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457 % R a n d o m T h r e s h o l d I m a g e %
1461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1463 % RandomThresholdImage() changes the value of individual pixels based on the
1464 % intensity of each pixel compared to a random threshold. The result is a
1465 % low-contrast, two color image.
1467 % The format of the RandomThresholdImage method is:
1469 % MagickBooleanType RandomThresholdImage(Image *image,
1470 % const char *thresholds,ExceptionInfo *exception)
1472 % A description of each parameter follows:
1474 % o image: the image.
1476 % o thresholds: a geometry string containing low,high thresholds. If the
1477 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1478 % is performed instead.
1480 % o exception: return any errors or warnings in this structure.
1483 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1484 const char *thresholds,ExceptionInfo *exception)
1486 #define ThresholdImageTag "Threshold/Image"
1511 **restrict random_info;
1516 assert(image != (Image *) NULL);
1517 assert(image->signature == MagickSignature);
1518 if (image->debug != MagickFalse)
1519 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1520 assert(exception != (ExceptionInfo *) NULL);
1521 assert(exception->signature == MagickSignature);
1522 if (thresholds == (const char *) NULL)
1524 GetPixelInfo(image,&threshold);
1526 max_threshold=(MagickRealType) QuantumRange;
1527 flags=ParseGeometry(thresholds,&geometry_info);
1528 min_threshold=geometry_info.rho;
1529 max_threshold=geometry_info.sigma;
1530 if ((flags & SigmaValue) == 0)
1531 max_threshold=min_threshold;
1532 if (strchr(thresholds,'%') != (char *) NULL)
1534 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1535 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1538 Random threshold image.
1542 if (image->sync != MagickFalse)
1544 if (AcquireImageColormap(image,2,exception) == MagickFalse)
1545 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1547 random_info=AcquireRandomInfoThreadSet();
1548 image_view=AcquireCacheView(image);
1549 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1550 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1552 for (y=0; y < (ssize_t) image->rows; y++)
1555 id = GetOpenMPThreadId();
1566 if (status == MagickFalse)
1568 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1570 if (q == (Quantum *) NULL)
1575 for (x=0; x < (ssize_t) image->columns; x++)
1583 intensity=(MagickRealType) GetPixelIntensity(image,q);
1584 if (intensity < min_threshold)
1585 threshold.black=min_threshold;
1587 if (intensity > max_threshold)
1588 threshold.black=max_threshold;
1590 threshold.black=(MagickRealType)(QuantumRange*
1591 GetPseudoRandomValue(random_info[id]));
1592 index=(Quantum) (intensity <= threshold.black ? 0 : 1);
1593 SetPixelIndex(image,index,q);
1594 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
1595 q+=GetPixelChannels(image);
1597 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1598 if (sync == MagickFalse)
1600 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1605 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1606 #pragma omp critical (MagickCore_RandomThresholdImage)
1608 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1610 if (proceed == MagickFalse)
1614 image_view=DestroyCacheView(image_view);
1615 random_info=DestroyRandomInfoThreadSet(random_info);
1618 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1619 return(MagickFalse);
1620 random_info=AcquireRandomInfoThreadSet();
1621 image_view=AcquireCacheView(image);
1622 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1623 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1625 for (y=0; y < (ssize_t) image->rows; y++)
1628 id = GetOpenMPThreadId();
1636 if (status == MagickFalse)
1638 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1639 if (q == (Quantum *) NULL)
1644 for (x=0; x < (ssize_t) image->columns; x++)
1646 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1648 if ((MagickRealType) GetPixelRed(image,q) < min_threshold)
1649 threshold.red=min_threshold;
1651 if ((MagickRealType) GetPixelRed(image,q) > max_threshold)
1652 threshold.red=max_threshold;
1654 threshold.red=(MagickRealType) (QuantumRange*
1655 GetPseudoRandomValue(random_info[id]));
1657 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1659 if ((MagickRealType) GetPixelGreen(image,q) < min_threshold)
1660 threshold.green=min_threshold;
1662 if ((MagickRealType) GetPixelGreen(image,q) > max_threshold)
1663 threshold.green=max_threshold;
1665 threshold.green=(MagickRealType) (QuantumRange*
1666 GetPseudoRandomValue(random_info[id]));
1668 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1670 if ((MagickRealType) GetPixelBlue(image,q) < min_threshold)
1671 threshold.blue=min_threshold;
1673 if ((MagickRealType) GetPixelBlue(image,q) > max_threshold)
1674 threshold.blue=max_threshold;
1676 threshold.blue=(MagickRealType) (QuantumRange*
1677 GetPseudoRandomValue(random_info[id]));
1679 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1680 (image->colorspace == CMYKColorspace))
1682 if ((MagickRealType) GetPixelBlack(image,q) < min_threshold)
1683 threshold.black=min_threshold;
1685 if ((MagickRealType) GetPixelBlack(image,q) > max_threshold)
1686 threshold.black=max_threshold;
1688 threshold.black=(MagickRealType) (QuantumRange*
1689 GetPseudoRandomValue(random_info[id]));
1691 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1693 if ((MagickRealType) GetPixelAlpha(image,q) < min_threshold)
1694 threshold.alpha=min_threshold;
1696 if ((MagickRealType) GetPixelAlpha(image,q) > max_threshold)
1697 threshold.alpha=max_threshold;
1699 threshold.alpha=(MagickRealType) (QuantumRange*
1700 GetPseudoRandomValue(random_info[id]));
1702 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1703 SetPixelRed(image,(Quantum) ((MagickRealType)
1704 GetPixelRed(image,q) <= threshold.red ? 0 : QuantumRange),q);
1705 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1706 SetPixelGreen(image,(Quantum) ((MagickRealType)
1707 GetPixelGreen(image,q) <= threshold.green ? 0 : QuantumRange),q);
1708 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1709 SetPixelBlue(image,(Quantum) ((MagickRealType)
1710 GetPixelBlue(image,q) <= threshold.blue ? 0 : QuantumRange),q);
1711 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1712 (image->colorspace == CMYKColorspace))
1713 SetPixelBlack(image,(Quantum) ((MagickRealType)
1714 GetPixelBlack(image,q) <= threshold.black ? 0 : QuantumRange),q);
1715 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1716 SetPixelAlpha(image,(Quantum) ((MagickRealType)
1717 GetPixelAlpha(image,q) <= threshold.alpha ? 0 : QuantumRange),q);
1718 q+=GetPixelChannels(image);
1720 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1722 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1727 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1728 #pragma omp critical (MagickCore_RandomThresholdImage)
1730 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1732 if (proceed == MagickFalse)
1736 image_view=DestroyCacheView(image_view);
1737 random_info=DestroyRandomInfoThreadSet(random_info);
1742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1746 % W h i t e T h r e s h o l d I m a g e %
1750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1752 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1753 % the threshold into white while leaving all pixels at or below the threshold
1756 % The format of the WhiteThresholdImage method is:
1758 % MagickBooleanType WhiteThresholdImage(Image *image,
1759 % const char *threshold,ExceptionInfo *exception)
1761 % A description of each parameter follows:
1763 % o image: the image.
1765 % o threshold: Define the threshold value.
1767 % o exception: return any errors or warnings in this structure.
1770 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1771 const char *thresholds,ExceptionInfo *exception)
1773 #define ThresholdImageTag "Threshold/Image"
1796 assert(image != (Image *) NULL);
1797 assert(image->signature == MagickSignature);
1798 if (image->debug != MagickFalse)
1799 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1800 if (thresholds == (const char *) NULL)
1802 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1803 return(MagickFalse);
1804 flags=ParseGeometry(thresholds,&geometry_info);
1805 GetPixelInfo(image,&threshold);
1806 threshold.red=geometry_info.rho;
1807 threshold.green=geometry_info.sigma;
1808 if ((flags & SigmaValue) == 0)
1809 threshold.green=threshold.red;
1810 threshold.blue=geometry_info.xi;
1811 if ((flags & XiValue) == 0)
1812 threshold.blue=threshold.red;
1813 threshold.alpha=geometry_info.psi;
1814 if ((flags & PsiValue) == 0)
1815 threshold.alpha=threshold.red;
1816 threshold.black=geometry_info.chi;
1817 if ((flags & ChiValue) == 0)
1818 threshold.black=threshold.red;
1819 if ((flags & PercentValue) != 0)
1821 threshold.red*=(QuantumRange/100.0);
1822 threshold.green*=(QuantumRange/100.0);
1823 threshold.blue*=(QuantumRange/100.0);
1824 threshold.alpha*=(QuantumRange/100.0);
1825 threshold.black*=(QuantumRange/100.0);
1828 White threshold image.
1832 image_view=AcquireCacheView(image);
1833 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1834 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1836 for (y=0; y < (ssize_t) image->rows; y++)
1844 if (status == MagickFalse)
1846 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1847 if (q == (Quantum *) NULL)
1852 for (x=0; x < (ssize_t) image->columns; x++)
1854 if (image->sync != MagickFalse)
1856 if (GetPixelIntensity(image,q) > GetPixelInfoIntensity(&threshold))
1858 SetPixelRed(image,QuantumRange,q);
1859 SetPixelGreen(image,QuantumRange,q);
1860 SetPixelBlue(image,QuantumRange,q);
1861 if (image->colorspace == CMYKColorspace)
1862 SetPixelBlack(image,QuantumRange,q);
1867 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
1868 ((MagickRealType) GetPixelRed(image,q) > threshold.red))
1869 SetPixelRed(image,QuantumRange,q);
1870 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
1871 ((MagickRealType) GetPixelGreen(image,q) > threshold.green))
1872 SetPixelGreen(image,QuantumRange,q);
1873 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
1874 ((MagickRealType) GetPixelBlue(image,q) > threshold.blue))
1875 SetPixelBlue(image,QuantumRange,q);
1876 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1877 (image->colorspace == CMYKColorspace) &&
1878 ((MagickRealType) GetPixelBlack(image,q)) > threshold.black)
1879 SetPixelBlack(image,QuantumRange,q);
1880 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1881 ((MagickRealType) GetPixelAlpha(image,q) > threshold.alpha))
1882 SetPixelAlpha(image,QuantumRange,q);
1884 q+=GetPixelChannels(image);
1886 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1888 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1893 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1894 #pragma omp critical (MagickCore_WhiteThresholdImage)
1896 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1898 if (proceed == MagickFalse)
1902 image_view=DestroyCacheView(image_view);