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/colorspace-private.h"
52 #include "MagickCore/configure.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/gem.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/monitor.h"
68 #include "MagickCore/monitor-private.h"
69 #include "MagickCore/montage.h"
70 #include "MagickCore/option.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/quantize.h"
73 #include "MagickCore/quantum.h"
74 #include "MagickCore/random_.h"
75 #include "MagickCore/random-private.h"
76 #include "MagickCore/resize.h"
77 #include "MagickCore/resource_.h"
78 #include "MagickCore/segment.h"
79 #include "MagickCore/shear.h"
80 #include "MagickCore/signature-private.h"
81 #include "MagickCore/string_.h"
82 #include "MagickCore/string-private.h"
83 #include "MagickCore/thread-private.h"
84 #include "MagickCore/threshold.h"
85 #include "MagickCore/token.h"
86 #include "MagickCore/transform.h"
87 #include "MagickCore/xml-tree.h"
88 #include "MagickCore/xml-tree-private.h"
93 #define ThresholdsFilename "thresholds.xml"
114 Forward declarations.
117 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
124 % A d a p t i v e T h r e s h o l d I m a g e %
128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
130 % AdaptiveThresholdImage() selects an individual threshold for each pixel
131 % based on the range of intensity values in its local neighborhood. This
132 % allows for thresholding of an image whose global intensity histogram
133 % doesn't contain distinctive peaks.
135 % The format of the AdaptiveThresholdImage method is:
137 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
138 % const size_t height,const double bias,ExceptionInfo *exception)
140 % A description of each parameter follows:
142 % o image: the image.
144 % o width: the width of the local neighborhood.
146 % o height: the height of the local neighborhood.
148 % o bias: the mean bias.
150 % o exception: return any errors or warnings in this structure.
153 MagickExport Image *AdaptiveThresholdImage(const Image *image,
154 const size_t width,const size_t height,const double bias,
155 ExceptionInfo *exception)
157 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
179 Initialize threshold image attributes.
181 assert(image != (Image *) NULL);
182 assert(image->signature == MagickSignature);
183 if (image->debug != MagickFalse)
184 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185 assert(exception != (ExceptionInfo *) NULL);
186 assert(exception->signature == MagickSignature);
187 if ((width % 2) == 0)
188 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
189 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
191 if (threshold_image == (Image *) NULL)
192 return((Image *) NULL);
193 status=SetImageStorageClass(threshold_image,DirectClass,exception);
194 if (status == MagickFalse)
196 threshold_image=DestroyImage(threshold_image);
197 return((Image *) NULL);
204 number_pixels=(MagickSizeType) width*height;
205 image_view=AcquireVirtualCacheView(image,exception);
206 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
207 #if defined(MAGICKCORE_OPENMP_SUPPORT)
208 #pragma omp parallel for schedule(static,4) shared(progress,status) \
209 dynamic_number_threads(image,image->columns,image->rows,1)
211 for (y=0; y < (ssize_t) image->rows; y++)
213 register const Quantum
225 if (status == MagickFalse)
227 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
228 (height/2L),image->columns+width,height,exception);
229 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
231 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
236 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
237 GetPixelChannels(image)*(width/2);
238 for (x=0; x < (ssize_t) image->columns; x++)
243 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
256 register const Quantum
265 channel=GetPixelChannelChannel(image,i);
266 traits=GetPixelChannelTraits(image,channel);
267 threshold_traits=GetPixelChannelTraits(threshold_image,channel);
268 if ((traits == UndefinedPixelTrait) ||
269 (threshold_traits == UndefinedPixelTrait))
271 if (((threshold_traits & CopyPixelTrait) != 0) ||
272 (GetPixelMask(image,p) != 0))
274 SetPixelChannel(threshold_image,channel,p[center+i],q);
279 for (v=0; v < (ssize_t) height; v++)
281 for (u=0; u < (ssize_t) width; u++)
284 pixels+=GetPixelChannels(image);
286 pixels+=image->columns*GetPixelChannels(image);
288 mean=(double) (pixel/number_pixels+bias);
289 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
290 p[center+i] <= mean ? 0 : QuantumRange),q);
292 p+=GetPixelChannels(image);
293 q+=GetPixelChannels(threshold_image);
295 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
297 if (image->progress_monitor != (MagickProgressMonitor) NULL)
302 #if defined(MAGICKCORE_OPENMP_SUPPORT)
303 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
305 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
307 if (proceed == MagickFalse)
311 threshold_image->type=image->type;
312 threshold_view=DestroyCacheView(threshold_view);
313 image_view=DestroyCacheView(image_view);
314 if (status == MagickFalse)
315 threshold_image=DestroyImage(threshold_image);
316 return(threshold_image);
320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324 % B i l e v e l I m a g e %
328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330 % BilevelImage() changes the value of individual pixels based on the
331 % intensity of each pixel channel. The result is a high-contrast image.
333 % More precisely each channel value of the image is 'thresholded' so that if
334 % it is equal to or less than the given value it is set to zero, while any
335 % value greater than that give is set to it maximum or QuantumRange.
337 % This function is what is used to implement the "-threshold" operator for
338 % the command line API.
340 % If the default channel setting is given the image is thresholded using just
341 % the gray 'intensity' of the image, rather than the individual channels.
343 % The format of the BilevelImage method is:
345 % MagickBooleanType BilevelImage(Image *image,const double threshold,
346 % ExceptionInfo *exception)
348 % A description of each parameter follows:
350 % o image: the image.
352 % o threshold: define the threshold values.
354 % o exception: return any errors or warnings in this structure.
356 % Aside: You can get the same results as operator using LevelImages()
357 % with the 'threshold' value for both the black_point and the white_point.
360 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
361 ExceptionInfo *exception)
363 #define ThresholdImageTag "Threshold/Image"
377 assert(image != (Image *) NULL);
378 assert(image->signature == MagickSignature);
379 if (image->debug != MagickFalse)
380 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
381 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
384 Bilevel threshold image.
388 image_view=AcquireAuthenticCacheView(image,exception);
389 #if defined(MAGICKCORE_OPENMP_SUPPORT)
390 #pragma omp parallel for schedule(static,4) shared(progress,status) \
391 dynamic_number_threads(image,image->columns,image->rows,1)
393 for (y=0; y < (ssize_t) image->rows; y++)
401 if (status == MagickFalse)
403 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
404 if (q == (Quantum *) NULL)
409 for (x=0; x < (ssize_t) image->columns; x++)
417 if (GetPixelMask(image,q) != 0)
419 q+=GetPixelChannels(image);
422 pixel=GetPixelIntensity(image,q);
423 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
431 channel=GetPixelChannelChannel(image,i);
432 traits=GetPixelChannelTraits(image,channel);
433 if ((traits & UpdatePixelTrait) == 0)
435 if (image->channel_mask != DefaultChannels)
437 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
439 q+=GetPixelChannels(image);
441 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
443 if (image->progress_monitor != (MagickProgressMonitor) NULL)
448 #if defined(MAGICKCORE_OPENMP_SUPPORT)
449 #pragma omp critical (MagickCore_BilevelImage)
451 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
453 if (proceed == MagickFalse)
457 image_view=DestroyCacheView(image_view);
462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466 % B l a c k T h r e s h o l d I m a g e %
470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
473 % the threshold into black while leaving all pixels at or above the threshold
476 % The format of the BlackThresholdImage method is:
478 % MagickBooleanType BlackThresholdImage(Image *image,
479 % const char *threshold,ExceptionInfo *exception)
481 % A description of each parameter follows:
483 % o image: the image.
485 % o threshold: define the threshold value.
487 % o exception: return any errors or warnings in this structure.
490 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
491 const char *thresholds,ExceptionInfo *exception)
493 #define ThresholdImageTag "Threshold/Image"
516 assert(image != (Image *) NULL);
517 assert(image->signature == MagickSignature);
518 if (image->debug != MagickFalse)
519 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
520 if (thresholds == (const char *) NULL)
522 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
524 if (IsGrayColorspace(image->colorspace) != MagickFalse)
525 (void) TransformImageColorspace(image,RGBColorspace,exception);
526 GetPixelInfo(image,&threshold);
527 flags=ParseGeometry(thresholds,&geometry_info);
528 threshold.red=geometry_info.rho;
529 threshold.green=geometry_info.rho;
530 threshold.blue=geometry_info.rho;
531 threshold.black=geometry_info.rho;
532 threshold.alpha=100.0;
533 if ((flags & SigmaValue) != 0)
534 threshold.green=geometry_info.sigma;
535 if ((flags & XiValue) != 0)
536 threshold.blue=geometry_info.xi;
537 if ((flags & PsiValue) != 0)
538 threshold.alpha=geometry_info.psi;
539 if (threshold.colorspace == CMYKColorspace)
541 if ((flags & PsiValue) != 0)
542 threshold.black=geometry_info.psi;
543 if ((flags & ChiValue) != 0)
544 threshold.alpha=geometry_info.chi;
546 if ((flags & PercentValue) != 0)
548 threshold.red*=(QuantumRange/100.0);
549 threshold.green*=(QuantumRange/100.0);
550 threshold.blue*=(QuantumRange/100.0);
551 threshold.black*=(QuantumRange/100.0);
552 threshold.alpha*=(QuantumRange/100.0);
555 White threshold image.
559 image_view=AcquireAuthenticCacheView(image,exception);
560 #if defined(MAGICKCORE_OPENMP_SUPPORT)
561 #pragma omp parallel for schedule(static,4) shared(progress,status) \
562 dynamic_number_threads(image,image->columns,image->rows,1)
564 for (y=0; y < (ssize_t) image->rows; y++)
572 if (status == MagickFalse)
574 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
575 if (q == (Quantum *) NULL)
580 for (x=0; x < (ssize_t) image->columns; x++)
588 if (GetPixelMask(image,q) != 0)
590 q+=GetPixelChannels(image);
593 pixel=GetPixelIntensity(image,q);
594 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
602 channel=GetPixelChannelChannel(image,i);
603 traits=GetPixelChannelTraits(image,channel);
604 if ((traits & UpdatePixelTrait) == 0)
606 if (image->channel_mask != DefaultChannels)
608 if (pixel <= GetPixelInfoChannel(&threshold,channel))
611 q+=GetPixelChannels(image);
613 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
615 if (image->progress_monitor != (MagickProgressMonitor) NULL)
620 #if defined(MAGICKCORE_OPENMP_SUPPORT)
621 #pragma omp critical (MagickCore_BlackThresholdImage)
623 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
625 if (proceed == MagickFalse)
629 image_view=DestroyCacheView(image_view);
634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638 % C l a m p I m a g e %
642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
644 % ClampImage() restricts the color range from 0 to the quantum depth.
646 % The format of the ClampImage method is:
648 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
650 % A description of each parameter follows:
652 % o image: the image.
654 % o exception: return any errors or warnings in this structure.
658 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
662 if (quantum >= QuantumRange)
663 return(QuantumRange);
667 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
669 #if !defined(MAGICKCORE_HDRI_SUPPORT)
672 #define ClampImageTag "Clamp/Image"
686 assert(image != (Image *) NULL);
687 assert(image->signature == MagickSignature);
688 if (image->debug != MagickFalse)
689 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
690 if (image->storage_class == PseudoClass)
699 for (i=0; i < (ssize_t) image->colors; i++)
701 q->red=(double) ClampToUnsignedQuantum(ClampToQuantum(q->red));
702 q->green=(double) ClampToUnsignedQuantum(ClampToQuantum(q->green));
703 q->blue=(double) ClampToUnsignedQuantum(ClampToQuantum(q->blue));
704 q->alpha=(double) ClampToUnsignedQuantum(ClampToQuantum(q->alpha));
707 return(SyncImage(image,exception));
714 image_view=AcquireAuthenticCacheView(image,exception);
715 #if defined(MAGICKCORE_OPENMP_SUPPORT)
716 #pragma omp parallel for schedule(static,4) shared(progress,status) \
717 dynamic_number_threads(image,image->columns,image->rows,1)
719 for (y=0; y < (ssize_t) image->rows; y++)
727 if (status == MagickFalse)
729 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
730 if (q == (Quantum *) NULL)
735 for (x=0; x < (ssize_t) image->columns; x++)
740 if (GetPixelMask(image,q) != 0)
742 q+=GetPixelChannels(image);
745 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
753 channel=GetPixelChannelChannel(image,i);
754 traits=GetPixelChannelTraits(image,channel);
755 if (traits == UndefinedPixelTrait)
757 q[i]=ClampToUnsignedQuantum(q[i]);
759 q+=GetPixelChannels(image);
761 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
763 if (image->progress_monitor != (MagickProgressMonitor) NULL)
768 #if defined(MAGICKCORE_OPENMP_SUPPORT)
769 #pragma omp critical (MagickCore_ClampImage)
771 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
772 if (proceed == MagickFalse)
776 image_view=DestroyCacheView(image_view);
782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
786 % D e s t r o y T h r e s h o l d M a p %
790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
792 % DestroyThresholdMap() de-allocate the given ThresholdMap
794 % The format of the ListThresholdMaps method is:
796 % ThresholdMap *DestroyThresholdMap(Threshold *map)
798 % A description of each parameter follows.
800 % o map: Pointer to the Threshold map to destroy
803 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
805 assert(map != (ThresholdMap *) NULL);
806 if (map->map_id != (char *) NULL)
807 map->map_id=DestroyString(map->map_id);
808 if (map->description != (char *) NULL)
809 map->description=DestroyString(map->description);
810 if (map->levels != (ssize_t *) NULL)
811 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
812 map=(ThresholdMap *) RelinquishMagickMemory(map);
817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821 % G e t T h r e s h o l d M a p %
825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
827 % GetThresholdMap() loads and searches one or more threshold map files for the
828 % map matching the given name or alias.
830 % The format of the GetThresholdMap method is:
832 % ThresholdMap *GetThresholdMap(const char *map_id,
833 % ExceptionInfo *exception)
835 % A description of each parameter follows.
837 % o map_id: ID of the map to look for.
839 % o exception: return any errors or warnings in this structure.
842 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
843 ExceptionInfo *exception)
854 map=(ThresholdMap *)NULL;
855 options=GetConfigureOptions(ThresholdsFilename,exception);
856 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
857 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
858 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
859 GetStringInfoPath(option),map_id,exception);
860 options=DestroyConfigureOptions(options);
865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
869 + G e t T h r e s h o l d M a p F i l e %
873 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
875 % GetThresholdMapFile() look for a given threshold map name or alias in the
876 % given XML file data, and return the allocated the map when found.
878 % The format of the ListThresholdMaps method is:
880 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
881 % const char *map_id,ExceptionInfo *exception)
883 % A description of each parameter follows.
885 % o xml: The threshold map list in XML format.
887 % o filename: The threshold map XML filename.
889 % o map_id: ID of the map to look for in XML list.
891 % o exception: return any errors or warnings in this structure.
894 static ThresholdMap *GetThresholdMapFile(const char *xml,
895 const char *filename,const char *map_id,ExceptionInfo *exception)
919 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
920 "Loading threshold map file \"%s\" ...",filename);
921 map=(ThresholdMap *) NULL;
922 thresholds=NewXMLTree(xml,exception);
923 if (thresholds == (XMLTreeInfo *) NULL)
925 for (threshold=GetXMLTreeChild(thresholds,"threshold");
926 threshold != (XMLTreeInfo *) NULL;
927 threshold=GetNextXMLTreeTag(threshold))
929 attribute=GetXMLTreeAttribute(threshold,"map");
930 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
932 attribute=GetXMLTreeAttribute(threshold,"alias");
933 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
936 if (threshold == (XMLTreeInfo *) NULL)
938 description=GetXMLTreeChild(threshold,"description");
939 if (description == (XMLTreeInfo *) NULL)
941 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
942 "XmlMissingElement", "<description>, map \"%s\"",map_id);
943 thresholds=DestroyXMLTree(thresholds);
946 levels=GetXMLTreeChild(threshold,"levels");
947 if (levels == (XMLTreeInfo *) NULL)
949 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
950 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
951 thresholds=DestroyXMLTree(thresholds);
954 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
955 if (map == (ThresholdMap *) NULL)
956 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
957 map->map_id=(char *) NULL;
958 map->description=(char *) NULL;
959 map->levels=(ssize_t *) NULL;
960 attribute=GetXMLTreeAttribute(threshold,"map");
961 if (attribute != (char *) NULL)
962 map->map_id=ConstantString(attribute);
963 content=GetXMLTreeContent(description);
964 if (content != (char *) NULL)
965 map->description=ConstantString(content);
966 attribute=GetXMLTreeAttribute(levels,"width");
967 if (attribute == (char *) NULL)
969 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
970 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
971 thresholds=DestroyXMLTree(thresholds);
972 map=DestroyThresholdMap(map);
975 map->width=StringToUnsignedLong(attribute);
978 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
979 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
980 thresholds=DestroyXMLTree(thresholds);
981 map=DestroyThresholdMap(map);
984 attribute=GetXMLTreeAttribute(levels,"height");
985 if (attribute == (char *) NULL)
987 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
988 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
989 thresholds=DestroyXMLTree(thresholds);
990 map=DestroyThresholdMap(map);
993 map->height=StringToUnsignedLong(attribute);
994 if (map->height == 0)
996 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
997 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
998 thresholds=DestroyXMLTree(thresholds);
999 map=DestroyThresholdMap(map);
1002 attribute=GetXMLTreeAttribute(levels,"divisor");
1003 if (attribute == (char *) NULL)
1005 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1006 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1007 thresholds=DestroyXMLTree(thresholds);
1008 map=DestroyThresholdMap(map);
1011 map->divisor=(ssize_t) StringToLong(attribute);
1012 if (map->divisor < 2)
1014 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1015 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1016 thresholds=DestroyXMLTree(thresholds);
1017 map=DestroyThresholdMap(map);
1020 content=GetXMLTreeContent(levels);
1021 if (content == (char *) NULL)
1023 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1024 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1025 thresholds=DestroyXMLTree(thresholds);
1026 map=DestroyThresholdMap(map);
1029 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1030 sizeof(*map->levels));
1031 if (map->levels == (ssize_t *) NULL)
1032 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1033 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1035 map->levels[i]=(ssize_t) strtol(content,&p,10);
1038 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1039 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1040 thresholds=DestroyXMLTree(thresholds);
1041 map=DestroyThresholdMap(map);
1044 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1046 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1047 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1048 (double) map->levels[i],map_id);
1049 thresholds=DestroyXMLTree(thresholds);
1050 map=DestroyThresholdMap(map);
1055 value=(double) strtol(content,&p,10);
1059 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1060 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1061 thresholds=DestroyXMLTree(thresholds);
1062 map=DestroyThresholdMap(map);
1065 thresholds=DestroyXMLTree(thresholds);
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1074 + L i s t T h r e s h o l d M a p F i l e %
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1080 % ListThresholdMapFile() lists the threshold maps and their descriptions
1081 % in the given XML file data.
1083 % The format of the ListThresholdMaps method is:
1085 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1086 % const char *filename,ExceptionInfo *exception)
1088 % A description of each parameter follows.
1090 % o file: An pointer to the output FILE.
1092 % o xml: The threshold map list in XML format.
1094 % o filename: The threshold map XML filename.
1096 % o exception: return any errors or warnings in this structure.
1099 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1100 const char *filename,ExceptionInfo *exception)
1112 assert( xml != (char *)NULL );
1113 assert( file != (FILE *)NULL );
1114 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1115 "Loading threshold map file \"%s\" ...",filename);
1116 thresholds=NewXMLTree(xml,exception);
1117 if ( thresholds == (XMLTreeInfo *)NULL )
1118 return(MagickFalse);
1119 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1120 (void) FormatLocaleFile(file,
1121 "----------------------------------------------------\n");
1122 threshold=GetXMLTreeChild(thresholds,"threshold");
1123 for ( ; threshold != (XMLTreeInfo *) NULL;
1124 threshold=GetNextXMLTreeTag(threshold))
1126 map=GetXMLTreeAttribute(threshold,"map");
1127 if (map == (char *) NULL)
1129 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1130 "XmlMissingAttribute", "<map>");
1131 thresholds=DestroyXMLTree(thresholds);
1132 return(MagickFalse);
1134 alias=GetXMLTreeAttribute(threshold,"alias");
1135 description=GetXMLTreeChild(threshold,"description");
1136 if (description == (XMLTreeInfo *) NULL)
1138 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1139 "XmlMissingElement", "<description>, map \"%s\"",map);
1140 thresholds=DestroyXMLTree(thresholds);
1141 return(MagickFalse);
1143 content=GetXMLTreeContent(description);
1144 if (content == (char *) NULL)
1146 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1147 "XmlMissingContent", "<description>, map \"%s\"", map);
1148 thresholds=DestroyXMLTree(thresholds);
1149 return(MagickFalse);
1151 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1154 thresholds=DestroyXMLTree(thresholds);
1159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1163 % L i s t T h r e s h o l d M a p s %
1167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1169 % ListThresholdMaps() lists the threshold maps and their descriptions
1170 % as defined by "threshold.xml" to a file.
1172 % The format of the ListThresholdMaps method is:
1174 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1176 % A description of each parameter follows.
1178 % o file: An pointer to the output FILE.
1180 % o exception: return any errors or warnings in this structure.
1183 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1184 ExceptionInfo *exception)
1196 if (file == (FILE *) NULL)
1198 options=GetConfigureOptions(ThresholdsFilename,exception);
1199 (void) FormatLocaleFile(file,
1200 "\n Threshold Maps for Ordered Dither Operations\n");
1201 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1202 (const StringInfo *) NULL)
1204 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1205 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1206 GetStringInfoPath(option),exception);
1208 options=DestroyConfigureOptions(options);
1209 return(status != 0 ? MagickTrue : MagickFalse);
1213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217 % O r d e r e d P o s t e r i z e I m a g e %
1221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1223 % OrderedPosterizeImage() will perform a ordered dither based on a number
1224 % of pre-defined dithering threshold maps, but over multiple intensity
1225 % levels, which can be different for different channels, according to the
1228 % The format of the OrderedPosterizeImage method is:
1230 % MagickBooleanType OrderedPosterizeImage(Image *image,
1231 % const char *threshold_map,ExceptionInfo *exception)
1233 % A description of each parameter follows:
1235 % o image: the image.
1237 % o threshold_map: A string containing the name of the threshold dither
1238 % map to use, followed by zero or more numbers representing the number
1239 % of color levels tho dither between.
1241 % Any level number less than 2 will be equivalent to 2, and means only
1242 % binary dithering will be applied to each color channel.
1244 % No numbers also means a 2 level (bitmap) dither will be applied to all
1245 % channels, while a single number is the number of levels applied to each
1246 % channel in sequence. More numbers will be applied in turn to each of
1247 % the color channels.
1249 % For example: "o3x3,6" will generate a 6 level posterization of the
1250 % image with a ordered 3x3 diffused pixel dither being applied between
1251 % each level. While checker,8,8,4 will produce a 332 colormaped image
1252 % with only a single checkerboard hash pattern (50% grey) between each
1253 % color level, to basically double the number of color levels with
1254 % a bare minimim of dithering.
1256 % o exception: return any errors or warnings in this structure.
1259 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1260 const char *threshold_map,ExceptionInfo *exception)
1262 #define DitherImageTag "Dither/Image"
1268 token[MaxTextExtent];
1280 levels[CompositePixelChannel];
1291 assert(image != (Image *) NULL);
1292 assert(image->signature == MagickSignature);
1293 if (image->debug != MagickFalse)
1294 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1295 assert(exception != (ExceptionInfo *) NULL);
1296 assert(exception->signature == MagickSignature);
1297 if (threshold_map == (const char *) NULL)
1299 p=(char *) threshold_map;
1300 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1304 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1307 if ((p-threshold_map) >= (MaxTextExtent-1))
1309 token[p-threshold_map]=(*p);
1312 token[p-threshold_map]='\0';
1313 map=GetThresholdMap(token,exception);
1314 if (map == (ThresholdMap *) NULL)
1316 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1317 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1318 return(MagickFalse);
1320 for (i=0; i < MaxPixelChannels; i++)
1322 p=strchr((char *) threshold_map,',');
1323 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1324 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1326 GetMagickToken(p,&p,token);
1328 GetMagickToken(p,&p,token);
1329 levels[i]=StringToDouble(token,(char **) NULL);
1331 for (i=0; i < MaxPixelChannels; i++)
1332 if (fabs(levels[i]) >= 1)
1334 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1335 return(MagickFalse);
1338 image_view=AcquireAuthenticCacheView(image,exception);
1339 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1340 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1341 dynamic_number_threads(image,image->columns,image->rows,1)
1343 for (y=0; y < (ssize_t) image->rows; y++)
1351 if (status == MagickFalse)
1353 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1354 if (q == (Quantum *) NULL)
1359 for (x=0; x < (ssize_t) image->columns; x++)
1368 if (GetPixelMask(image,q) != 0)
1370 q+=GetPixelChannels(image);
1373 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1385 channel=GetPixelChannelChannel(image,i);
1386 traits=GetPixelChannelTraits(image,channel);
1387 if ((traits & UpdatePixelTrait) == 0)
1389 if (fabs(levels[n++]) < MagickEpsilon)
1391 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1392 level=threshold/(map->divisor-1);
1393 threshold-=level*(map->divisor-1);
1394 q[i]=ClampToQuantum((double) (level+(threshold >=
1395 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1396 QuantumRange/levels[n]);
1399 q+=GetPixelChannels(image);
1401 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1403 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1408 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1409 #pragma omp critical (MagickCore_OrderedPosterizeImage)
1411 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1412 if (proceed == MagickFalse)
1416 image_view=DestroyCacheView(image_view);
1417 map=DestroyThresholdMap(map);
1422 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1426 % R a n d o m T h r e s h o l d I m a g e %
1430 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1432 % RandomThresholdImage() changes the value of individual pixels based on the
1433 % intensity of each pixel compared to a random threshold. The result is a
1434 % low-contrast, two color image.
1436 % The format of the RandomThresholdImage method is:
1438 % MagickBooleanType RandomThresholdImage(Image *image,
1439 % const char *thresholds,ExceptionInfo *exception)
1441 % A description of each parameter follows:
1443 % o image: the image.
1445 % o thresholds: a geometry string containing low,high thresholds. If the
1446 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1447 % is performed instead.
1449 % o exception: return any errors or warnings in this structure.
1452 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1453 const char *thresholds,ExceptionInfo *exception)
1455 #define ThresholdImageTag "Threshold/Image"
1480 **restrict random_info;
1488 assert(image != (Image *) NULL);
1489 assert(image->signature == MagickSignature);
1490 if (image->debug != MagickFalse)
1491 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1492 assert(exception != (ExceptionInfo *) NULL);
1493 assert(exception->signature == MagickSignature);
1494 if (thresholds == (const char *) NULL)
1496 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1497 return(MagickFalse);
1498 GetPixelInfo(image,&threshold);
1500 max_threshold=(double) QuantumRange;
1501 flags=ParseGeometry(thresholds,&geometry_info);
1502 min_threshold=geometry_info.rho;
1503 max_threshold=geometry_info.sigma;
1504 if ((flags & SigmaValue) == 0)
1505 max_threshold=min_threshold;
1506 if (strchr(thresholds,'%') != (char *) NULL)
1508 max_threshold*=(double) (0.01*QuantumRange);
1509 min_threshold*=(double) (0.01*QuantumRange);
1512 Random threshold image.
1516 random_info=AcquireRandomInfoThreadSet();
1517 key=GetRandomSecretKey(random_info[0]);
1518 image_view=AcquireAuthenticCacheView(image,exception);
1519 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1520 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1521 dynamic_number_threads(image,image->columns,image->rows,key == ~0UL)
1523 for (y=0; y < (ssize_t) image->rows; y++)
1526 id = GetOpenMPThreadId();
1534 if (status == MagickFalse)
1536 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1537 if (q == (Quantum *) NULL)
1542 for (x=0; x < (ssize_t) image->columns; x++)
1547 if (GetPixelMask(image,q) != 0)
1549 q+=GetPixelChannels(image);
1552 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1563 channel=GetPixelChannelChannel(image,i);
1564 traits=GetPixelChannelTraits(image,channel);
1565 if ((traits & UpdatePixelTrait) == 0)
1567 if ((double) q[i] < min_threshold)
1568 threshold=min_threshold;
1570 if ((double) q[i] > max_threshold)
1571 threshold=max_threshold;
1573 threshold=(double) (QuantumRange*
1574 GetPseudoRandomValue(random_info[id]));
1575 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
1577 q+=GetPixelChannels(image);
1579 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1581 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1586 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1587 #pragma omp critical (MagickCore_RandomThresholdImage)
1589 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1591 if (proceed == MagickFalse)
1595 image_view=DestroyCacheView(image_view);
1596 random_info=DestroyRandomInfoThreadSet(random_info);
1601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605 % W h i t e T h r e s h o l d I m a g e %
1609 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1611 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1612 % the threshold into white while leaving all pixels at or below the threshold
1615 % The format of the WhiteThresholdImage method is:
1617 % MagickBooleanType WhiteThresholdImage(Image *image,
1618 % const char *threshold,ExceptionInfo *exception)
1620 % A description of each parameter follows:
1622 % o image: the image.
1624 % o threshold: Define the threshold value.
1626 % o exception: return any errors or warnings in this structure.
1629 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1630 const char *thresholds,ExceptionInfo *exception)
1632 #define ThresholdImageTag "Threshold/Image"
1655 assert(image != (Image *) NULL);
1656 assert(image->signature == MagickSignature);
1657 if (image->debug != MagickFalse)
1658 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1659 if (thresholds == (const char *) NULL)
1661 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1662 return(MagickFalse);
1663 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1664 (void) TransformImageColorspace(image,RGBColorspace,exception);
1665 GetPixelInfo(image,&threshold);
1666 flags=ParseGeometry(thresholds,&geometry_info);
1667 threshold.red=geometry_info.rho;
1668 threshold.green=geometry_info.rho;
1669 threshold.blue=geometry_info.rho;
1670 threshold.black=geometry_info.rho;
1671 threshold.alpha=100.0;
1672 if ((flags & SigmaValue) != 0)
1673 threshold.green=geometry_info.sigma;
1674 if ((flags & XiValue) != 0)
1675 threshold.blue=geometry_info.xi;
1676 if ((flags & PsiValue) != 0)
1677 threshold.alpha=geometry_info.psi;
1678 if (threshold.colorspace == CMYKColorspace)
1680 if ((flags & PsiValue) != 0)
1681 threshold.black=geometry_info.psi;
1682 if ((flags & ChiValue) != 0)
1683 threshold.alpha=geometry_info.chi;
1685 if ((flags & PercentValue) != 0)
1687 threshold.red*=(QuantumRange/100.0);
1688 threshold.green*=(QuantumRange/100.0);
1689 threshold.blue*=(QuantumRange/100.0);
1690 threshold.black*=(QuantumRange/100.0);
1691 threshold.alpha*=(QuantumRange/100.0);
1694 White threshold image.
1698 image_view=AcquireAuthenticCacheView(image,exception);
1699 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1700 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1701 dynamic_number_threads(image,image->columns,image->rows,1)
1703 for (y=0; y < (ssize_t) image->rows; y++)
1711 if (status == MagickFalse)
1713 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1714 if (q == (Quantum *) NULL)
1719 for (x=0; x < (ssize_t) image->columns; x++)
1727 if (GetPixelMask(image,q) != 0)
1729 q+=GetPixelChannels(image);
1732 pixel=GetPixelIntensity(image,q);
1733 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1741 channel=GetPixelChannelChannel(image,i);
1742 traits=GetPixelChannelTraits(image,channel);
1743 if ((traits & UpdatePixelTrait) == 0)
1745 if (image->channel_mask != DefaultChannels)
1746 pixel=(double) q[i];
1747 if (pixel > GetPixelInfoChannel(&threshold,channel))
1750 q+=GetPixelChannels(image);
1752 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1754 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1759 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1760 #pragma omp critical (MagickCore_WhiteThresholdImage)
1762 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1764 if (proceed == MagickFalse)
1768 image_view=DestroyCacheView(image_view);