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-2012 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/property.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/configure.h"
52 #include "MagickCore/constitute.h"
53 #include "MagickCore/decorate.h"
54 #include "MagickCore/draw.h"
55 #include "MagickCore/enhance.h"
56 #include "MagickCore/exception.h"
57 #include "MagickCore/exception-private.h"
58 #include "MagickCore/effect.h"
59 #include "MagickCore/fx.h"
60 #include "MagickCore/gem.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/monitor.h"
67 #include "MagickCore/monitor-private.h"
68 #include "MagickCore/montage.h"
69 #include "MagickCore/option.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/quantize.h"
72 #include "MagickCore/quantum.h"
73 #include "MagickCore/random_.h"
74 #include "MagickCore/random-private.h"
75 #include "MagickCore/resize.h"
76 #include "MagickCore/resource_.h"
77 #include "MagickCore/segment.h"
78 #include "MagickCore/shear.h"
79 #include "MagickCore/signature-private.h"
80 #include "MagickCore/string_.h"
81 #include "MagickCore/string-private.h"
82 #include "MagickCore/thread-private.h"
83 #include "MagickCore/threshold.h"
84 #include "MagickCore/token.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/xml-tree.h"
87 #include "MagickCore/xml-tree-private.h"
92 #define ThresholdsFilename "thresholds.xml"
113 Forward declarations.
116 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123 % A d a p t i v e T h r e s h o l d I m a g e %
127 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129 % AdaptiveThresholdImage() selects an individual threshold for each pixel
130 % based on the range of intensity values in its local neighborhood. This
131 % allows for thresholding of an image whose global intensity histogram
132 % doesn't contain distinctive peaks.
134 % The format of the AdaptiveThresholdImage method is:
136 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
137 % const size_t height,const double bias,ExceptionInfo *exception)
139 % A description of each parameter follows:
141 % o image: the image.
143 % o width: the width of the local neighborhood.
145 % o height: the height of the local neighborhood.
147 % o bias: the mean bias.
149 % o exception: return any errors or warnings in this structure.
152 MagickExport Image *AdaptiveThresholdImage(const Image *image,
153 const size_t width,const size_t height,const double bias,
154 ExceptionInfo *exception)
156 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
178 Initialize threshold image attributes.
180 assert(image != (Image *) NULL);
181 assert(image->signature == MagickSignature);
182 if (image->debug != MagickFalse)
183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
184 assert(exception != (ExceptionInfo *) NULL);
185 assert(exception->signature == MagickSignature);
186 if ((width % 2) == 0)
187 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
188 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
190 if (threshold_image == (Image *) NULL)
191 return((Image *) NULL);
192 status=SetImageStorageClass(threshold_image,DirectClass,exception);
193 if (status == MagickFalse)
195 threshold_image=DestroyImage(threshold_image);
196 return((Image *) NULL);
203 number_pixels=(MagickSizeType) width*height;
204 image_view=AcquireCacheView(image);
205 threshold_view=AcquireCacheView(threshold_image);
206 #if defined(MAGICKCORE_OPENMP_SUPPORT)
207 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
209 for (y=0; y < (ssize_t) image->rows; y++)
211 register const Quantum
223 if (status == MagickFalse)
225 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
226 (height/2L),image->columns+width,height,exception);
227 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
229 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
234 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
235 GetPixelChannels(image)*(width/2);
236 for (x=0; x < (ssize_t) image->columns; x++)
241 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
254 register const Quantum
263 channel=GetPixelChannelMapChannel(image,i);
264 traits=GetPixelChannelMapTraits(image,channel);
265 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
266 if ((traits == UndefinedPixelTrait) ||
267 (threshold_traits == UndefinedPixelTrait))
269 if ((threshold_traits & CopyPixelTrait) != 0)
271 SetPixelChannel(threshold_image,channel,p[center+i],q);
276 for (v=0; v < (ssize_t) height; v++)
278 for (u=0; u < (ssize_t) width; u++)
281 pixels+=GetPixelChannels(image);
283 pixels+=image->columns*GetPixelChannels(image);
285 mean=(MagickRealType) (pixel/number_pixels+bias);
286 SetPixelChannel(threshold_image,channel,(Quantum) ((MagickRealType)
287 p[center+i] <= mean ? 0 : QuantumRange),q);
289 p+=GetPixelChannels(image);
290 q+=GetPixelChannels(threshold_image);
292 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
294 if (image->progress_monitor != (MagickProgressMonitor) NULL)
299 #if defined(MAGICKCORE_OPENMP_SUPPORT)
300 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
302 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
304 if (proceed == MagickFalse)
308 threshold_image->type=image->type;
309 threshold_view=DestroyCacheView(threshold_view);
310 image_view=DestroyCacheView(image_view);
311 if (status == MagickFalse)
312 threshold_image=DestroyImage(threshold_image);
313 return(threshold_image);
317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
321 % B i l e v e l I m a g e %
325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327 % BilevelImage() changes the value of individual pixels based on the
328 % intensity of each pixel channel. The result is a high-contrast image.
330 % More precisely each channel value of the image is 'thresholded' so that if
331 % it is equal to or less than the given value it is set to zero, while any
332 % value greater than that give is set to it maximum or QuantumRange.
334 % This function is what is used to implement the "-threshold" operator for
335 % the command line API.
337 % If the default channel setting is given the image is thresholded using just
338 % the gray 'intensity' of the image, rather than the individual channels.
340 % The format of the BilevelImage method is:
342 % MagickBooleanType BilevelImage(Image *image,const double threshold,
343 % ExceptionInfo *exception)
345 % A description of each parameter follows:
347 % o image: the image.
349 % o threshold: define the threshold values.
351 % o exception: return any errors or warnings in this structure.
353 % Aside: You can get the same results as operator using LevelImages()
354 % with the 'threshold' value for both the black_point and the white_point.
357 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
358 ExceptionInfo *exception)
360 #define ThresholdImageTag "Threshold/Image"
374 assert(image != (Image *) NULL);
375 assert(image->signature == MagickSignature);
376 if (image->debug != MagickFalse)
377 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
378 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
381 Bilevel threshold image.
385 image_view=AcquireCacheView(image);
386 #if defined(MAGICKCORE_OPENMP_SUPPORT)
387 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
389 for (y=0; y < (ssize_t) image->rows; y++)
397 if (status == MagickFalse)
399 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
400 if (q == (Quantum *) NULL)
405 for (x=0; x < (ssize_t) image->columns; x++)
410 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
418 channel=GetPixelChannelMapChannel(image,i);
419 traits=GetPixelChannelMapTraits(image,channel);
420 if ((traits & UpdatePixelTrait) != 0)
421 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
424 q+=GetPixelChannels(image);
426 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
428 if (image->progress_monitor != (MagickProgressMonitor) NULL)
433 #if defined(MAGICKCORE_OPENMP_SUPPORT)
434 #pragma omp critical (MagickCore_BilevelImage)
436 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
438 if (proceed == MagickFalse)
442 image_view=DestroyCacheView(image_view);
447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
451 % B l a c k T h r e s h o l d I m a g e %
455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
458 % the threshold into black while leaving all pixels at or above the threshold
461 % The format of the BlackThresholdImage method is:
463 % MagickBooleanType BlackThresholdImage(Image *image,
464 % const char *threshold,ExceptionInfo *exception)
466 % A description of each parameter follows:
468 % o image: the image.
470 % o threshold: define the threshold value.
472 % o exception: return any errors or warnings in this structure.
475 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
476 const char *thresholds,ExceptionInfo *exception)
478 #define ThresholdImageTag "Threshold/Image"
504 assert(image != (Image *) NULL);
505 assert(image->signature == MagickSignature);
506 if (image->debug != MagickFalse)
507 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
508 if (thresholds == (const char *) NULL)
510 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
512 flags=ParseGeometry(thresholds,&geometry_info);
513 for (i=0; i < 5; i++)
514 threshold[i]=geometry_info.rho;
515 if ((flags & SigmaValue) != 0)
516 threshold[1]=geometry_info.sigma;
517 if ((flags & XiValue) != 0)
518 threshold[2]=geometry_info.xi;
519 if ((flags & PsiValue) != 0)
520 threshold[3]=geometry_info.psi;
521 if ((flags & ChiValue) != 0)
522 threshold[4]=geometry_info.chi;
523 if ((flags & PercentValue) != 0)
524 for (i=0; i < 5; i++)
525 threshold[i]*=(QuantumRange/100.0);
527 White threshold image.
531 image_view=AcquireCacheView(image);
532 #if defined(MAGICKCORE_OPENMP_SUPPORT)
533 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
535 for (y=0; y < (ssize_t) image->rows; y++)
543 if (status == MagickFalse)
545 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
546 if (q == (Quantum *) NULL)
551 for (x=0; x < (ssize_t) image->columns; x++)
560 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
568 channel=GetPixelChannelMapChannel(image,i);
569 traits=GetPixelChannelMapTraits(image,channel);
570 if ((traits & UpdatePixelTrait) == 0)
572 if ((MagickRealType) q[i] < threshold[n++ % 5])
575 q+=GetPixelChannels(image);
577 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
579 if (image->progress_monitor != (MagickProgressMonitor) NULL)
584 #if defined(MAGICKCORE_OPENMP_SUPPORT)
585 #pragma omp critical (MagickCore_WhiteThresholdImage)
587 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
589 if (proceed == MagickFalse)
593 image_view=DestroyCacheView(image_view);
598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
602 % C l a m p I m a g e %
606 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
608 % ClampImage() restricts the color range from 0 to the quantum depth.
610 % The format of the ClampImage method is:
612 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
614 % A description of each parameter follows:
616 % o image: the image.
618 % o exception: return any errors or warnings in this structure.
622 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
624 #if defined(MAGICKCORE_HDRI_SUPPORT)
627 if (quantum >= QuantumRange)
628 return(QuantumRange);
635 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
637 #define ClampImageTag "Clamp/Image"
651 assert(image != (Image *) NULL);
652 assert(image->signature == MagickSignature);
653 if (image->debug != MagickFalse)
654 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
655 if (image->storage_class == PseudoClass)
664 for (i=0; i < (ssize_t) image->colors; i++)
666 q->red=ClampToUnsignedQuantum(q->red);
667 q->green=ClampToUnsignedQuantum(q->green);
668 q->blue=ClampToUnsignedQuantum(q->blue);
669 q->alpha=ClampToUnsignedQuantum(q->alpha);
672 return(SyncImage(image,exception));
679 image_view=AcquireCacheView(image);
680 #if defined(MAGICKCORE_OPENMP_SUPPORT)
681 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
683 for (y=0; y < (ssize_t) image->rows; y++)
691 if (status == MagickFalse)
693 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
694 if (q == (Quantum *) NULL)
699 for (x=0; x < (ssize_t) image->columns; x++)
704 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
712 channel=GetPixelChannelMapChannel(image,i);
713 traits=GetPixelChannelMapTraits(image,channel);
714 if (traits == UndefinedPixelTrait)
716 q[i]=ClampToUnsignedQuantum(q[i]);
718 q+=GetPixelChannels(image);
720 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
722 if (image->progress_monitor != (MagickProgressMonitor) NULL)
727 #if defined(MAGICKCORE_OPENMP_SUPPORT)
728 #pragma omp critical (MagickCore_ClampImage)
730 proceed=SetImageProgress(image,ClampImageTag,progress++,
732 if (proceed == MagickFalse)
736 image_view=DestroyCacheView(image_view);
741 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
745 % D e s t r o y T h r e s h o l d M a p %
749 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751 % DestroyThresholdMap() de-allocate the given ThresholdMap
753 % The format of the ListThresholdMaps method is:
755 % ThresholdMap *DestroyThresholdMap(Threshold *map)
757 % A description of each parameter follows.
759 % o map: Pointer to the Threshold map to destroy
762 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
764 assert(map != (ThresholdMap *) NULL);
765 if (map->map_id != (char *) NULL)
766 map->map_id=DestroyString(map->map_id);
767 if (map->description != (char *) NULL)
768 map->description=DestroyString(map->description);
769 if (map->levels != (ssize_t *) NULL)
770 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
771 map=(ThresholdMap *) RelinquishMagickMemory(map);
776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780 % G e t T h r e s h o l d M a p %
784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
786 % GetThresholdMap() loads and searches one or more threshold map files for the
787 % map matching the given name or alias.
789 % The format of the GetThresholdMap method is:
791 % ThresholdMap *GetThresholdMap(const char *map_id,
792 % ExceptionInfo *exception)
794 % A description of each parameter follows.
796 % o map_id: ID of the map to look for.
798 % o exception: return any errors or warnings in this structure.
801 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
802 ExceptionInfo *exception)
813 map=(ThresholdMap *)NULL;
814 options=GetConfigureOptions(ThresholdsFilename,exception);
815 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
816 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
817 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
818 GetStringInfoPath(option),map_id,exception);
819 options=DestroyConfigureOptions(options);
824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
828 + G e t T h r e s h o l d M a p F i l e %
832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
834 % GetThresholdMapFile() look for a given threshold map name or alias in the
835 % given XML file data, and return the allocated the map when found.
837 % The format of the ListThresholdMaps method is:
839 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
840 % const char *map_id,ExceptionInfo *exception)
842 % A description of each parameter follows.
844 % o xml: The threshold map list in XML format.
846 % o filename: The threshold map XML filename.
848 % o map_id: ID of the map to look for in XML list.
850 % o exception: return any errors or warnings in this structure.
853 static ThresholdMap *GetThresholdMapFile(const char *xml,
854 const char *filename,const char *map_id,ExceptionInfo *exception)
878 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
879 "Loading threshold map file \"%s\" ...",filename);
880 map=(ThresholdMap *) NULL;
881 thresholds=NewXMLTree(xml,exception);
882 if (thresholds == (XMLTreeInfo *) NULL)
884 for (threshold=GetXMLTreeChild(thresholds,"threshold");
885 threshold != (XMLTreeInfo *) NULL;
886 threshold=GetNextXMLTreeTag(threshold))
888 attribute=GetXMLTreeAttribute(threshold,"map");
889 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
891 attribute=GetXMLTreeAttribute(threshold,"alias");
892 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
895 if (threshold == (XMLTreeInfo *) NULL)
897 description=GetXMLTreeChild(threshold,"description");
898 if (description == (XMLTreeInfo *) NULL)
900 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
901 "XmlMissingElement", "<description>, map \"%s\"",map_id);
902 thresholds=DestroyXMLTree(thresholds);
905 levels=GetXMLTreeChild(threshold,"levels");
906 if (levels == (XMLTreeInfo *) NULL)
908 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
909 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
910 thresholds=DestroyXMLTree(thresholds);
913 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
914 if (map == (ThresholdMap *) NULL)
915 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
916 map->map_id=(char *) NULL;
917 map->description=(char *) NULL;
918 map->levels=(ssize_t *) NULL;
919 attribute=GetXMLTreeAttribute(threshold,"map");
920 if (attribute != (char *) NULL)
921 map->map_id=ConstantString(attribute);
922 content=GetXMLTreeContent(description);
923 if (content != (char *) NULL)
924 map->description=ConstantString(content);
925 attribute=GetXMLTreeAttribute(levels,"width");
926 if (attribute == (char *) NULL)
928 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
929 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
930 thresholds=DestroyXMLTree(thresholds);
931 map=DestroyThresholdMap(map);
934 map->width=StringToUnsignedLong(attribute);
937 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
938 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
939 thresholds=DestroyXMLTree(thresholds);
940 map=DestroyThresholdMap(map);
943 attribute=GetXMLTreeAttribute(levels,"height");
944 if (attribute == (char *) NULL)
946 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
947 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
948 thresholds=DestroyXMLTree(thresholds);
949 map=DestroyThresholdMap(map);
952 map->height=StringToUnsignedLong(attribute);
953 if (map->height == 0)
955 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
956 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
957 thresholds=DestroyXMLTree(thresholds);
958 map=DestroyThresholdMap(map);
961 attribute=GetXMLTreeAttribute(levels,"divisor");
962 if (attribute == (char *) NULL)
964 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
965 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
966 thresholds=DestroyXMLTree(thresholds);
967 map=DestroyThresholdMap(map);
970 map->divisor=(ssize_t) StringToLong(attribute);
971 if (map->divisor < 2)
973 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
974 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
975 thresholds=DestroyXMLTree(thresholds);
976 map=DestroyThresholdMap(map);
979 content=GetXMLTreeContent(levels);
980 if (content == (char *) NULL)
982 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
983 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
984 thresholds=DestroyXMLTree(thresholds);
985 map=DestroyThresholdMap(map);
988 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
989 sizeof(*map->levels));
990 if (map->levels == (ssize_t *) NULL)
991 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
992 for (i=0; i < (ssize_t) (map->width*map->height); i++)
994 map->levels[i]=(ssize_t) strtol(content,&p,10);
997 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
998 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
999 thresholds=DestroyXMLTree(thresholds);
1000 map=DestroyThresholdMap(map);
1003 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1005 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1006 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1007 (double) map->levels[i],map_id);
1008 thresholds=DestroyXMLTree(thresholds);
1009 map=DestroyThresholdMap(map);
1014 value=(double) strtol(content,&p,10);
1018 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1019 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1020 thresholds=DestroyXMLTree(thresholds);
1021 map=DestroyThresholdMap(map);
1024 thresholds=DestroyXMLTree(thresholds);
1029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1033 + L i s t T h r e s h o l d M a p F i l e %
1037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1039 % ListThresholdMapFile() lists the threshold maps and their descriptions
1040 % in the given XML file data.
1042 % The format of the ListThresholdMaps method is:
1044 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1045 % const char *filename,ExceptionInfo *exception)
1047 % A description of each parameter follows.
1049 % o file: An pointer to the output FILE.
1051 % o xml: The threshold map list in XML format.
1053 % o filename: The threshold map XML filename.
1055 % o exception: return any errors or warnings in this structure.
1058 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1059 const char *filename,ExceptionInfo *exception)
1071 assert( xml != (char *)NULL );
1072 assert( file != (FILE *)NULL );
1073 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1074 "Loading threshold map file \"%s\" ...",filename);
1075 thresholds=NewXMLTree(xml,exception);
1076 if ( thresholds == (XMLTreeInfo *)NULL )
1077 return(MagickFalse);
1078 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1079 (void) FormatLocaleFile(file,
1080 "----------------------------------------------------\n");
1081 threshold=GetXMLTreeChild(thresholds,"threshold");
1082 for ( ; threshold != (XMLTreeInfo *) NULL;
1083 threshold=GetNextXMLTreeTag(threshold))
1085 map=GetXMLTreeAttribute(threshold,"map");
1086 if (map == (char *) NULL)
1088 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1089 "XmlMissingAttribute", "<map>");
1090 thresholds=DestroyXMLTree(thresholds);
1091 return(MagickFalse);
1093 alias=GetXMLTreeAttribute(threshold,"alias");
1094 description=GetXMLTreeChild(threshold,"description");
1095 if (description == (XMLTreeInfo *) NULL)
1097 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1098 "XmlMissingElement", "<description>, map \"%s\"",map);
1099 thresholds=DestroyXMLTree(thresholds);
1100 return(MagickFalse);
1102 content=GetXMLTreeContent(description);
1103 if (content == (char *) NULL)
1105 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1106 "XmlMissingContent", "<description>, map \"%s\"", map);
1107 thresholds=DestroyXMLTree(thresholds);
1108 return(MagickFalse);
1110 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1113 thresholds=DestroyXMLTree(thresholds);
1118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1122 % L i s t T h r e s h o l d M a p s %
1126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1128 % ListThresholdMaps() lists the threshold maps and their descriptions
1129 % as defined by "threshold.xml" to a file.
1131 % The format of the ListThresholdMaps method is:
1133 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1135 % A description of each parameter follows.
1137 % o file: An pointer to the output FILE.
1139 % o exception: return any errors or warnings in this structure.
1142 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1143 ExceptionInfo *exception)
1155 if (file == (FILE *) NULL)
1157 options=GetConfigureOptions(ThresholdsFilename,exception);
1158 (void) FormatLocaleFile(file,
1159 "\n Threshold Maps for Ordered Dither Operations\n");
1160 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1161 (const StringInfo *) NULL)
1163 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1164 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1165 GetStringInfoPath(option),exception);
1167 options=DestroyConfigureOptions(options);
1168 return(status != 0 ? MagickTrue : MagickFalse);
1172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176 % O r d e r e d P o s t e r i z e I m a g e %
1180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182 % OrderedPosterizeImage() will perform a ordered dither based on a number
1183 % of pre-defined dithering threshold maps, but over multiple intensity
1184 % levels, which can be different for different channels, according to the
1187 % The format of the OrderedPosterizeImage method is:
1189 % MagickBooleanType OrderedPosterizeImage(Image *image,
1190 % const char *threshold_map,ExceptionInfo *exception)
1192 % A description of each parameter follows:
1194 % o image: the image.
1196 % o threshold_map: A string containing the name of the threshold dither
1197 % map to use, followed by zero or more numbers representing the number
1198 % of color levels tho dither between.
1200 % Any level number less than 2 will be equivalent to 2, and means only
1201 % binary dithering will be applied to each color channel.
1203 % No numbers also means a 2 level (bitmap) dither will be applied to all
1204 % channels, while a single number is the number of levels applied to each
1205 % channel in sequence. More numbers will be applied in turn to each of
1206 % the color channels.
1208 % For example: "o3x3,6" will generate a 6 level posterization of the
1209 % image with a ordered 3x3 diffused pixel dither being applied between
1210 % each level. While checker,8,8,4 will produce a 332 colormaped image
1211 % with only a single checkerboard hash pattern (50% grey) between each
1212 % color level, to basically double the number of color levels with
1213 % a bare minimim of dithering.
1215 % o exception: return any errors or warnings in this structure.
1218 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1219 const char *threshold_map,ExceptionInfo *exception)
1221 #define DitherImageTag "Dither/Image"
1227 token[MaxTextExtent];
1239 levels[CompositePixelChannel];
1250 assert(image != (Image *) NULL);
1251 assert(image->signature == MagickSignature);
1252 if (image->debug != MagickFalse)
1253 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1254 assert(exception != (ExceptionInfo *) NULL);
1255 assert(exception->signature == MagickSignature);
1256 if (threshold_map == (const char *) NULL)
1258 p=(char *) threshold_map;
1259 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1263 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1266 if ((p-threshold_map) >= (MaxTextExtent-1))
1268 token[p-threshold_map]=(*p);
1271 token[p-threshold_map]='\0';
1272 map=GetThresholdMap(token,exception);
1273 if (map == (ThresholdMap *) NULL)
1275 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1276 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1277 return(MagickFalse);
1279 for (i=0; i < MaxPixelChannels; i++)
1281 p=strchr((char *) threshold_map,',');
1282 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1283 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1285 GetMagickToken(p,&p,token);
1287 GetMagickToken(p,&p,token);
1288 levels[i]=StringToDouble(token,(char **) NULL);
1290 for (i=0; i < MaxPixelChannels; i++)
1291 if (fabs(levels[i]) >= 1)
1293 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1294 return(MagickFalse);
1297 image_view=AcquireCacheView(image);
1298 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1299 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1301 for (y=0; y < (ssize_t) image->rows; y++)
1309 if (status == MagickFalse)
1311 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1312 if (q == (Quantum *) NULL)
1317 for (x=0; x < (ssize_t) image->columns; x++)
1326 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1338 channel=GetPixelChannelMapChannel(image,i);
1339 traits=GetPixelChannelMapTraits(image,channel);
1340 if ((traits & UpdatePixelTrait) == 0)
1342 if (fabs(levels[n++]) < MagickEpsilon)
1344 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1345 level=threshold/(map->divisor-1);
1346 threshold-=level*(map->divisor-1);
1347 q[i]=RoundToQuantum((level+(threshold >= map->levels[(x % map->width)+
1348 map->width*(y % map->height)]))*QuantumRange/levels[n]);
1351 q+=GetPixelChannels(image);
1353 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1355 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1360 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1361 #pragma omp critical (MagickCore_OrderedPosterizeImage)
1363 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1364 if (proceed == MagickFalse)
1368 image_view=DestroyCacheView(image_view);
1369 map=DestroyThresholdMap(map);
1374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378 % R a n d o m T h r e s h o l d I m a g e %
1382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1384 % RandomThresholdImage() changes the value of individual pixels based on the
1385 % intensity of each pixel compared to a random threshold. The result is a
1386 % low-contrast, two color image.
1388 % The format of the RandomThresholdImage method is:
1390 % MagickBooleanType RandomThresholdImage(Image *image,
1391 % const char *thresholds,ExceptionInfo *exception)
1393 % A description of each parameter follows:
1395 % o image: the image.
1397 % o thresholds: a geometry string containing low,high thresholds. If the
1398 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1399 % is performed instead.
1401 % o exception: return any errors or warnings in this structure.
1404 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1405 const char *thresholds,ExceptionInfo *exception)
1407 #define ThresholdImageTag "Threshold/Image"
1432 **restrict random_info;
1437 assert(image != (Image *) NULL);
1438 assert(image->signature == MagickSignature);
1439 if (image->debug != MagickFalse)
1440 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1441 assert(exception != (ExceptionInfo *) NULL);
1442 assert(exception->signature == MagickSignature);
1443 if (thresholds == (const char *) NULL)
1445 GetPixelInfo(image,&threshold);
1447 max_threshold=(MagickRealType) QuantumRange;
1448 flags=ParseGeometry(thresholds,&geometry_info);
1449 min_threshold=geometry_info.rho;
1450 max_threshold=geometry_info.sigma;
1451 if ((flags & SigmaValue) == 0)
1452 max_threshold=min_threshold;
1453 if (strchr(thresholds,'%') != (char *) NULL)
1455 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1456 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1459 Random threshold image.
1463 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1464 return(MagickFalse);
1465 random_info=AcquireRandomInfoThreadSet();
1466 image_view=AcquireCacheView(image);
1467 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1468 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1470 for (y=0; y < (ssize_t) image->rows; y++)
1473 id = GetOpenMPThreadId();
1481 if (status == MagickFalse)
1483 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1484 if (q == (Quantum *) NULL)
1489 for (x=0; x < (ssize_t) image->columns; x++)
1494 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1505 channel=GetPixelChannelMapChannel(image,i);
1506 traits=GetPixelChannelMapTraits(image,channel);
1507 if ((traits & UpdatePixelTrait) == 0)
1509 if ((MagickRealType) q[i] < min_threshold)
1510 threshold=min_threshold;
1512 if ((MagickRealType) q[i] > max_threshold)
1513 threshold=max_threshold;
1515 threshold=(MagickRealType) (QuantumRange*
1516 GetPseudoRandomValue(random_info[id]));
1517 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
1520 q+=GetPixelChannels(image);
1522 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1529 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1530 #pragma omp critical (MagickCore_RandomThresholdImage)
1532 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1534 if (proceed == MagickFalse)
1538 image_view=DestroyCacheView(image_view);
1539 random_info=DestroyRandomInfoThreadSet(random_info);
1544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1548 % W h i t e T h r e s h o l d I m a g e %
1552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1554 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1555 % the threshold into white while leaving all pixels at or below the threshold
1558 % The format of the WhiteThresholdImage method is:
1560 % MagickBooleanType WhiteThresholdImage(Image *image,
1561 % const char *threshold,ExceptionInfo *exception)
1563 % A description of each parameter follows:
1565 % o image: the image.
1567 % o threshold: Define the threshold value.
1569 % o exception: return any errors or warnings in this structure.
1572 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1573 const char *thresholds,ExceptionInfo *exception)
1575 #define ThresholdImageTag "Threshold/Image"
1601 assert(image != (Image *) NULL);
1602 assert(image->signature == MagickSignature);
1603 if (image->debug != MagickFalse)
1604 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1605 if (thresholds == (const char *) NULL)
1607 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1608 return(MagickFalse);
1609 flags=ParseGeometry(thresholds,&geometry_info);
1610 for (i=0; i < 5; i++)
1611 threshold[i]=geometry_info.rho;
1612 if ((flags & SigmaValue) != 0)
1613 threshold[1]=geometry_info.sigma;
1614 if ((flags & XiValue) != 0)
1615 threshold[2]=geometry_info.xi;
1616 if ((flags & PsiValue) != 0)
1617 threshold[3]=geometry_info.psi;
1618 if ((flags & ChiValue) != 0)
1619 threshold[4]=geometry_info.chi;
1620 if ((flags & PercentValue) != 0)
1621 for (i=0; i < 5; i++)
1622 threshold[i]*=(QuantumRange/100.0);
1624 White threshold image.
1628 image_view=AcquireCacheView(image);
1629 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1630 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1632 for (y=0; y < (ssize_t) image->rows; y++)
1640 if (status == MagickFalse)
1642 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1643 if (q == (Quantum *) NULL)
1648 for (x=0; x < (ssize_t) image->columns; x++)
1657 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1665 channel=GetPixelChannelMapChannel(image,i);
1666 traits=GetPixelChannelMapTraits(image,channel);
1667 if ((traits & UpdatePixelTrait) == 0)
1669 if ((MagickRealType) q[i] > threshold[n++ % 5])
1672 q+=GetPixelChannels(image);
1674 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1676 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1681 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1682 #pragma omp critical (MagickCore_WhiteThresholdImage)
1684 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1686 if (proceed == MagickFalse)
1690 image_view=DestroyCacheView(image_view);