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-2010 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 "magick/studio.h"
44 #include "magick/property.h"
45 #include "magick/blob.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colormap.h"
50 #include "magick/colorspace.h"
51 #include "magick/configure.h"
52 #include "magick/constitute.h"
53 #include "magick/decorate.h"
54 #include "magick/draw.h"
55 #include "magick/enhance.h"
56 #include "magick/exception.h"
57 #include "magick/exception-private.h"
58 #include "magick/effect.h"
59 #include "magick/fx.h"
60 #include "magick/gem.h"
61 #include "magick/geometry.h"
62 #include "magick/image-private.h"
63 #include "magick/list.h"
64 #include "magick/log.h"
65 #include "magick/memory_.h"
66 #include "magick/monitor.h"
67 #include "magick/monitor-private.h"
68 #include "magick/montage.h"
69 #include "magick/option.h"
70 #include "magick/pixel-private.h"
71 #include "magick/quantize.h"
72 #include "magick/quantum.h"
73 #include "magick/random_.h"
74 #include "magick/random-private.h"
75 #include "magick/resize.h"
76 #include "magick/resource_.h"
77 #include "magick/segment.h"
78 #include "magick/shear.h"
79 #include "magick/signature-private.h"
80 #include "magick/string_.h"
81 #include "magick/string-private.h"
82 #include "magick/transform.h"
83 #include "magick/threshold.h"
84 #include "magick/xml-tree.h"
89 #define ThresholdsFilename "thresholds.xml"
110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 % A d a p t i v e T h r e s h o l d I m a g e %
118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120 % AdaptiveThresholdImage() selects an individual threshold for each pixel
121 % based on the range of intensity values in its local neighborhood. This
122 % allows for thresholding of an image whose global intensity histogram
123 % doesn't contain distinctive peaks.
125 % The format of the AdaptiveThresholdImage method is:
127 % Image *AdaptiveThresholdImage(const Image *image,
128 % const size_t width,const size_t height,
129 % const ssize_t offset,ExceptionInfo *exception)
131 % A description of each parameter follows:
133 % o image: the image.
135 % o width: the width of the local neighborhood.
137 % o height: the height of the local neighborhood.
139 % o offset: the mean offset.
141 % o exception: return any errors or warnings in this structure.
144 MagickExport Image *AdaptiveThresholdImage(const Image *image,
145 const size_t width,const size_t height,const ssize_t offset,
146 ExceptionInfo *exception)
148 #define ThresholdImageTag "Threshold/Image"
172 assert(image != (const Image *) NULL);
173 assert(image->signature == MagickSignature);
174 if (image->debug != MagickFalse)
175 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
176 assert(exception != (ExceptionInfo *) NULL);
177 assert(exception->signature == MagickSignature);
178 if ((image->columns < width) || (image->rows < height))
179 ThrowImageException(OptionError,"ImageSmallerThanRadius");
180 threshold_image=CloneImage(image,0,0,MagickTrue,exception);
181 if (threshold_image == (Image *) NULL)
182 return((Image *) NULL);
183 if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
185 InheritException(exception,&threshold_image->exception);
186 threshold_image=DestroyImage(threshold_image);
187 return((Image *) NULL);
190 Local adaptive threshold.
194 GetMagickPixelPacket(image,&zero);
195 number_pixels=(MagickRealType) 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++)
206 register const IndexPacket
209 register const PixelPacket
213 *restrict threshold_indexes;
221 if (status == MagickFalse)
223 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
224 height/2L,image->columns+width,height,exception);
225 q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
227 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
232 indexes=GetCacheViewVirtualIndexQueue(image_view);
233 threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
234 for (x=0; x < (ssize_t) image->columns; x++)
243 register const PixelPacket
252 for (v=0; v < (ssize_t) height; v++)
254 for (u=0; u < (ssize_t) width; u++)
257 pixel.green+=r[u].green;
258 pixel.blue+=r[u].blue;
259 pixel.opacity+=r[u].opacity;
260 if (image->colorspace == CMYKColorspace)
261 pixel.index=(MagickRealType) indexes[x+(r-p)+u];
263 r+=image->columns+width;
265 mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
266 mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
267 mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
268 mean.opacity=(MagickRealType) (pixel.opacity/number_pixels+offset);
269 if (image->colorspace == CMYKColorspace)
270 mean.index=(MagickRealType) (pixel.index/number_pixels+offset);
271 q->red=(Quantum) (((MagickRealType) q->red <= mean.red) ?
273 q->green=(Quantum) (((MagickRealType) q->green <= mean.green) ?
275 q->blue=(Quantum) (((MagickRealType) q->blue <= mean.blue) ?
277 q->opacity=(Quantum) (((MagickRealType) q->opacity <= mean.opacity) ?
279 if (image->colorspace == CMYKColorspace)
280 threshold_indexes[x]=(IndexPacket) (((MagickRealType)
281 threshold_indexes[x] <= mean.index) ? 0 : QuantumRange);
285 sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
286 if (sync == MagickFalse)
288 if (image->progress_monitor != (MagickProgressMonitor) NULL)
293 #if defined(MAGICKCORE_OPENMP_SUPPORT)
294 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
296 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
298 if (proceed == MagickFalse)
302 threshold_view=DestroyCacheView(threshold_view);
303 image_view=DestroyCacheView(image_view);
304 if (status == MagickFalse)
305 threshold_image=DestroyImage(threshold_image);
306 return(threshold_image);
310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
314 % B i l e v e l I m a g e %
318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
320 % BilevelImage() changes the value of individual pixels based on the
321 % intensity of each pixel channel. The result is a high-contrast image.
323 % More precisely each channel value of the image is 'thresholded' so that if
324 % it is equal to or less than the given value it is set to zero, while any
325 % value greater than that give is set to it maximum or QuantumRange.
327 % This function is what is used to implement the "-threshold" operator for
328 % the command line API.
330 % If the default channel setting is given the image is thresholded using just
331 % the gray 'intensity' of the image, rather than the individual channels.
333 % The format of the BilevelImageChannel method is:
335 % MagickBooleanType BilevelImage(Image *image,const double threshold)
336 % MagickBooleanType BilevelImageChannel(Image *image,
337 % const ChannelType channel,const double threshold)
339 % A description of each parameter follows:
341 % o image: the image.
343 % o channel: the channel type.
345 % o threshold: define the threshold values.
347 % Aside: You can get the same results as operator using LevelImageChannels()
348 % with the 'threshold' value for both the black_point and the white_point.
352 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
357 status=BilevelImageChannel(image,DefaultChannels,threshold);
361 MagickExport MagickBooleanType BilevelImageChannel(Image *image,
362 const ChannelType channel,const double threshold)
364 #define ThresholdImageTag "Threshold/Image"
381 assert(image != (Image *) NULL);
382 assert(image->signature == MagickSignature);
383 if (image->debug != MagickFalse)
384 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
385 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
388 Bilevel threshold image.
392 exception=(&image->exception);
393 image_view=AcquireCacheView(image);
394 #if defined(MAGICKCORE_OPENMP_SUPPORT)
395 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
397 for (y=0; y < (ssize_t) image->rows; y++)
408 if (status == MagickFalse)
410 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
411 if (q == (PixelPacket *) NULL)
416 indexes=GetCacheViewAuthenticIndexQueue(image_view);
417 if (channel == DefaultChannels)
419 for (x=0; x < (ssize_t) image->columns; x++)
421 q->red=(Quantum) ((MagickRealType) PixelIntensityToQuantum(q) <=
422 threshold ? 0 : QuantumRange);
429 for (x=0; x < (ssize_t) image->columns; x++)
431 if ((channel & RedChannel) != 0)
432 q->red=(Quantum) ((MagickRealType) q->red <= threshold ? 0 :
434 if ((channel & GreenChannel) != 0)
435 q->green=(Quantum) ((MagickRealType) q->green <= threshold ? 0 :
437 if ((channel & BlueChannel) != 0)
438 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold ? 0 :
440 if ((channel & OpacityChannel) != 0)
442 if (image->matte == MagickFalse)
443 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
446 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
447 OpaqueOpacity : TransparentOpacity);
449 if (((channel & IndexChannel) != 0) &&
450 (image->colorspace == CMYKColorspace))
451 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <= threshold ?
455 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
457 if (image->progress_monitor != (MagickProgressMonitor) NULL)
462 #if defined(MAGICKCORE_OPENMP_SUPPORT)
463 #pragma omp critical (MagickCore_BilevelImageChannel)
465 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
467 if (proceed == MagickFalse)
471 image_view=DestroyCacheView(image_view);
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480 % B l a c k T h r e s h o l d I m a g e %
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
487 % the threshold into black while leaving all pixels at or above the threshold
490 % The format of the BlackThresholdImage method is:
492 % MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
493 % MagickBooleanType BlackThresholdImageChannel(Image *image,
494 % const ChannelType channel,const char *threshold,
495 % ExceptionInfo *exception)
497 % A description of each parameter follows:
499 % o image: the image.
501 % o channel: the channel or channels to be thresholded.
503 % o threshold: Define the threshold value.
505 % o exception: return any errors or warnings in this structure.
508 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
509 const char *threshold)
514 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
519 MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
520 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
522 #define ThresholdImageTag "Threshold/Image"
545 assert(image != (Image *) NULL);
546 assert(image->signature == MagickSignature);
547 if (image->debug != MagickFalse)
548 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
549 if (thresholds == (const char *) NULL)
551 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
553 GetMagickPixelPacket(image,&threshold);
554 flags=ParseGeometry(thresholds,&geometry_info);
555 threshold.red=geometry_info.rho;
556 threshold.green=geometry_info.sigma;
557 if ((flags & SigmaValue) == 0)
558 threshold.green=threshold.red;
559 threshold.blue=geometry_info.xi;
560 if ((flags & XiValue) == 0)
561 threshold.blue=threshold.red;
562 threshold.opacity=geometry_info.psi;
563 if ((flags & PsiValue) == 0)
564 threshold.opacity=threshold.red;
565 threshold.index=geometry_info.chi;
566 if ((flags & ChiValue) == 0)
567 threshold.index=threshold.red;
568 if ((flags & PercentValue) != 0)
570 threshold.red*=(QuantumRange/100.0);
571 threshold.green*=(QuantumRange/100.0);
572 threshold.blue*=(QuantumRange/100.0);
573 threshold.opacity*=(QuantumRange/100.0);
574 threshold.index*=(QuantumRange/100.0);
577 Black threshold image.
581 image_view=AcquireCacheView(image);
582 #if defined(MAGICKCORE_OPENMP_SUPPORT)
583 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
585 for (y=0; y < (ssize_t) image->rows; y++)
596 if (status == MagickFalse)
598 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
599 if (q == (PixelPacket *) NULL)
604 indexes=GetCacheViewAuthenticIndexQueue(image_view);
605 for (x=0; x < (ssize_t) image->columns; x++)
607 if (channel != DefaultChannels)
609 if (PixelIntensity(q) < MagickPixelIntensity(&threshold))
612 q->green=(Quantum) 0;
614 if (image->colorspace == CMYKColorspace)
615 indexes[x]=(Quantum) 0;
620 if (((channel & RedChannel) != 0) &&
621 ((MagickRealType) q->red < threshold.red))
623 if (((channel & GreenChannel) != 0) &&
624 ((MagickRealType) q->green < threshold.green))
625 q->green=(Quantum) 0;
626 if (((channel & BlueChannel) != 0) &&
627 ((MagickRealType) q->blue < threshold.blue))
629 if (((channel & OpacityChannel) != 0) &&
630 ((MagickRealType) q->opacity < threshold.opacity))
631 q->opacity=(Quantum) 0;
632 if (((channel & IndexChannel) != 0) &&
633 (image->colorspace == CMYKColorspace) &&
634 ((MagickRealType) indexes[x] < threshold.index))
635 indexes[x]=(Quantum) 0;
639 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
641 if (image->progress_monitor != (MagickProgressMonitor) NULL)
646 #if defined(MAGICKCORE_OPENMP_SUPPORT)
647 #pragma omp critical (MagickCore_BlackThresholdImageChannel)
649 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
651 if (proceed == MagickFalse)
655 image_view=DestroyCacheView(image_view);
660 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
664 % C l a m p I m a g e %
668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 % ClampImage() restricts the color range from 0 to the quantum depth.
672 % The format of the ClampImageChannel method is:
674 % MagickBooleanType ClampImage(Image *image)
675 % MagickBooleanType ClampImageChannel(Image *image,
676 % const ChannelType channel)
678 % A description of each parameter follows:
680 % o image: the image.
682 % o channel: the channel type.
686 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
688 #if defined(MAGICKCORE_HDRI_SUPPORT)
691 if (quantum >= QuantumRange)
692 return(QuantumRange);
699 MagickExport MagickBooleanType ClampImage(Image *image)
704 status=ClampImageChannel(image,DefaultChannels);
708 MagickExport MagickBooleanType ClampImageChannel(Image *image,
709 const ChannelType channel)
711 #define ClampImageTag "Clamp/Image"
728 assert(image != (Image *) NULL);
729 assert(image->signature == MagickSignature);
730 if (image->debug != MagickFalse)
731 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
732 if (image->storage_class == PseudoClass)
741 for (i=0; i < (ssize_t) image->colors; i++)
743 q->red=ClampToUnsignedQuantum(q->red);
744 q->green=ClampToUnsignedQuantum(q->green);
745 q->blue=ClampToUnsignedQuantum(q->blue);
746 q->opacity=ClampToUnsignedQuantum(q->opacity);
749 return(SyncImage(image));
756 exception=(&image->exception);
757 image_view=AcquireCacheView(image);
758 #if defined(MAGICKCORE_OPENMP_SUPPORT)
759 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
761 for (y=0; y < (ssize_t) image->rows; y++)
772 if (status == MagickFalse)
774 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
775 if (q == (PixelPacket *) NULL)
780 indexes=GetCacheViewAuthenticIndexQueue(image_view);
781 for (x=0; x < (ssize_t) image->columns; x++)
783 if ((channel & RedChannel) != 0)
784 q->red=ClampToUnsignedQuantum(q->red);
785 if ((channel & GreenChannel) != 0)
786 q->green=ClampToUnsignedQuantum(q->green);
787 if ((channel & BlueChannel) != 0)
788 q->blue=ClampToUnsignedQuantum(q->blue);
789 if ((channel & OpacityChannel) != 0)
790 q->opacity=ClampToUnsignedQuantum(q->opacity);
791 if (((channel & IndexChannel) != 0) &&
792 (image->colorspace == CMYKColorspace))
793 indexes[x]=(IndexPacket) ClampToUnsignedQuantum(indexes[x]);
796 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
798 if (image->progress_monitor != (MagickProgressMonitor) NULL)
803 #if defined(MAGICKCORE_OPENMP_SUPPORT)
804 #pragma omp critical (MagickCore_ClampImageChannel)
806 proceed=SetImageProgress(image,ClampImageTag,progress++,
808 if (proceed == MagickFalse)
812 image_view=DestroyCacheView(image_view);
817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821 % D e s t r o y T h r e s h o l d M a p %
825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
827 % DestroyThresholdMap() de-allocate the given ThresholdMap
829 % The format of the ListThresholdMaps method is:
831 % ThresholdMap *DestroyThresholdMap(Threshold *map)
833 % A description of each parameter follows.
835 % o map: Pointer to the Threshold map to destroy
838 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
840 assert(map != (ThresholdMap *) NULL);
841 if (map->map_id != (char *) NULL)
842 map->map_id=DestroyString(map->map_id);
843 if (map->description != (char *) NULL)
844 map->description=DestroyString(map->description);
845 if (map->levels != (ssize_t *) NULL)
846 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
847 map=(ThresholdMap *) RelinquishMagickMemory(map);
852 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856 + G e t T h r e s h o l d M a p F i l e %
860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
862 % GetThresholdMapFile() look for a given threshold map name or alias in the
863 % given XML file data, and return the allocated the map when found.
865 % The format of the ListThresholdMaps method is:
867 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
868 % const char *map_id,ExceptionInfo *exception)
870 % A description of each parameter follows.
872 % o xml: The threshold map list in XML format.
874 % o filename: The threshold map XML filename.
876 % o map_id: ID of the map to look for in XML list.
878 % o exception: return any errors or warnings in this structure.
881 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
882 const char *filename,const char *map_id,ExceptionInfo *exception)
900 map = (ThresholdMap *)NULL;
901 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
902 "Loading threshold map file \"%s\" ...",filename);
903 thresholds=NewXMLTree(xml,exception);
904 if ( thresholds == (XMLTreeInfo *)NULL )
907 for( threshold = GetXMLTreeChild(thresholds,"threshold");
908 threshold != (XMLTreeInfo *)NULL;
909 threshold = GetNextXMLTreeTag(threshold) ) {
910 attr = GetXMLTreeAttribute(threshold, "map");
911 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
913 attr = GetXMLTreeAttribute(threshold, "alias");
914 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
917 if ( threshold == (XMLTreeInfo *)NULL ) {
920 description = GetXMLTreeChild(threshold,"description");
921 if ( description == (XMLTreeInfo *)NULL ) {
922 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
923 "XmlMissingElement", "<description>, map \"%s\"", map_id);
924 thresholds = DestroyXMLTree(thresholds);
927 levels = GetXMLTreeChild(threshold,"levels");
928 if ( levels == (XMLTreeInfo *)NULL ) {
929 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
930 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
931 thresholds = DestroyXMLTree(thresholds);
935 /* The map has been found -- Allocate a Threshold Map to return */
936 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
937 if ( map == (ThresholdMap *)NULL )
938 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
939 map->map_id = (char *)NULL;
940 map->description = (char *)NULL;
941 map->levels = (ssize_t *) NULL;
943 /* Assign Basic Attributes */
944 attr = GetXMLTreeAttribute(threshold, "map");
945 if ( attr != (char *)NULL )
946 map->map_id = ConstantString(attr);
948 content = GetXMLTreeContent(description);
949 if ( content != (char *)NULL )
950 map->description = ConstantString(content);
952 attr = GetXMLTreeAttribute(levels, "width");
953 if ( attr == (char *)NULL ) {
954 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
955 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
956 thresholds = DestroyXMLTree(thresholds);
957 map = DestroyThresholdMap(map);
960 map->width = StringToUnsignedLong(attr);
961 if ( map->width == 0 ) {
962 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
963 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
964 thresholds = DestroyXMLTree(thresholds);
965 map = DestroyThresholdMap(map);
969 attr = GetXMLTreeAttribute(levels, "height");
970 if ( attr == (char *)NULL ) {
971 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
972 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
973 thresholds = DestroyXMLTree(thresholds);
974 map = DestroyThresholdMap(map);
977 map->height = StringToUnsignedLong(attr);
978 if ( map->height == 0 ) {
979 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
980 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
981 thresholds = DestroyXMLTree(thresholds);
982 map = DestroyThresholdMap(map);
986 attr = GetXMLTreeAttribute(levels, "divisor");
987 if ( attr == (char *)NULL ) {
988 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
989 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
990 thresholds = DestroyXMLTree(thresholds);
991 map = DestroyThresholdMap(map);
994 map->divisor = StringToLong(attr);
995 if ( map->divisor < 2 ) {
996 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
997 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
998 thresholds = DestroyXMLTree(thresholds);
999 map = DestroyThresholdMap(map);
1003 /* Allocate theshold levels array */
1004 content = GetXMLTreeContent(levels);
1005 if ( content == (char *)NULL ) {
1006 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1007 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
1008 thresholds = DestroyXMLTree(thresholds);
1009 map = DestroyThresholdMap(map);
1012 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1013 sizeof(*map->levels));
1014 if ( map->levels == (ssize_t *)NULL )
1015 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1016 { /* parse levels into integer array */
1019 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
1020 map->levels[i] = (ssize_t)strtol(content, &p, 10);
1021 if ( p == content ) {
1022 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1023 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1024 thresholds = DestroyXMLTree(thresholds);
1025 map = DestroyThresholdMap(map);
1028 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
1029 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1030 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1031 (double) map->levels[i],map_id);
1032 thresholds = DestroyXMLTree(thresholds);
1033 map = DestroyThresholdMap(map);
1038 value=(double) strtol(content,&p,10);
1041 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1042 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
1043 thresholds=DestroyXMLTree(thresholds);
1044 map=DestroyThresholdMap(map);
1049 thresholds = DestroyXMLTree(thresholds);
1054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1058 % G e t T h r e s h o l d M a p %
1062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1064 % GetThresholdMap() load and search one or more threshold map files for the
1065 % a map matching the given name or aliase.
1067 % The format of the GetThresholdMap method is:
1069 % ThresholdMap *GetThresholdMap(const char *map_id,
1070 % ExceptionInfo *exception)
1072 % A description of each parameter follows.
1074 % o map_id: ID of the map to look for.
1076 % o exception: return any errors or warnings in this structure.
1079 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1080 ExceptionInfo *exception)
1091 map=(ThresholdMap *)NULL;
1092 options=GetConfigureOptions(ThresholdsFilename,exception);
1093 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1094 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1095 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1096 GetStringInfoPath(option),map_id,exception);
1097 options=DestroyConfigureOptions(options);
1102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1106 + L i s t T h r e s h o l d M a p F i l e %
1110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1112 % ListThresholdMapFile() lists the threshold maps and their descriptions
1113 % in the given XML file data.
1115 % The format of the ListThresholdMaps method is:
1117 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1118 % const char *filename,ExceptionInfo *exception)
1120 % A description of each parameter follows.
1122 % o file: An pointer to the output FILE.
1124 % o xml: The threshold map list in XML format.
1126 % o filename: The threshold map XML filename.
1128 % o exception: return any errors or warnings in this structure.
1131 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1132 const char *filename,ExceptionInfo *exception)
1134 XMLTreeInfo *thresholds,*threshold,*description;
1135 const char *map,*alias,*content;
1137 assert( xml != (char *)NULL );
1138 assert( file != (FILE *)NULL );
1140 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1141 "Loading threshold map file \"%s\" ...",filename);
1142 thresholds=NewXMLTree(xml,exception);
1143 if ( thresholds == (XMLTreeInfo *)NULL )
1144 return(MagickFalse);
1146 (void) fprintf(file,"%-16s %-12s %s\n", "Map", "Alias", "Description");
1147 (void) fprintf(file,"----------------------------------------------------\n");
1149 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1150 threshold != (XMLTreeInfo *)NULL;
1151 threshold = GetNextXMLTreeTag(threshold) )
1153 map = GetXMLTreeAttribute(threshold, "map");
1154 if (map == (char *) NULL) {
1155 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1156 "XmlMissingAttribute", "<map>");
1157 thresholds=DestroyXMLTree(thresholds);
1158 return(MagickFalse);
1160 alias = GetXMLTreeAttribute(threshold, "alias");
1161 /* alias is optional, no if test needed */
1162 description=GetXMLTreeChild(threshold,"description");
1163 if ( description == (XMLTreeInfo *)NULL ) {
1164 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1165 "XmlMissingElement", "<description>, map \"%s\"", map);
1166 thresholds=DestroyXMLTree(thresholds);
1167 return(MagickFalse);
1169 content=GetXMLTreeContent(description);
1170 if ( content == (char *)NULL ) {
1171 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1172 "XmlMissingContent", "<description>, map \"%s\"", map);
1173 thresholds=DestroyXMLTree(thresholds);
1174 return(MagickFalse);
1176 (void) fprintf(file,"%-16s %-12s %s\n",map,alias ? alias : "", content);
1178 thresholds=DestroyXMLTree(thresholds);
1183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1187 % L i s t T h r e s h o l d M a p s %
1191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1193 % ListThresholdMaps() lists the threshold maps and their descriptions
1194 % as defined by "threshold.xml" to a file.
1196 % The format of the ListThresholdMaps method is:
1198 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1200 % A description of each parameter follows.
1202 % o file: An pointer to the output FILE.
1204 % o exception: return any errors or warnings in this structure.
1207 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1208 ExceptionInfo *exception)
1220 if ( file == (FILE *)NULL )
1222 options=GetConfigureOptions(ThresholdsFilename,exception);
1224 (void) fprintf(file, "\n Threshold Maps for Ordered Dither Operations\n");
1226 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1227 != (const StringInfo *) NULL)
1229 (void) fprintf(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1230 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1231 GetStringInfoPath(option),exception);
1233 options=DestroyConfigureOptions(options);
1234 return(status != 0 ? MagickTrue : MagickFalse);
1238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242 % O r d e r e d D i t h e r I m a g e %
1246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248 % OrderedDitherImage() uses the ordered dithering technique of reducing color
1249 % images to monochrome using positional information to retain as much
1250 % information as possible.
1252 % WARNING: This function is deprecated, and is now just a call to
1253 % the more more powerful OrderedPosterizeImage(); function.
1255 % The format of the OrderedDitherImage method is:
1257 % MagickBooleanType OrderedDitherImage(Image *image)
1258 % MagickBooleanType OrderedDitherImageChannel(Image *image,
1259 % const ChannelType channel,ExceptionInfo *exception)
1261 % A description of each parameter follows:
1263 % o image: the image.
1265 % o channel: the channel or channels to be thresholded.
1267 % o exception: return any errors or warnings in this structure.
1271 MagickExport MagickBooleanType OrderedDitherImage(Image *image)
1276 status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
1280 MagickExport MagickBooleanType OrderedDitherImageChannel(Image *image,
1281 const ChannelType channel,ExceptionInfo *exception)
1287 Call the augumented function OrderedPosterizeImage()
1289 status=OrderedPosterizeImageChannel(image,channel,"o8x8",exception);
1294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1298 % O r d e r e d P o s t e r i z e I m a g e %
1302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1304 % OrderedPosterizeImage() will perform a ordered dither based on a number
1305 % of pre-defined dithering threshold maps, but over multiple intensity
1306 % levels, which can be different for different channels, according to the
1309 % The format of the OrderedPosterizeImage method is:
1311 % MagickBooleanType OrderedPosterizeImage(Image *image,
1312 % const char *threshold_map,ExceptionInfo *exception)
1313 % MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1314 % const ChannelType channel,const char *threshold_map,
1315 % ExceptionInfo *exception)
1317 % A description of each parameter follows:
1319 % o image: the image.
1321 % o channel: the channel or channels to be thresholded.
1323 % o threshold_map: A string containing the name of the threshold dither
1324 % map to use, followed by zero or more numbers representing the number
1325 % of color levels tho dither between.
1327 % Any level number less than 2 will be equivelent to 2, and means only
1328 % binary dithering will be applied to each color channel.
1330 % No numbers also means a 2 level (bitmap) dither will be applied to all
1331 % channels, while a single number is the number of levels applied to each
1332 % channel in sequence. More numbers will be applied in turn to each of
1333 % the color channels.
1335 % For example: "o3x3,6" will generate a 6 level posterization of the
1336 % image with a ordered 3x3 diffused pixel dither being applied between
1337 % each level. While checker,8,8,4 will produce a 332 colormaped image
1338 % with only a single checkerboard hash pattern (50% grey) between each
1339 % color level, to basically double the number of color levels with
1340 % a bare minimim of dithering.
1342 % o exception: return any errors or warnings in this structure.
1345 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1346 const char *threshold_map,ExceptionInfo *exception)
1351 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1356 MagickExport MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1357 const ChannelType channel,const char *threshold_map,ExceptionInfo *exception)
1359 #define DitherImageTag "Dither/Image"
1379 assert(image != (Image *) NULL);
1380 assert(image->signature == MagickSignature);
1381 if (image->debug != MagickFalse)
1382 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1383 assert(exception != (ExceptionInfo *) NULL);
1384 assert(exception->signature == MagickSignature);
1385 if (threshold_map == (const char *) NULL)
1389 token[MaxTextExtent];
1394 p=(char *)threshold_map;
1395 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1399 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1401 if ((p-threshold_map) >= MaxTextExtent)
1403 token[p-threshold_map] = *p;
1406 token[p-threshold_map] = '\0';
1407 map = GetThresholdMap(token, exception);
1408 if ( map == (ThresholdMap *)NULL ) {
1409 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1410 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1411 return(MagickFalse);
1414 /* Set channel levels from extra comma seperated arguments
1415 Default to 2, the single value given, or individual channel values
1418 { /* parse directly as a comma seperated list of integers */
1421 p = strchr((char *) threshold_map,',');
1422 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
1423 levels.index = (unsigned int) strtoul(p, &p, 10);
1427 levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
1428 levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
1429 levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
1430 levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
1431 levels.index = ((channel & IndexChannel) != 0
1432 && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
1434 /* if more than a single number, each channel has a separate value */
1435 if ( p != (char *) NULL && *p == ',' ) {
1436 p=strchr((char *) threshold_map,',');
1438 if ((channel & RedChannel) != 0)
1439 levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1440 if ((channel & GreenChannel) != 0)
1441 levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1442 if ((channel & BlueChannel) != 0)
1443 levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1444 if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
1445 levels.index=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1446 if ((channel & OpacityChannel) != 0)
1447 levels.opacity = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1451 /* Parse level values as a geometry */
1453 * How to map GeometryInfo structure elements into
1454 * LongPixelPacket structure elements, but according to channel?
1455 * Note the channels list may skip elements!!!!
1456 * EG -channel BA -ordered-dither map,2,3
1457 * will need to map g.rho -> l.blue, and g.sigma -> l.opacity
1458 * A simpler way is needed, probably converting geometry to a temporary
1459 * array, then using channel to advance the index into ssize_t pixel packet.
1464 printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1465 levels.red, levels.green, levels.blue, levels.opacity, levels.index);
1468 { /* Do the posterized ordered dithering of the image */
1472 /* d = number of psuedo-level divisions added between color levels */
1475 /* reduce levels to levels - 1 */
1476 levels.red = levels.red ? levels.red-1 : 0;
1477 levels.green = levels.green ? levels.green-1 : 0;
1478 levels.blue = levels.blue ? levels.blue-1 : 0;
1479 levels.opacity = levels.opacity ? levels.opacity-1 : 0;
1480 levels.index = levels.index ? levels.index-1 : 0;
1482 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1484 InheritException(exception,&image->exception);
1485 return(MagickFalse);
1489 image_view=AcquireCacheView(image);
1490 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1491 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1493 for (y=0; y < (ssize_t) image->rows; y++)
1495 register IndexPacket
1501 register PixelPacket
1504 if (status == MagickFalse)
1506 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1507 if (q == (PixelPacket *) NULL)
1512 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1513 for (x=0; x < (ssize_t) image->columns; x++)
1521 Figure out the dither threshold for this pixel
1522 This must be a integer from 1 to map->divisor-1
1524 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1526 /* Dither each channel in the image as appropriate
1527 Notes on the integer Math...
1528 total number of divisions = (levels-1)*(divisor-1)+1)
1529 t1 = this colors psuedo_level =
1530 q->red * total_divisions / (QuantumRange+1)
1531 l = posterization level 0..levels
1532 t = dither threshold level 0..divisor-1 NB: 0 only on last
1533 Each color_level is of size QuantumRange / (levels-1)
1534 NB: All input levels and divisor are already had 1 subtracted
1535 Opacity is inverted so 'off' represents transparent.
1538 t = (ssize_t) (QuantumScale*q->red*(levels.red*d+1));
1540 q->red=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.red);
1543 t = (ssize_t) (QuantumScale*q->green*(levels.green*d+1));
1545 q->green=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.green);
1548 t = (ssize_t) (QuantumScale*q->blue*(levels.blue*d+1));
1550 q->blue=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.blue);
1552 if (levels.opacity) {
1553 t = (ssize_t) ((1.0-QuantumScale*q->opacity)*(levels.opacity*d+1));
1555 q->opacity=(Quantum) ((1.0-l-(t >= threshold))*QuantumRange/
1559 t = (ssize_t) (QuantumScale*indexes[x]*(levels.index*d+1));
1561 indexes[x]=(IndexPacket) ((l+(t>=threshold))*QuantumRange/
1566 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1568 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1573 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1574 #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
1576 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1577 if (proceed == MagickFalse)
1581 image_view=DestroyCacheView(image_view);
1583 map=DestroyThresholdMap(map);
1588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1592 % R a n d o m T h r e s h o l d I m a g e %
1596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1598 % RandomThresholdImage() changes the value of individual pixels based on the
1599 % intensity of each pixel compared to a random threshold. The result is a
1600 % low-contrast, two color image.
1602 % The format of the RandomThresholdImage method is:
1604 % MagickBooleanType RandomThresholdImageChannel(Image *image,
1605 % const char *thresholds,ExceptionInfo *exception)
1606 % MagickBooleanType RandomThresholdImageChannel(Image *image,
1607 % const ChannelType channel,const char *thresholds,
1608 % ExceptionInfo *exception)
1610 % A description of each parameter follows:
1612 % o image: the image.
1614 % o channel: the channel or channels to be thresholded.
1616 % o thresholds: a geometry string containing low,high thresholds. If the
1617 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1618 % is performed instead.
1620 % o exception: return any errors or warnings in this structure.
1624 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1625 const char *thresholds,ExceptionInfo *exception)
1630 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
1635 MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
1636 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1638 #define ThresholdImageTag "Threshold/Image"
1663 **restrict random_info;
1668 assert(image != (Image *) NULL);
1669 assert(image->signature == MagickSignature);
1670 if (image->debug != MagickFalse)
1671 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1672 assert(exception != (ExceptionInfo *) NULL);
1673 assert(exception->signature == MagickSignature);
1674 if (thresholds == (const char *) NULL)
1676 GetMagickPixelPacket(image,&threshold);
1678 max_threshold=(MagickRealType) QuantumRange;
1679 flags=ParseGeometry(thresholds,&geometry_info);
1680 min_threshold=geometry_info.rho;
1681 max_threshold=geometry_info.sigma;
1682 if ((flags & SigmaValue) == 0)
1683 max_threshold=min_threshold;
1684 if (strchr(thresholds,'%') != (char *) NULL)
1686 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1687 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1690 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
1691 (min_threshold <= 8))
1694 Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
1696 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
1700 Random threshold image.
1704 if (channel == AllChannels)
1706 if (AcquireImageColormap(image,2) == MagickFalse)
1707 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1709 random_info=AcquireRandomInfoThreadSet();
1710 image_view=AcquireCacheView(image);
1711 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1712 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1714 for (y=0; y < (ssize_t) image->rows; y++)
1717 id = GetOpenMPThreadId();
1722 register IndexPacket
1728 register PixelPacket
1731 if (status == MagickFalse)
1733 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1735 if (q == (PixelPacket *) NULL)
1740 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1741 for (x=0; x < (ssize_t) image->columns; x++)
1749 intensity=(MagickRealType) PixelIntensityToQuantum(q);
1750 if (intensity < min_threshold)
1751 threshold.index=min_threshold;
1752 else if (intensity > max_threshold)
1753 threshold.index=max_threshold;
1755 threshold.index=(MagickRealType)(QuantumRange*
1756 GetPseudoRandomValue(random_info[id]));
1757 index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
1759 *q++=image->colormap[(ssize_t) index];
1761 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1762 if (sync == MagickFalse)
1764 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1769 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1770 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1772 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1774 if (proceed == MagickFalse)
1778 image_view=DestroyCacheView(image_view);
1779 random_info=DestroyRandomInfoThreadSet(random_info);
1782 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1784 InheritException(exception,&image->exception);
1785 return(MagickFalse);
1787 random_info=AcquireRandomInfoThreadSet();
1788 image_view=AcquireCacheView(image);
1789 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1790 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1792 for (y=0; y < (ssize_t) image->rows; y++)
1795 id = GetOpenMPThreadId();
1797 register IndexPacket
1800 register PixelPacket
1806 if (status == MagickFalse)
1808 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1809 if (q == (PixelPacket *) NULL)
1814 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1815 for (x=0; x < (ssize_t) image->columns; x++)
1817 if ((channel & RedChannel) != 0)
1819 if ((MagickRealType) q->red < min_threshold)
1820 threshold.red=min_threshold;
1822 if ((MagickRealType) q->red > max_threshold)
1823 threshold.red=max_threshold;
1825 threshold.red=(MagickRealType) (QuantumRange*
1826 GetPseudoRandomValue(random_info[id]));
1828 if ((channel & GreenChannel) != 0)
1830 if ((MagickRealType) q->green < min_threshold)
1831 threshold.green=min_threshold;
1833 if ((MagickRealType) q->green > max_threshold)
1834 threshold.green=max_threshold;
1836 threshold.green=(MagickRealType) (QuantumRange*
1837 GetPseudoRandomValue(random_info[id]));
1839 if ((channel & BlueChannel) != 0)
1841 if ((MagickRealType) q->blue < min_threshold)
1842 threshold.blue=min_threshold;
1844 if ((MagickRealType) q->blue > max_threshold)
1845 threshold.blue=max_threshold;
1847 threshold.blue=(MagickRealType) (QuantumRange*
1848 GetPseudoRandomValue(random_info[id]));
1850 if ((channel & OpacityChannel) != 0)
1852 if ((MagickRealType) q->opacity < min_threshold)
1853 threshold.opacity=min_threshold;
1855 if ((MagickRealType) q->opacity > max_threshold)
1856 threshold.opacity=max_threshold;
1858 threshold.opacity=(MagickRealType) (QuantumRange*
1859 GetPseudoRandomValue(random_info[id]));
1861 if (((channel & IndexChannel) != 0) &&
1862 (image->colorspace == CMYKColorspace))
1864 if ((MagickRealType) indexes[x] < min_threshold)
1865 threshold.index=min_threshold;
1867 if ((MagickRealType) indexes[x] > max_threshold)
1868 threshold.index=max_threshold;
1870 threshold.index=(MagickRealType) (QuantumRange*
1871 GetPseudoRandomValue(random_info[id]));
1873 if ((channel & RedChannel) != 0)
1874 q->red=(Quantum) ((MagickRealType) q->red <= threshold.red ? 0 :
1876 if ((channel & GreenChannel) != 0)
1877 q->green=(Quantum) ((MagickRealType) q->green <= threshold.green ? 0 :
1879 if ((channel & BlueChannel) != 0)
1880 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold.blue ? 0 :
1882 if ((channel & OpacityChannel) != 0)
1883 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold.opacity ?
1885 if (((channel & IndexChannel) != 0) &&
1886 (image->colorspace == CMYKColorspace))
1887 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <=
1888 threshold.index ? 0 : QuantumRange);
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_RandomThresholdImageChannel)
1901 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1903 if (proceed == MagickFalse)
1907 image_view=DestroyCacheView(image_view);
1908 random_info=DestroyRandomInfoThreadSet(random_info);
1913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1917 % W h i t e T h r e s h o l d I m a g e %
1921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1923 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1924 % the threshold into white while leaving all pixels at or below the threshold
1927 % The format of the WhiteThresholdImage method is:
1929 % MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
1930 % MagickBooleanType WhiteThresholdImageChannel(Image *image,
1931 % const ChannelType channel,const char *threshold,
1932 % ExceptionInfo *exception)
1934 % A description of each parameter follows:
1936 % o image: the image.
1938 % o channel: the channel or channels to be thresholded.
1940 % o threshold: Define the threshold value.
1942 % o exception: return any errors or warnings in this structure.
1945 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1946 const char *threshold)
1951 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
1956 MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
1957 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1959 #define ThresholdImageTag "Threshold/Image"
1982 assert(image != (Image *) NULL);
1983 assert(image->signature == MagickSignature);
1984 if (image->debug != MagickFalse)
1985 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1986 if (thresholds == (const char *) NULL)
1988 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1989 return(MagickFalse);
1990 flags=ParseGeometry(thresholds,&geometry_info);
1991 GetMagickPixelPacket(image,&threshold);
1992 threshold.red=geometry_info.rho;
1993 threshold.green=geometry_info.sigma;
1994 if ((flags & SigmaValue) == 0)
1995 threshold.green=threshold.red;
1996 threshold.blue=geometry_info.xi;
1997 if ((flags & XiValue) == 0)
1998 threshold.blue=threshold.red;
1999 threshold.opacity=geometry_info.psi;
2000 if ((flags & PsiValue) == 0)
2001 threshold.opacity=threshold.red;
2002 threshold.index=geometry_info.chi;
2003 if ((flags & ChiValue) == 0)
2004 threshold.index=threshold.red;
2005 if ((flags & PercentValue) != 0)
2007 threshold.red*=(QuantumRange/100.0);
2008 threshold.green*=(QuantumRange/100.0);
2009 threshold.blue*=(QuantumRange/100.0);
2010 threshold.opacity*=(QuantumRange/100.0);
2011 threshold.index*=(QuantumRange/100.0);
2014 White threshold image.
2018 image_view=AcquireCacheView(image);
2019 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2020 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2022 for (y=0; y < (ssize_t) image->rows; y++)
2024 register IndexPacket
2030 register PixelPacket
2033 if (status == MagickFalse)
2035 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2036 if (q == (PixelPacket *) NULL)
2041 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2042 for (x=0; x < (ssize_t) image->columns; x++)
2044 if (channel != DefaultChannels)
2046 if (PixelIntensity(q) > MagickPixelIntensity(&threshold))
2048 q->red=(Quantum) QuantumRange;
2049 q->green=(Quantum) QuantumRange;
2050 q->blue=(Quantum) QuantumRange;
2051 if (image->colorspace == CMYKColorspace)
2052 indexes[x]=(Quantum) QuantumRange;
2057 if (((channel & RedChannel) != 0) &&
2058 ((MagickRealType) q->red > threshold.red))
2059 q->red=(Quantum) QuantumRange;
2060 if (((channel & GreenChannel) != 0) &&
2061 ((MagickRealType) q->green > threshold.green))
2062 q->green=(Quantum) QuantumRange;
2063 if (((channel & BlueChannel) != 0) &&
2064 ((MagickRealType) q->blue > threshold.blue))
2065 q->blue=(Quantum) QuantumRange;
2066 if (((channel & OpacityChannel) != 0) &&
2067 ((MagickRealType) q->opacity > threshold.opacity))
2068 q->opacity=(Quantum) QuantumRange;
2069 if (((channel & IndexChannel) != 0) &&
2070 (image->colorspace == CMYKColorspace) &&
2071 ((MagickRealType) indexes[x] > threshold.index))
2072 indexes[x]=(Quantum) QuantumRange;
2076 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2078 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2083 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2084 #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
2086 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2088 if (proceed == MagickFalse)
2092 image_view=DestroyCacheView(image_view);