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-2013 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 magick_threads(image,threshold_image,image->rows,1)
211 for (y=0; y < (ssize_t) image->rows; y++)
214 channel_bias[MaxPixelChannels],
215 channel_sum[MaxPixelChannels];
224 register const Quantum
240 if (status == MagickFalse)
242 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
243 (height/2L),image->columns+width,height,exception);
244 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
246 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
251 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
252 GetPixelChannels(image)*(width/2);
253 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
255 channel=GetPixelChannelChannel(image,i);
256 traits=GetPixelChannelTraits(image,channel);
257 threshold_traits=GetPixelChannelTraits(threshold_image,channel);
258 if ((traits == UndefinedPixelTrait) ||
259 (threshold_traits == UndefinedPixelTrait))
261 if (((threshold_traits & CopyPixelTrait) != 0) ||
262 (GetPixelMask(image,p) != 0))
264 SetPixelChannel(threshold_image,channel,p[center+i],q);
268 channel_bias[channel]=0.0;
269 channel_sum[channel]=0.0;
270 for (v=0; v < (ssize_t) height; v++)
272 for (u=0; u < (ssize_t) width; u++)
274 if (u == (ssize_t) (width-1))
275 channel_bias[channel]+=pixels[i];
276 channel_sum[channel]+=pixels[i];
277 pixels+=GetPixelChannels(image);
279 pixels+=image->columns*GetPixelChannels(image);
282 for (x=0; x < (ssize_t) image->columns; x++)
284 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
289 channel=GetPixelChannelChannel(image,i);
290 traits=GetPixelChannelTraits(image,channel);
291 threshold_traits=GetPixelChannelTraits(threshold_image,channel);
292 if ((traits == UndefinedPixelTrait) ||
293 (threshold_traits == UndefinedPixelTrait))
295 if (((threshold_traits & CopyPixelTrait) != 0) ||
296 (GetPixelMask(image,p) != 0))
298 SetPixelChannel(threshold_image,channel,p[center+i],q);
301 channel_sum[channel]-=channel_bias[channel];
302 channel_bias[channel]=0.0;
304 for (v=0; v < (ssize_t) height; v++)
306 channel_bias[channel]+=pixels[i];
307 pixels+=(width-1)*GetPixelChannels(image);
308 channel_sum[channel]+=pixels[i];
309 pixels+=(image->columns+1)*GetPixelChannels(image);
311 mean=(double) (channel_sum[channel]/number_pixels+bias);
312 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
313 p[center+i] <= mean ? 0 : QuantumRange),q);
315 p+=GetPixelChannels(image);
316 q+=GetPixelChannels(threshold_image);
318 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
320 if (image->progress_monitor != (MagickProgressMonitor) NULL)
325 #if defined(MAGICKCORE_OPENMP_SUPPORT)
326 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
328 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
330 if (proceed == MagickFalse)
334 threshold_image->type=image->type;
335 threshold_view=DestroyCacheView(threshold_view);
336 image_view=DestroyCacheView(image_view);
337 if (status == MagickFalse)
338 threshold_image=DestroyImage(threshold_image);
339 return(threshold_image);
343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347 % B i l e v e l I m a g e %
351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
353 % BilevelImage() changes the value of individual pixels based on the
354 % intensity of each pixel channel. The result is a high-contrast image.
356 % More precisely each channel value of the image is 'thresholded' so that if
357 % it is equal to or less than the given value it is set to zero, while any
358 % value greater than that give is set to it maximum or QuantumRange.
360 % This function is what is used to implement the "-threshold" operator for
361 % the command line API.
363 % If the default channel setting is given the image is thresholded using just
364 % the gray 'intensity' of the image, rather than the individual channels.
366 % The format of the BilevelImage method is:
368 % MagickBooleanType BilevelImage(Image *image,const double threshold,
369 % ExceptionInfo *exception)
371 % A description of each parameter follows:
373 % o image: the image.
375 % o threshold: define the threshold values.
377 % o exception: return any errors or warnings in this structure.
379 % Aside: You can get the same results as operator using LevelImages()
380 % with the 'threshold' value for both the black_point and the white_point.
383 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
384 ExceptionInfo *exception)
386 #define ThresholdImageTag "Threshold/Image"
400 assert(image != (Image *) NULL);
401 assert(image->signature == MagickSignature);
402 if (image->debug != MagickFalse)
403 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
404 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
407 Bilevel threshold image.
411 image_view=AcquireAuthenticCacheView(image,exception);
412 #if defined(MAGICKCORE_OPENMP_SUPPORT)
413 #pragma omp parallel for schedule(static,4) shared(progress,status) \
414 magick_threads(image,image,image->rows,1)
416 for (y=0; y < (ssize_t) image->rows; y++)
424 if (status == MagickFalse)
426 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
427 if (q == (Quantum *) NULL)
432 for (x=0; x < (ssize_t) image->columns; x++)
440 if (GetPixelMask(image,q) != 0)
442 q+=GetPixelChannels(image);
445 pixel=GetPixelIntensity(image,q);
446 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
454 channel=GetPixelChannelChannel(image,i);
455 traits=GetPixelChannelTraits(image,channel);
456 if ((traits & UpdatePixelTrait) == 0)
458 if (image->channel_mask != DefaultChannels)
460 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
462 q+=GetPixelChannels(image);
464 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
466 if (image->progress_monitor != (MagickProgressMonitor) NULL)
471 #if defined(MAGICKCORE_OPENMP_SUPPORT)
472 #pragma omp critical (MagickCore_BilevelImage)
474 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
476 if (proceed == MagickFalse)
480 image_view=DestroyCacheView(image_view);
485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489 % B l a c k T h r e s h o l d I m a g e %
493 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
495 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
496 % the threshold into black while leaving all pixels at or above the threshold
499 % The format of the BlackThresholdImage method is:
501 % MagickBooleanType BlackThresholdImage(Image *image,
502 % const char *threshold,ExceptionInfo *exception)
504 % A description of each parameter follows:
506 % o image: the image.
508 % o threshold: define the threshold value.
510 % o exception: return any errors or warnings in this structure.
513 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
514 const char *thresholds,ExceptionInfo *exception)
516 #define ThresholdImageTag "Threshold/Image"
539 assert(image != (Image *) NULL);
540 assert(image->signature == MagickSignature);
541 if (image->debug != MagickFalse)
542 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
543 if (thresholds == (const char *) NULL)
545 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
547 if (IsGrayColorspace(image->colorspace) != MagickFalse)
548 (void) TransformImageColorspace(image,RGBColorspace,exception);
549 GetPixelInfo(image,&threshold);
550 flags=ParseGeometry(thresholds,&geometry_info);
551 threshold.red=geometry_info.rho;
552 threshold.green=geometry_info.rho;
553 threshold.blue=geometry_info.rho;
554 threshold.black=geometry_info.rho;
555 threshold.alpha=100.0;
556 if ((flags & SigmaValue) != 0)
557 threshold.green=geometry_info.sigma;
558 if ((flags & XiValue) != 0)
559 threshold.blue=geometry_info.xi;
560 if ((flags & PsiValue) != 0)
561 threshold.alpha=geometry_info.psi;
562 if (threshold.colorspace == CMYKColorspace)
564 if ((flags & PsiValue) != 0)
565 threshold.black=geometry_info.psi;
566 if ((flags & ChiValue) != 0)
567 threshold.alpha=geometry_info.chi;
569 if ((flags & PercentValue) != 0)
571 threshold.red*=(MagickRealType) (QuantumRange/100.0);
572 threshold.green*=(MagickRealType) (QuantumRange/100.0);
573 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
574 threshold.black*=(MagickRealType) (QuantumRange/100.0);
575 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
578 White threshold image.
582 image_view=AcquireAuthenticCacheView(image,exception);
583 #if defined(MAGICKCORE_OPENMP_SUPPORT)
584 #pragma omp parallel for schedule(static,4) shared(progress,status) \
585 magick_threads(image,image,image->rows,1)
587 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 == (Quantum *) NULL)
603 for (x=0; x < (ssize_t) image->columns; x++)
611 if (GetPixelMask(image,q) != 0)
613 q+=GetPixelChannels(image);
616 pixel=GetPixelIntensity(image,q);
617 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
625 channel=GetPixelChannelChannel(image,i);
626 traits=GetPixelChannelTraits(image,channel);
627 if ((traits & UpdatePixelTrait) == 0)
629 if (image->channel_mask != DefaultChannels)
631 if (pixel <= GetPixelInfoChannel(&threshold,channel))
634 q+=GetPixelChannels(image);
636 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
638 if (image->progress_monitor != (MagickProgressMonitor) NULL)
643 #if defined(MAGICKCORE_OPENMP_SUPPORT)
644 #pragma omp critical (MagickCore_BlackThresholdImage)
646 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
648 if (proceed == MagickFalse)
652 image_view=DestroyCacheView(image_view);
657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661 % C l a m p I m a g e %
665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667 % ClampImage() set each pixel whose value is below zero to zero and any the
668 % pixel whose value is above the quantum range to the quantum range (e.g.
669 % 65535) otherwise the pixel value remains unchanged.
671 % The format of the ClampImage method is:
673 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
675 % A description of each parameter follows:
677 % o image: the image.
679 % o exception: return any errors or warnings in this structure.
683 static inline Quantum ClampPixel(const MagickRealType value)
685 #if !defined(MAGICKCORE_HDRI_SUPPORT)
686 return((Quantum) value);
690 if (value >= (MagickRealType) QuantumRange)
691 return((Quantum) QuantumRange);
696 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
698 #define ClampImageTag "Clamp/Image"
712 assert(image != (Image *) NULL);
713 assert(image->signature == MagickSignature);
714 if (image->debug != MagickFalse)
715 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
716 if (image->storage_class == PseudoClass)
725 for (i=0; i < (ssize_t) image->colors; i++)
727 q->red=(double) ClampPixel(q->red);
728 q->green=(double) ClampPixel(q->green);
729 q->blue=(double) ClampPixel(q->blue);
730 q->alpha=(double) ClampPixel(q->alpha);
733 return(SyncImage(image,exception));
740 image_view=AcquireAuthenticCacheView(image,exception);
741 #if defined(MAGICKCORE_OPENMP_SUPPORT)
742 #pragma omp parallel for schedule(static,4) shared(progress,status) \
743 magick_threads(image,image,image->rows,1)
745 for (y=0; y < (ssize_t) image->rows; y++)
753 if (status == MagickFalse)
755 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
756 if (q == (Quantum *) NULL)
761 for (x=0; x < (ssize_t) image->columns; x++)
766 if (GetPixelMask(image,q) != 0)
768 q+=GetPixelChannels(image);
771 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
779 channel=GetPixelChannelChannel(image,i);
780 traits=GetPixelChannelTraits(image,channel);
781 if (traits == UndefinedPixelTrait)
783 q[i]=ClampPixel(q[i]);
785 q+=GetPixelChannels(image);
787 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
789 if (image->progress_monitor != (MagickProgressMonitor) NULL)
794 #if defined(MAGICKCORE_OPENMP_SUPPORT)
795 #pragma omp critical (MagickCore_ClampImage)
797 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
798 if (proceed == MagickFalse)
802 image_view=DestroyCacheView(image_view);
807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
811 % D e s t r o y T h r e s h o l d M a p %
815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817 % DestroyThresholdMap() de-allocate the given ThresholdMap
819 % The format of the ListThresholdMaps method is:
821 % ThresholdMap *DestroyThresholdMap(Threshold *map)
823 % A description of each parameter follows.
825 % o map: Pointer to the Threshold map to destroy
828 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
830 assert(map != (ThresholdMap *) NULL);
831 if (map->map_id != (char *) NULL)
832 map->map_id=DestroyString(map->map_id);
833 if (map->description != (char *) NULL)
834 map->description=DestroyString(map->description);
835 if (map->levels != (ssize_t *) NULL)
836 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
837 map=(ThresholdMap *) RelinquishMagickMemory(map);
842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846 % G e t T h r e s h o l d M a p %
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852 % GetThresholdMap() loads and searches one or more threshold map files for the
853 % map matching the given name or alias.
855 % The format of the GetThresholdMap method is:
857 % ThresholdMap *GetThresholdMap(const char *map_id,
858 % ExceptionInfo *exception)
860 % A description of each parameter follows.
862 % o map_id: ID of the map to look for.
864 % o exception: return any errors or warnings in this structure.
867 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
868 ExceptionInfo *exception)
879 map=(ThresholdMap *)NULL;
880 options=GetConfigureOptions(ThresholdsFilename,exception);
881 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
882 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
883 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
884 GetStringInfoPath(option),map_id,exception);
885 options=DestroyConfigureOptions(options);
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
894 + G e t T h r e s h o l d M a p F i l e %
898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 % GetThresholdMapFile() look for a given threshold map name or alias in the
901 % given XML file data, and return the allocated the map when found.
903 % The format of the ListThresholdMaps method is:
905 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
906 % const char *map_id,ExceptionInfo *exception)
908 % A description of each parameter follows.
910 % o xml: The threshold map list in XML format.
912 % o filename: The threshold map XML filename.
914 % o map_id: ID of the map to look for in XML list.
916 % o exception: return any errors or warnings in this structure.
919 static ThresholdMap *GetThresholdMapFile(const char *xml,
920 const char *filename,const char *map_id,ExceptionInfo *exception)
944 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
945 "Loading threshold map file \"%s\" ...",filename);
946 map=(ThresholdMap *) NULL;
947 thresholds=NewXMLTree(xml,exception);
948 if (thresholds == (XMLTreeInfo *) NULL)
950 for (threshold=GetXMLTreeChild(thresholds,"threshold");
951 threshold != (XMLTreeInfo *) NULL;
952 threshold=GetNextXMLTreeTag(threshold))
954 attribute=GetXMLTreeAttribute(threshold,"map");
955 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
957 attribute=GetXMLTreeAttribute(threshold,"alias");
958 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
961 if (threshold == (XMLTreeInfo *) NULL)
963 description=GetXMLTreeChild(threshold,"description");
964 if (description == (XMLTreeInfo *) NULL)
966 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
967 "XmlMissingElement", "<description>, map \"%s\"",map_id);
968 thresholds=DestroyXMLTree(thresholds);
971 levels=GetXMLTreeChild(threshold,"levels");
972 if (levels == (XMLTreeInfo *) NULL)
974 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
975 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
976 thresholds=DestroyXMLTree(thresholds);
979 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
980 if (map == (ThresholdMap *) NULL)
981 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
982 map->map_id=(char *) NULL;
983 map->description=(char *) NULL;
984 map->levels=(ssize_t *) NULL;
985 attribute=GetXMLTreeAttribute(threshold,"map");
986 if (attribute != (char *) NULL)
987 map->map_id=ConstantString(attribute);
988 content=GetXMLTreeContent(description);
989 if (content != (char *) NULL)
990 map->description=ConstantString(content);
991 attribute=GetXMLTreeAttribute(levels,"width");
992 if (attribute == (char *) NULL)
994 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
995 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
996 thresholds=DestroyXMLTree(thresholds);
997 map=DestroyThresholdMap(map);
1000 map->width=StringToUnsignedLong(attribute);
1001 if (map->width == 0)
1003 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1004 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1005 thresholds=DestroyXMLTree(thresholds);
1006 map=DestroyThresholdMap(map);
1009 attribute=GetXMLTreeAttribute(levels,"height");
1010 if (attribute == (char *) NULL)
1012 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1013 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1014 thresholds=DestroyXMLTree(thresholds);
1015 map=DestroyThresholdMap(map);
1018 map->height=StringToUnsignedLong(attribute);
1019 if (map->height == 0)
1021 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1022 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1023 thresholds=DestroyXMLTree(thresholds);
1024 map=DestroyThresholdMap(map);
1027 attribute=GetXMLTreeAttribute(levels,"divisor");
1028 if (attribute == (char *) NULL)
1030 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1031 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1032 thresholds=DestroyXMLTree(thresholds);
1033 map=DestroyThresholdMap(map);
1036 map->divisor=(ssize_t) StringToLong(attribute);
1037 if (map->divisor < 2)
1039 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1040 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1041 thresholds=DestroyXMLTree(thresholds);
1042 map=DestroyThresholdMap(map);
1045 content=GetXMLTreeContent(levels);
1046 if (content == (char *) NULL)
1048 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1049 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1050 thresholds=DestroyXMLTree(thresholds);
1051 map=DestroyThresholdMap(map);
1054 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1055 sizeof(*map->levels));
1056 if (map->levels == (ssize_t *) NULL)
1057 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1058 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1060 map->levels[i]=(ssize_t) strtol(content,&p,10);
1063 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1064 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1065 thresholds=DestroyXMLTree(thresholds);
1066 map=DestroyThresholdMap(map);
1069 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1071 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1072 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1073 (double) map->levels[i],map_id);
1074 thresholds=DestroyXMLTree(thresholds);
1075 map=DestroyThresholdMap(map);
1080 value=(double) strtol(content,&p,10);
1084 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1085 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1086 thresholds=DestroyXMLTree(thresholds);
1087 map=DestroyThresholdMap(map);
1090 thresholds=DestroyXMLTree(thresholds);
1095 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1099 + L i s t T h r e s h o l d M a p F i l e %
1103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1105 % ListThresholdMapFile() lists the threshold maps and their descriptions
1106 % in the given XML file data.
1108 % The format of the ListThresholdMaps method is:
1110 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1111 % const char *filename,ExceptionInfo *exception)
1113 % A description of each parameter follows.
1115 % o file: An pointer to the output FILE.
1117 % o xml: The threshold map list in XML format.
1119 % o filename: The threshold map XML filename.
1121 % o exception: return any errors or warnings in this structure.
1124 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1125 const char *filename,ExceptionInfo *exception)
1137 assert( xml != (char *)NULL );
1138 assert( file != (FILE *)NULL );
1139 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1140 "Loading threshold map file \"%s\" ...",filename);
1141 thresholds=NewXMLTree(xml,exception);
1142 if ( thresholds == (XMLTreeInfo *)NULL )
1143 return(MagickFalse);
1144 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1145 (void) FormatLocaleFile(file,
1146 "----------------------------------------------------\n");
1147 threshold=GetXMLTreeChild(thresholds,"threshold");
1148 for ( ; threshold != (XMLTreeInfo *) NULL;
1149 threshold=GetNextXMLTreeTag(threshold))
1151 map=GetXMLTreeAttribute(threshold,"map");
1152 if (map == (char *) NULL)
1154 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1155 "XmlMissingAttribute", "<map>");
1156 thresholds=DestroyXMLTree(thresholds);
1157 return(MagickFalse);
1159 alias=GetXMLTreeAttribute(threshold,"alias");
1160 description=GetXMLTreeChild(threshold,"description");
1161 if (description == (XMLTreeInfo *) NULL)
1163 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1164 "XmlMissingElement", "<description>, map \"%s\"",map);
1165 thresholds=DestroyXMLTree(thresholds);
1166 return(MagickFalse);
1168 content=GetXMLTreeContent(description);
1169 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) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1179 thresholds=DestroyXMLTree(thresholds);
1184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1188 % L i s t T h r e s h o l d M a p s %
1192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 % ListThresholdMaps() lists the threshold maps and their descriptions
1195 % as defined by "threshold.xml" to a file.
1197 % The format of the ListThresholdMaps method is:
1199 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1201 % A description of each parameter follows.
1203 % o file: An pointer to the output FILE.
1205 % o exception: return any errors or warnings in this structure.
1208 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1209 ExceptionInfo *exception)
1221 if (file == (FILE *) NULL)
1223 options=GetConfigureOptions(ThresholdsFilename,exception);
1224 (void) FormatLocaleFile(file,
1225 "\n Threshold Maps for Ordered Dither Operations\n");
1226 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1227 (const StringInfo *) NULL)
1229 (void) FormatLocaleFile(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 P o s t e r i z e I m a g e %
1246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248 % OrderedPosterizeImage() will perform a ordered dither based on a number
1249 % of pre-defined dithering threshold maps, but over multiple intensity
1250 % levels, which can be different for different channels, according to the
1253 % The format of the OrderedPosterizeImage method is:
1255 % MagickBooleanType OrderedPosterizeImage(Image *image,
1256 % const char *threshold_map,ExceptionInfo *exception)
1258 % A description of each parameter follows:
1260 % o image: the image.
1262 % o threshold_map: A string containing the name of the threshold dither
1263 % map to use, followed by zero or more numbers representing the number
1264 % of color levels tho dither between.
1266 % Any level number less than 2 will be equivalent to 2, and means only
1267 % binary dithering will be applied to each color channel.
1269 % No numbers also means a 2 level (bitmap) dither will be applied to all
1270 % channels, while a single number is the number of levels applied to each
1271 % channel in sequence. More numbers will be applied in turn to each of
1272 % the color channels.
1274 % For example: "o3x3,6" will generate a 6 level posterization of the
1275 % image with a ordered 3x3 diffused pixel dither being applied between
1276 % each level. While checker,8,8,4 will produce a 332 colormaped image
1277 % with only a single checkerboard hash pattern (50% grey) between each
1278 % color level, to basically double the number of color levels with
1279 % a bare minimim of dithering.
1281 % o exception: return any errors or warnings in this structure.
1284 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1285 const char *threshold_map,ExceptionInfo *exception)
1287 #define DitherImageTag "Dither/Image"
1293 token[MaxTextExtent];
1305 levels[CompositePixelChannel];
1316 assert(image != (Image *) NULL);
1317 assert(image->signature == MagickSignature);
1318 if (image->debug != MagickFalse)
1319 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1320 assert(exception != (ExceptionInfo *) NULL);
1321 assert(exception->signature == MagickSignature);
1322 if (threshold_map == (const char *) NULL)
1324 p=(char *) threshold_map;
1325 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1329 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1332 if ((p-threshold_map) >= (MaxTextExtent-1))
1334 token[p-threshold_map]=(*p);
1337 token[p-threshold_map]='\0';
1338 map=GetThresholdMap(token,exception);
1339 if (map == (ThresholdMap *) NULL)
1341 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1342 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1343 return(MagickFalse);
1345 for (i=0; i < MaxPixelChannels; i++)
1347 p=strchr((char *) threshold_map,',');
1348 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1349 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1351 GetMagickToken(p,&p,token);
1353 GetMagickToken(p,&p,token);
1354 levels[i]=StringToDouble(token,(char **) NULL);
1356 for (i=0; i < MaxPixelChannels; i++)
1357 if (fabs(levels[i]) >= 1)
1359 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1360 return(MagickFalse);
1363 image_view=AcquireAuthenticCacheView(image,exception);
1364 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1365 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1366 magick_threads(image,image,image->rows,1)
1368 for (y=0; y < (ssize_t) image->rows; y++)
1376 if (status == MagickFalse)
1378 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1379 if (q == (Quantum *) NULL)
1384 for (x=0; x < (ssize_t) image->columns; x++)
1393 if (GetPixelMask(image,q) != 0)
1395 q+=GetPixelChannels(image);
1398 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1410 channel=GetPixelChannelChannel(image,i);
1411 traits=GetPixelChannelTraits(image,channel);
1412 if ((traits & UpdatePixelTrait) == 0)
1414 if (fabs(levels[n++]) < MagickEpsilon)
1416 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1417 level=threshold/(map->divisor-1);
1418 threshold-=level*(map->divisor-1);
1419 q[i]=ClampToQuantum((double) (level+(threshold >=
1420 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1421 QuantumRange/levels[n]);
1424 q+=GetPixelChannels(image);
1426 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1428 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1433 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1434 #pragma omp critical (MagickCore_OrderedPosterizeImage)
1436 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1437 if (proceed == MagickFalse)
1441 image_view=DestroyCacheView(image_view);
1442 map=DestroyThresholdMap(map);
1447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1451 % P e r c e p t i b l e I m a g e %
1455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1458 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1461 % The format of the PerceptibleImage method is:
1463 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1464 % ExceptionInfo *exception)
1466 % A description of each parameter follows:
1468 % o image: the image.
1470 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1472 % o exception: return any errors or warnings in this structure.
1476 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1477 const double epsilon)
1482 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1483 if ((sign*quantum) >= epsilon)
1485 return((Quantum) (sign*epsilon));
1488 MagickExport MagickBooleanType PerceptibleImage(Image *image,
1489 const double epsilon,ExceptionInfo *exception)
1491 #define PerceptibleImageTag "Perceptible/Image"
1505 assert(image != (Image *) NULL);
1506 assert(image->signature == MagickSignature);
1507 if (image->debug != MagickFalse)
1508 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1509 if (image->storage_class == PseudoClass)
1518 for (i=0; i < (ssize_t) image->colors; i++)
1520 q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1522 q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1524 q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1526 q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1530 return(SyncImage(image,exception));
1537 image_view=AcquireAuthenticCacheView(image,exception);
1538 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1539 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1540 magick_threads(image,image,image->rows,1)
1542 for (y=0; y < (ssize_t) image->rows; y++)
1550 if (status == MagickFalse)
1552 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1553 if (q == (Quantum *) NULL)
1558 for (x=0; x < (ssize_t) image->columns; x++)
1563 if (GetPixelMask(image,q) != 0)
1565 q+=GetPixelChannels(image);
1568 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1576 channel=GetPixelChannelChannel(image,i);
1577 traits=GetPixelChannelTraits(image,channel);
1578 if (traits == UndefinedPixelTrait)
1580 q[i]=PerceptibleThreshold(q[i],epsilon);
1582 q+=GetPixelChannels(image);
1584 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1586 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1591 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1592 #pragma omp critical (MagickCore_PerceptibleImage)
1594 proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1595 if (proceed == MagickFalse)
1599 image_view=DestroyCacheView(image_view);
1604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1608 % R a n d o m T h r e s h o l d I m a g e %
1612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1614 % RandomThresholdImage() changes the value of individual pixels based on the
1615 % intensity of each pixel compared to a random threshold. The result is a
1616 % low-contrast, two color image.
1618 % The format of the RandomThresholdImage method is:
1620 % MagickBooleanType RandomThresholdImage(Image *image,
1621 % const char *thresholds,ExceptionInfo *exception)
1623 % A description of each parameter follows:
1625 % o image: the image.
1627 % o thresholds: a geometry string containing low,high thresholds. If the
1628 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1629 % is performed instead.
1631 % o exception: return any errors or warnings in this structure.
1634 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1635 const char *thresholds,ExceptionInfo *exception)
1637 #define ThresholdImageTag "Threshold/Image"
1662 **restrict random_info;
1667 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1672 assert(image != (Image *) NULL);
1673 assert(image->signature == MagickSignature);
1674 if (image->debug != MagickFalse)
1675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1676 assert(exception != (ExceptionInfo *) NULL);
1677 assert(exception->signature == MagickSignature);
1678 if (thresholds == (const char *) NULL)
1680 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1681 return(MagickFalse);
1682 GetPixelInfo(image,&threshold);
1684 max_threshold=(double) QuantumRange;
1685 flags=ParseGeometry(thresholds,&geometry_info);
1686 min_threshold=geometry_info.rho;
1687 max_threshold=geometry_info.sigma;
1688 if ((flags & SigmaValue) == 0)
1689 max_threshold=min_threshold;
1690 if (strchr(thresholds,'%') != (char *) NULL)
1692 max_threshold*=(double) (0.01*QuantumRange);
1693 min_threshold*=(double) (0.01*QuantumRange);
1696 Random threshold image.
1700 random_info=AcquireRandomInfoThreadSet();
1701 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1702 key=GetRandomSecretKey(random_info[0]);
1704 image_view=AcquireAuthenticCacheView(image,exception);
1705 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1706 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1707 magick_threads(image,image,image->rows,key == ~0UL)
1709 for (y=0; y < (ssize_t) image->rows; y++)
1712 id = GetOpenMPThreadId();
1720 if (status == MagickFalse)
1722 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1723 if (q == (Quantum *) NULL)
1728 for (x=0; x < (ssize_t) image->columns; x++)
1733 if (GetPixelMask(image,q) != 0)
1735 q+=GetPixelChannels(image);
1738 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1749 channel=GetPixelChannelChannel(image,i);
1750 traits=GetPixelChannelTraits(image,channel);
1751 if ((traits & UpdatePixelTrait) == 0)
1753 if ((double) q[i] < min_threshold)
1754 threshold=min_threshold;
1756 if ((double) q[i] > max_threshold)
1757 threshold=max_threshold;
1759 threshold=(double) (QuantumRange*
1760 GetPseudoRandomValue(random_info[id]));
1761 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
1763 q+=GetPixelChannels(image);
1765 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1767 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1772 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1773 #pragma omp critical (MagickCore_RandomThresholdImage)
1775 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1777 if (proceed == MagickFalse)
1781 image_view=DestroyCacheView(image_view);
1782 random_info=DestroyRandomInfoThreadSet(random_info);
1787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1791 % W h i t e T h r e s h o l d I m a g e %
1795 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1797 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1798 % the threshold into white while leaving all pixels at or below the threshold
1801 % The format of the WhiteThresholdImage method is:
1803 % MagickBooleanType WhiteThresholdImage(Image *image,
1804 % const char *threshold,ExceptionInfo *exception)
1806 % A description of each parameter follows:
1808 % o image: the image.
1810 % o threshold: Define the threshold value.
1812 % o exception: return any errors or warnings in this structure.
1815 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1816 const char *thresholds,ExceptionInfo *exception)
1818 #define ThresholdImageTag "Threshold/Image"
1841 assert(image != (Image *) NULL);
1842 assert(image->signature == MagickSignature);
1843 if (image->debug != MagickFalse)
1844 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1845 if (thresholds == (const char *) NULL)
1847 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1848 return(MagickFalse);
1849 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1850 (void) TransformImageColorspace(image,RGBColorspace,exception);
1851 GetPixelInfo(image,&threshold);
1852 flags=ParseGeometry(thresholds,&geometry_info);
1853 threshold.red=geometry_info.rho;
1854 threshold.green=geometry_info.rho;
1855 threshold.blue=geometry_info.rho;
1856 threshold.black=geometry_info.rho;
1857 threshold.alpha=100.0;
1858 if ((flags & SigmaValue) != 0)
1859 threshold.green=geometry_info.sigma;
1860 if ((flags & XiValue) != 0)
1861 threshold.blue=geometry_info.xi;
1862 if ((flags & PsiValue) != 0)
1863 threshold.alpha=geometry_info.psi;
1864 if (threshold.colorspace == CMYKColorspace)
1866 if ((flags & PsiValue) != 0)
1867 threshold.black=geometry_info.psi;
1868 if ((flags & ChiValue) != 0)
1869 threshold.alpha=geometry_info.chi;
1871 if ((flags & PercentValue) != 0)
1873 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1874 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1875 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1876 threshold.black*=(MagickRealType) (QuantumRange/100.0);
1877 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
1880 White threshold image.
1884 image_view=AcquireAuthenticCacheView(image,exception);
1885 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1886 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1887 magick_threads(image,image,image->rows,1)
1889 for (y=0; y < (ssize_t) image->rows; y++)
1897 if (status == MagickFalse)
1899 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1900 if (q == (Quantum *) NULL)
1905 for (x=0; x < (ssize_t) image->columns; x++)
1913 if (GetPixelMask(image,q) != 0)
1915 q+=GetPixelChannels(image);
1918 pixel=GetPixelIntensity(image,q);
1919 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1927 channel=GetPixelChannelChannel(image,i);
1928 traits=GetPixelChannelTraits(image,channel);
1929 if ((traits & UpdatePixelTrait) == 0)
1931 if (image->channel_mask != DefaultChannels)
1932 pixel=(double) q[i];
1933 if (pixel > GetPixelInfoChannel(&threshold,channel))
1936 q+=GetPixelChannels(image);
1938 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1940 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1945 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1946 #pragma omp critical (MagickCore_WhiteThresholdImage)
1948 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1950 if (proceed == MagickFalse)
1954 image_view=DestroyCacheView(image_view);