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 "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/thread-private.h"
83 #include "magick/threshold.h"
84 #include "magick/transform.h"
85 #include "magick/xml-tree.h"
90 #define ThresholdsFilename "thresholds.xml"
111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115 % A d a p t i v e T h r e s h o l d I m a g e %
119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 % AdaptiveThresholdImage() selects an individual threshold for each pixel
122 % based on the range of intensity values in its local neighborhood. This
123 % allows for thresholding of an image whose global intensity histogram
124 % doesn't contain distinctive peaks.
126 % The format of the AdaptiveThresholdImage method is:
128 % Image *AdaptiveThresholdImage(const Image *image,
129 % const size_t width,const size_t height,
130 % const ssize_t offset,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 offset: the mean offset.
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 ssize_t offset,
147 ExceptionInfo *exception)
149 #define ThresholdImageTag "Threshold/Image"
173 assert(image != (const 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 threshold_image=CloneImage(image,0,0,MagickTrue,exception);
180 if (threshold_image == (Image *) NULL)
181 return((Image *) NULL);
182 if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
184 InheritException(exception,&threshold_image->exception);
185 threshold_image=DestroyImage(threshold_image);
186 return((Image *) NULL);
189 Local adaptive threshold.
193 GetMagickPixelPacket(image,&zero);
194 number_pixels=(MagickRealType) width*height;
195 image_view=AcquireCacheView(image);
196 threshold_view=AcquireCacheView(threshold_image);
197 #if defined(MAGICKCORE_OPENMP_SUPPORT)
198 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
200 for (y=0; y < (ssize_t) image->rows; y++)
205 register const IndexPacket
208 register const PixelPacket
212 *restrict threshold_indexes;
220 if (status == MagickFalse)
222 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
223 height/2L,image->columns+width,height,exception);
224 q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
226 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
231 indexes=GetCacheViewVirtualIndexQueue(image_view);
232 threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
233 for (x=0; x < (ssize_t) image->columns; x++)
239 register const PixelPacket
251 for (v=0; v < (ssize_t) height; v++)
253 for (u=0; u < (ssize_t) width; u++)
256 pixel.green+=r[u].green;
257 pixel.blue+=r[u].blue;
258 pixel.opacity+=r[u].opacity;
259 if (image->colorspace == CMYKColorspace)
260 pixel.index=(MagickRealType) indexes[x+(r-p)+u];
262 r+=image->columns+width;
264 mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
265 mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
266 mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
267 mean.opacity=(MagickRealType) (pixel.opacity/number_pixels+offset);
268 if (image->colorspace == CMYKColorspace)
269 mean.index=(MagickRealType) (pixel.index/number_pixels+offset);
270 q->red=(Quantum) (((MagickRealType) q->red <= mean.red) ?
272 q->green=(Quantum) (((MagickRealType) q->green <= mean.green) ?
274 q->blue=(Quantum) (((MagickRealType) q->blue <= mean.blue) ?
276 q->opacity=(Quantum) (((MagickRealType) q->opacity <= mean.opacity) ?
278 if (image->colorspace == CMYKColorspace)
279 threshold_indexes[x]=(IndexPacket) (((MagickRealType)
280 threshold_indexes[x] <= mean.index) ? 0 : QuantumRange);
284 sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
285 if (sync == MagickFalse)
287 if (image->progress_monitor != (MagickProgressMonitor) NULL)
292 #if defined(MAGICKCORE_OPENMP_SUPPORT)
293 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
295 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
297 if (proceed == MagickFalse)
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 BilevelImageChannel method is:
334 % MagickBooleanType BilevelImage(Image *image,const double threshold)
335 % MagickBooleanType BilevelImageChannel(Image *image,
336 % const ChannelType channel,const double threshold)
338 % A description of each parameter follows:
340 % o image: the image.
342 % o channel: the channel type.
344 % o threshold: define the threshold values.
346 % Aside: You can get the same results as operator using LevelImageChannels()
347 % with the 'threshold' value for both the black_point and the white_point.
351 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
356 status=BilevelImageChannel(image,DefaultChannels,threshold);
360 MagickExport MagickBooleanType BilevelImageChannel(Image *image,
361 const ChannelType channel,const double threshold)
363 #define ThresholdImageTag "Threshold/Image"
380 assert(image != (Image *) NULL);
381 assert(image->signature == MagickSignature);
382 if (image->debug != MagickFalse)
383 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
384 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
387 Bilevel threshold image.
391 exception=(&image->exception);
392 image_view=AcquireCacheView(image);
393 #if defined(MAGICKCORE_OPENMP_SUPPORT)
394 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
396 for (y=0; y < (ssize_t) image->rows; y++)
407 if (status == MagickFalse)
409 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
410 if (q == (PixelPacket *) NULL)
415 indexes=GetCacheViewAuthenticIndexQueue(image_view);
416 if (channel == DefaultChannels)
418 for (x=0; x < (ssize_t) image->columns; x++)
420 q->red=(Quantum) ((MagickRealType) PixelIntensityToQuantum(q) <=
421 threshold ? 0 : QuantumRange);
428 for (x=0; x < (ssize_t) image->columns; x++)
430 if ((channel & RedChannel) != 0)
431 q->red=(Quantum) ((MagickRealType) q->red <= threshold ? 0 :
433 if ((channel & GreenChannel) != 0)
434 q->green=(Quantum) ((MagickRealType) q->green <= threshold ? 0 :
436 if ((channel & BlueChannel) != 0)
437 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold ? 0 :
439 if ((channel & OpacityChannel) != 0)
441 if (image->matte == MagickFalse)
442 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
445 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
446 OpaqueOpacity : TransparentOpacity);
448 if (((channel & IndexChannel) != 0) &&
449 (image->colorspace == CMYKColorspace))
450 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <= threshold ?
454 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
456 if (image->progress_monitor != (MagickProgressMonitor) NULL)
461 #if defined(MAGICKCORE_OPENMP_SUPPORT)
462 #pragma omp critical (MagickCore_BilevelImageChannel)
464 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
466 if (proceed == MagickFalse)
470 image_view=DestroyCacheView(image_view);
475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479 % B l a c k T h r e s h o l d I m a g e %
483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
486 % the threshold into black while leaving all pixels at or above the threshold
489 % The format of the BlackThresholdImage method is:
491 % MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
492 % MagickBooleanType BlackThresholdImageChannel(Image *image,
493 % const ChannelType channel,const char *threshold,
494 % ExceptionInfo *exception)
496 % A description of each parameter follows:
498 % o image: the image.
500 % o channel: the channel or channels to be thresholded.
502 % o threshold: Define the threshold value.
504 % o exception: return any errors or warnings in this structure.
507 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
508 const char *threshold)
513 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
518 MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
519 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
521 #define ThresholdImageTag "Threshold/Image"
544 assert(image != (Image *) NULL);
545 assert(image->signature == MagickSignature);
546 if (image->debug != MagickFalse)
547 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
548 if (thresholds == (const char *) NULL)
550 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
552 GetMagickPixelPacket(image,&threshold);
553 flags=ParseGeometry(thresholds,&geometry_info);
554 threshold.red=geometry_info.rho;
555 threshold.green=geometry_info.sigma;
556 if ((flags & SigmaValue) == 0)
557 threshold.green=threshold.red;
558 threshold.blue=geometry_info.xi;
559 if ((flags & XiValue) == 0)
560 threshold.blue=threshold.red;
561 threshold.opacity=geometry_info.psi;
562 if ((flags & PsiValue) == 0)
563 threshold.opacity=threshold.red;
564 threshold.index=geometry_info.chi;
565 if ((flags & ChiValue) == 0)
566 threshold.index=threshold.red;
567 if ((flags & PercentValue) != 0)
569 threshold.red*=(QuantumRange/100.0);
570 threshold.green*=(QuantumRange/100.0);
571 threshold.blue*=(QuantumRange/100.0);
572 threshold.opacity*=(QuantumRange/100.0);
573 threshold.index*=(QuantumRange/100.0);
576 Black threshold image.
580 image_view=AcquireCacheView(image);
581 #if defined(MAGICKCORE_OPENMP_SUPPORT)
582 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
584 for (y=0; y < (ssize_t) image->rows; y++)
595 if (status == MagickFalse)
597 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
598 if (q == (PixelPacket *) NULL)
603 indexes=GetCacheViewAuthenticIndexQueue(image_view);
604 for (x=0; x < (ssize_t) image->columns; x++)
606 if (channel != DefaultChannels)
608 if (PixelIntensity(q) < MagickPixelIntensity(&threshold))
611 q->green=(Quantum) 0;
613 if (image->colorspace == CMYKColorspace)
614 indexes[x]=(Quantum) 0;
619 if (((channel & RedChannel) != 0) &&
620 ((MagickRealType) q->red < threshold.red))
622 if (((channel & GreenChannel) != 0) &&
623 ((MagickRealType) q->green < threshold.green))
624 q->green=(Quantum) 0;
625 if (((channel & BlueChannel) != 0) &&
626 ((MagickRealType) q->blue < threshold.blue))
628 if (((channel & OpacityChannel) != 0) &&
629 ((MagickRealType) q->opacity < threshold.opacity))
630 q->opacity=(Quantum) 0;
631 if (((channel & IndexChannel) != 0) &&
632 (image->colorspace == CMYKColorspace) &&
633 ((MagickRealType) indexes[x] < threshold.index))
634 indexes[x]=(Quantum) 0;
638 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
640 if (image->progress_monitor != (MagickProgressMonitor) NULL)
645 #if defined(MAGICKCORE_OPENMP_SUPPORT)
646 #pragma omp critical (MagickCore_BlackThresholdImageChannel)
648 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
650 if (proceed == MagickFalse)
654 image_view=DestroyCacheView(image_view);
659 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663 % C l a m p I m a g e %
667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669 % ClampImage() restricts the color range from 0 to the quantum depth.
671 % The format of the ClampImageChannel method is:
673 % MagickBooleanType ClampImage(Image *image)
674 % MagickBooleanType ClampImageChannel(Image *image,
675 % const ChannelType channel)
677 % A description of each parameter follows:
679 % o image: the image.
681 % o channel: the channel type.
685 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
687 #if defined(MAGICKCORE_HDRI_SUPPORT)
690 if (quantum >= QuantumRange)
691 return(QuantumRange);
698 MagickExport MagickBooleanType ClampImage(Image *image)
703 status=ClampImageChannel(image,DefaultChannels);
707 MagickExport MagickBooleanType ClampImageChannel(Image *image,
708 const ChannelType channel)
710 #define ClampImageTag "Clamp/Image"
727 assert(image != (Image *) NULL);
728 assert(image->signature == MagickSignature);
729 if (image->debug != MagickFalse)
730 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
731 if (image->storage_class == PseudoClass)
740 for (i=0; i < (ssize_t) image->colors; i++)
742 q->red=ClampToUnsignedQuantum(q->red);
743 q->green=ClampToUnsignedQuantum(q->green);
744 q->blue=ClampToUnsignedQuantum(q->blue);
745 q->opacity=ClampToUnsignedQuantum(q->opacity);
748 return(SyncImage(image));
755 exception=(&image->exception);
756 image_view=AcquireCacheView(image);
757 #if defined(MAGICKCORE_OPENMP_SUPPORT)
758 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
760 for (y=0; y < (ssize_t) image->rows; y++)
771 if (status == MagickFalse)
773 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
774 if (q == (PixelPacket *) NULL)
779 indexes=GetCacheViewAuthenticIndexQueue(image_view);
780 for (x=0; x < (ssize_t) image->columns; x++)
782 if ((channel & RedChannel) != 0)
783 q->red=ClampToUnsignedQuantum(q->red);
784 if ((channel & GreenChannel) != 0)
785 q->green=ClampToUnsignedQuantum(q->green);
786 if ((channel & BlueChannel) != 0)
787 q->blue=ClampToUnsignedQuantum(q->blue);
788 if ((channel & OpacityChannel) != 0)
789 q->opacity=ClampToUnsignedQuantum(q->opacity);
790 if (((channel & IndexChannel) != 0) &&
791 (image->colorspace == CMYKColorspace))
792 indexes[x]=(IndexPacket) ClampToUnsignedQuantum(indexes[x]);
795 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
797 if (image->progress_monitor != (MagickProgressMonitor) NULL)
802 #if defined(MAGICKCORE_OPENMP_SUPPORT)
803 #pragma omp critical (MagickCore_ClampImageChannel)
805 proceed=SetImageProgress(image,ClampImageTag,progress++,
807 if (proceed == MagickFalse)
811 image_view=DestroyCacheView(image_view);
816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
820 % D e s t r o y T h r e s h o l d M a p %
824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
826 % DestroyThresholdMap() de-allocate the given ThresholdMap
828 % The format of the ListThresholdMaps method is:
830 % ThresholdMap *DestroyThresholdMap(Threshold *map)
832 % A description of each parameter follows.
834 % o map: Pointer to the Threshold map to destroy
837 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
839 assert(map != (ThresholdMap *) NULL);
840 if (map->map_id != (char *) NULL)
841 map->map_id=DestroyString(map->map_id);
842 if (map->description != (char *) NULL)
843 map->description=DestroyString(map->description);
844 if (map->levels != (ssize_t *) NULL)
845 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
846 map=(ThresholdMap *) RelinquishMagickMemory(map);
851 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855 + G e t T h r e s h o l d M a p F i l e %
859 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861 % GetThresholdMapFile() look for a given threshold map name or alias in the
862 % given XML file data, and return the allocated the map when found.
864 % The format of the ListThresholdMaps method is:
866 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
867 % const char *map_id,ExceptionInfo *exception)
869 % A description of each parameter follows.
871 % o xml: The threshold map list in XML format.
873 % o filename: The threshold map XML filename.
875 % o map_id: ID of the map to look for in XML list.
877 % o exception: return any errors or warnings in this structure.
880 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
881 const char *filename,const char *map_id,ExceptionInfo *exception)
899 map = (ThresholdMap *)NULL;
900 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
901 "Loading threshold map file \"%s\" ...",filename);
902 thresholds=NewXMLTree(xml,exception);
903 if ( thresholds == (XMLTreeInfo *)NULL )
906 for( threshold = GetXMLTreeChild(thresholds,"threshold");
907 threshold != (XMLTreeInfo *)NULL;
908 threshold = GetNextXMLTreeTag(threshold) ) {
909 attr = GetXMLTreeAttribute(threshold, "map");
910 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
912 attr = GetXMLTreeAttribute(threshold, "alias");
913 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
916 if ( threshold == (XMLTreeInfo *)NULL ) {
919 description = GetXMLTreeChild(threshold,"description");
920 if ( description == (XMLTreeInfo *)NULL ) {
921 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
922 "XmlMissingElement", "<description>, map \"%s\"", map_id);
923 thresholds = DestroyXMLTree(thresholds);
926 levels = GetXMLTreeChild(threshold,"levels");
927 if ( levels == (XMLTreeInfo *)NULL ) {
928 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
929 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
930 thresholds = DestroyXMLTree(thresholds);
934 /* The map has been found -- Allocate a Threshold Map to return */
935 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
936 if ( map == (ThresholdMap *)NULL )
937 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
938 map->map_id = (char *)NULL;
939 map->description = (char *)NULL;
940 map->levels = (ssize_t *) NULL;
942 /* Assign Basic Attributes */
943 attr = GetXMLTreeAttribute(threshold, "map");
944 if ( attr != (char *)NULL )
945 map->map_id = ConstantString(attr);
947 content = GetXMLTreeContent(description);
948 if ( content != (char *)NULL )
949 map->description = ConstantString(content);
951 attr = GetXMLTreeAttribute(levels, "width");
952 if ( attr == (char *)NULL ) {
953 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
954 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
955 thresholds = DestroyXMLTree(thresholds);
956 map = DestroyThresholdMap(map);
959 map->width = StringToUnsignedLong(attr);
960 if ( map->width == 0 ) {
961 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
962 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
963 thresholds = DestroyXMLTree(thresholds);
964 map = DestroyThresholdMap(map);
968 attr = GetXMLTreeAttribute(levels, "height");
969 if ( attr == (char *)NULL ) {
970 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
971 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
972 thresholds = DestroyXMLTree(thresholds);
973 map = DestroyThresholdMap(map);
976 map->height = StringToUnsignedLong(attr);
977 if ( map->height == 0 ) {
978 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
979 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
980 thresholds = DestroyXMLTree(thresholds);
981 map = DestroyThresholdMap(map);
985 attr = GetXMLTreeAttribute(levels, "divisor");
986 if ( attr == (char *)NULL ) {
987 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
988 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
989 thresholds = DestroyXMLTree(thresholds);
990 map = DestroyThresholdMap(map);
993 map->divisor = (ssize_t) StringToLong(attr);
994 if ( map->divisor < 2 ) {
995 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
996 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
997 thresholds = DestroyXMLTree(thresholds);
998 map = DestroyThresholdMap(map);
1002 /* Allocate theshold levels array */
1003 content = GetXMLTreeContent(levels);
1004 if ( content == (char *)NULL ) {
1005 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1006 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
1007 thresholds = DestroyXMLTree(thresholds);
1008 map = DestroyThresholdMap(map);
1011 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1012 sizeof(*map->levels));
1013 if ( map->levels == (ssize_t *)NULL )
1014 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1015 { /* parse levels into integer array */
1018 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
1019 map->levels[i] = (ssize_t)strtol(content, &p, 10);
1020 if ( p == content ) {
1021 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1022 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1023 thresholds = DestroyXMLTree(thresholds);
1024 map = DestroyThresholdMap(map);
1027 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
1028 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1029 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1030 (double) map->levels[i],map_id);
1031 thresholds = DestroyXMLTree(thresholds);
1032 map = DestroyThresholdMap(map);
1037 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 separated arguments
1415 Default to 2, the single value given, or individual channel values
1418 { /* parse directly as a comma separated 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=RoundToQuantum((MagickRealType) ((l+(t >= threshold))*
1541 (MagickRealType) QuantumRange/levels.red));
1544 t = (ssize_t) (QuantumScale*q->green*(levels.green*d+1));
1546 q->green=RoundToQuantum((MagickRealType) ((l+(t >= threshold))*
1547 (MagickRealType) QuantumRange/levels.green));
1550 t = (ssize_t) (QuantumScale*q->blue*(levels.blue*d+1));
1552 q->blue=RoundToQuantum((MagickRealType) ((l+(t >= threshold))*
1553 (MagickRealType) QuantumRange/levels.blue));
1555 if (levels.opacity) {
1556 t = (ssize_t) ((1.0-QuantumScale*q->opacity)*(levels.opacity*d+1));
1558 q->opacity=RoundToQuantum((MagickRealType) ((1.0-l-(t >= threshold))*
1559 (MagickRealType) QuantumRange/levels.opacity));
1562 t = (ssize_t) (QuantumScale*indexes[x]*(levels.index*d+1));
1564 indexes[x]=(IndexPacket) RoundToQuantum((MagickRealType) ((l+
1565 (t>=threshold))*(MagickRealType) QuantumRange/levels.index));
1569 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1571 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1576 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1577 #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
1579 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1580 if (proceed == MagickFalse)
1584 image_view=DestroyCacheView(image_view);
1586 map=DestroyThresholdMap(map);
1591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1595 % R a n d o m T h r e s h o l d I m a g e %
1599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1601 % RandomThresholdImage() changes the value of individual pixels based on the
1602 % intensity of each pixel compared to a random threshold. The result is a
1603 % low-contrast, two color image.
1605 % The format of the RandomThresholdImage method is:
1607 % MagickBooleanType RandomThresholdImageChannel(Image *image,
1608 % const char *thresholds,ExceptionInfo *exception)
1609 % MagickBooleanType RandomThresholdImageChannel(Image *image,
1610 % const ChannelType channel,const char *thresholds,
1611 % ExceptionInfo *exception)
1613 % A description of each parameter follows:
1615 % o image: the image.
1617 % o channel: the channel or channels to be thresholded.
1619 % o thresholds: a geometry string containing low,high thresholds. If the
1620 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1621 % is performed instead.
1623 % o exception: return any errors or warnings in this structure.
1627 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1628 const char *thresholds,ExceptionInfo *exception)
1633 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
1638 MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
1639 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1641 #define ThresholdImageTag "Threshold/Image"
1666 **restrict random_info;
1671 assert(image != (Image *) NULL);
1672 assert(image->signature == MagickSignature);
1673 if (image->debug != MagickFalse)
1674 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1675 assert(exception != (ExceptionInfo *) NULL);
1676 assert(exception->signature == MagickSignature);
1677 if (thresholds == (const char *) NULL)
1679 GetMagickPixelPacket(image,&threshold);
1681 max_threshold=(MagickRealType) QuantumRange;
1682 flags=ParseGeometry(thresholds,&geometry_info);
1683 min_threshold=geometry_info.rho;
1684 max_threshold=geometry_info.sigma;
1685 if ((flags & SigmaValue) == 0)
1686 max_threshold=min_threshold;
1687 if (strchr(thresholds,'%') != (char *) NULL)
1689 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1690 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1693 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
1694 (min_threshold <= 8))
1697 Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
1699 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
1703 Random threshold image.
1707 if (channel == AllChannels)
1709 if (AcquireImageColormap(image,2) == MagickFalse)
1710 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1712 random_info=AcquireRandomInfoThreadSet();
1713 image_view=AcquireCacheView(image);
1714 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1715 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1717 for (y=0; y < (ssize_t) image->rows; y++)
1720 id = GetOpenMPThreadId();
1725 register IndexPacket
1731 register PixelPacket
1734 if (status == MagickFalse)
1736 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1738 if (q == (PixelPacket *) NULL)
1743 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1744 for (x=0; x < (ssize_t) image->columns; x++)
1752 intensity=(MagickRealType) PixelIntensityToQuantum(q);
1753 if (intensity < min_threshold)
1754 threshold.index=min_threshold;
1755 else if (intensity > max_threshold)
1756 threshold.index=max_threshold;
1758 threshold.index=(MagickRealType)(QuantumRange*
1759 GetPseudoRandomValue(random_info[id]));
1760 index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
1762 *q++=image->colormap[(ssize_t) index];
1764 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1765 if (sync == MagickFalse)
1767 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1772 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1773 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1775 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1777 if (proceed == MagickFalse)
1781 image_view=DestroyCacheView(image_view);
1782 random_info=DestroyRandomInfoThreadSet(random_info);
1785 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1787 InheritException(exception,&image->exception);
1788 return(MagickFalse);
1790 random_info=AcquireRandomInfoThreadSet();
1791 image_view=AcquireCacheView(image);
1792 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1793 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1795 for (y=0; y < (ssize_t) image->rows; y++)
1798 id = GetOpenMPThreadId();
1800 register IndexPacket
1803 register PixelPacket
1809 if (status == MagickFalse)
1811 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1812 if (q == (PixelPacket *) NULL)
1817 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1818 for (x=0; x < (ssize_t) image->columns; x++)
1820 if ((channel & RedChannel) != 0)
1822 if ((MagickRealType) q->red < min_threshold)
1823 threshold.red=min_threshold;
1825 if ((MagickRealType) q->red > max_threshold)
1826 threshold.red=max_threshold;
1828 threshold.red=(MagickRealType) (QuantumRange*
1829 GetPseudoRandomValue(random_info[id]));
1831 if ((channel & GreenChannel) != 0)
1833 if ((MagickRealType) q->green < min_threshold)
1834 threshold.green=min_threshold;
1836 if ((MagickRealType) q->green > max_threshold)
1837 threshold.green=max_threshold;
1839 threshold.green=(MagickRealType) (QuantumRange*
1840 GetPseudoRandomValue(random_info[id]));
1842 if ((channel & BlueChannel) != 0)
1844 if ((MagickRealType) q->blue < min_threshold)
1845 threshold.blue=min_threshold;
1847 if ((MagickRealType) q->blue > max_threshold)
1848 threshold.blue=max_threshold;
1850 threshold.blue=(MagickRealType) (QuantumRange*
1851 GetPseudoRandomValue(random_info[id]));
1853 if ((channel & OpacityChannel) != 0)
1855 if ((MagickRealType) q->opacity < min_threshold)
1856 threshold.opacity=min_threshold;
1858 if ((MagickRealType) q->opacity > max_threshold)
1859 threshold.opacity=max_threshold;
1861 threshold.opacity=(MagickRealType) (QuantumRange*
1862 GetPseudoRandomValue(random_info[id]));
1864 if (((channel & IndexChannel) != 0) &&
1865 (image->colorspace == CMYKColorspace))
1867 if ((MagickRealType) indexes[x] < min_threshold)
1868 threshold.index=min_threshold;
1870 if ((MagickRealType) indexes[x] > max_threshold)
1871 threshold.index=max_threshold;
1873 threshold.index=(MagickRealType) (QuantumRange*
1874 GetPseudoRandomValue(random_info[id]));
1876 if ((channel & RedChannel) != 0)
1877 q->red=(Quantum) ((MagickRealType) q->red <= threshold.red ? 0 :
1879 if ((channel & GreenChannel) != 0)
1880 q->green=(Quantum) ((MagickRealType) q->green <= threshold.green ? 0 :
1882 if ((channel & BlueChannel) != 0)
1883 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold.blue ? 0 :
1885 if ((channel & OpacityChannel) != 0)
1886 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold.opacity ?
1888 if (((channel & IndexChannel) != 0) &&
1889 (image->colorspace == CMYKColorspace))
1890 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <=
1891 threshold.index ? 0 : QuantumRange);
1894 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1896 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1901 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1902 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1904 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1906 if (proceed == MagickFalse)
1910 image_view=DestroyCacheView(image_view);
1911 random_info=DestroyRandomInfoThreadSet(random_info);
1916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1920 % W h i t e T h r e s h o l d I m a g e %
1924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1926 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1927 % the threshold into white while leaving all pixels at or below the threshold
1930 % The format of the WhiteThresholdImage method is:
1932 % MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
1933 % MagickBooleanType WhiteThresholdImageChannel(Image *image,
1934 % const ChannelType channel,const char *threshold,
1935 % ExceptionInfo *exception)
1937 % A description of each parameter follows:
1939 % o image: the image.
1941 % o channel: the channel or channels to be thresholded.
1943 % o threshold: Define the threshold value.
1945 % o exception: return any errors or warnings in this structure.
1948 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1949 const char *threshold)
1954 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
1959 MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
1960 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1962 #define ThresholdImageTag "Threshold/Image"
1985 assert(image != (Image *) NULL);
1986 assert(image->signature == MagickSignature);
1987 if (image->debug != MagickFalse)
1988 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1989 if (thresholds == (const char *) NULL)
1991 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1992 return(MagickFalse);
1993 flags=ParseGeometry(thresholds,&geometry_info);
1994 GetMagickPixelPacket(image,&threshold);
1995 threshold.red=geometry_info.rho;
1996 threshold.green=geometry_info.sigma;
1997 if ((flags & SigmaValue) == 0)
1998 threshold.green=threshold.red;
1999 threshold.blue=geometry_info.xi;
2000 if ((flags & XiValue) == 0)
2001 threshold.blue=threshold.red;
2002 threshold.opacity=geometry_info.psi;
2003 if ((flags & PsiValue) == 0)
2004 threshold.opacity=threshold.red;
2005 threshold.index=geometry_info.chi;
2006 if ((flags & ChiValue) == 0)
2007 threshold.index=threshold.red;
2008 if ((flags & PercentValue) != 0)
2010 threshold.red*=(QuantumRange/100.0);
2011 threshold.green*=(QuantumRange/100.0);
2012 threshold.blue*=(QuantumRange/100.0);
2013 threshold.opacity*=(QuantumRange/100.0);
2014 threshold.index*=(QuantumRange/100.0);
2017 White threshold image.
2021 image_view=AcquireCacheView(image);
2022 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2023 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2025 for (y=0; y < (ssize_t) image->rows; y++)
2027 register IndexPacket
2033 register PixelPacket
2036 if (status == MagickFalse)
2038 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2039 if (q == (PixelPacket *) NULL)
2044 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2045 for (x=0; x < (ssize_t) image->columns; x++)
2047 if (channel != DefaultChannels)
2049 if (PixelIntensity(q) > MagickPixelIntensity(&threshold))
2051 q->red=(Quantum) QuantumRange;
2052 q->green=(Quantum) QuantumRange;
2053 q->blue=(Quantum) QuantumRange;
2054 if (image->colorspace == CMYKColorspace)
2055 indexes[x]=(Quantum) QuantumRange;
2060 if (((channel & RedChannel) != 0) &&
2061 ((MagickRealType) q->red > threshold.red))
2062 q->red=(Quantum) QuantumRange;
2063 if (((channel & GreenChannel) != 0) &&
2064 ((MagickRealType) q->green > threshold.green))
2065 q->green=(Quantum) QuantumRange;
2066 if (((channel & BlueChannel) != 0) &&
2067 ((MagickRealType) q->blue > threshold.blue))
2068 q->blue=(Quantum) QuantumRange;
2069 if (((channel & OpacityChannel) != 0) &&
2070 ((MagickRealType) q->opacity > threshold.opacity))
2071 q->opacity=(Quantum) QuantumRange;
2072 if (((channel & IndexChannel) != 0) &&
2073 (image->colorspace == CMYKColorspace) &&
2074 ((MagickRealType) indexes[x] > threshold.index))
2075 indexes[x]=(Quantum) QuantumRange;
2079 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2081 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2086 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2087 #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
2089 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2091 if (proceed == MagickFalse)
2095 image_view=DestroyCacheView(image_view);