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-2016 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/pixel-private.h"
73 #include "MagickCore/quantize.h"
74 #include "MagickCore/quantum.h"
75 #include "MagickCore/random_.h"
76 #include "MagickCore/random-private.h"
77 #include "MagickCore/resize.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/segment.h"
80 #include "MagickCore/shear.h"
81 #include "MagickCore/signature-private.h"
82 #include "MagickCore/string_.h"
83 #include "MagickCore/string-private.h"
84 #include "MagickCore/thread-private.h"
85 #include "MagickCore/threshold.h"
86 #include "MagickCore/token.h"
87 #include "MagickCore/transform.h"
88 #include "MagickCore/xml-tree.h"
89 #include "MagickCore/xml-tree-private.h"
94 #define ThresholdsFilename "thresholds.xml"
118 *MinimalThresholdMap =
119 "<?xml version=\"1.0\"?>"
121 " <threshold map=\"threshold\" alias=\"1x1\">"
122 " <description>Threshold 1x1 (non-dither)</description>"
123 " <levels width=\"1\" height=\"1\" divisor=\"2\">"
127 " <threshold map=\"checks\" alias=\"2x1\">"
128 " <description>Checkerboard 2x1 (dither)</description>"
129 " <levels width=\"2\" height=\"2\" divisor=\"3\">"
137 Forward declarations.
140 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147 % A d a p t i v e T h r e s h o l d I m a g e %
151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 % AdaptiveThresholdImage() selects an individual threshold for each pixel
154 % based on the range of intensity values in its local neighborhood. This
155 % allows for thresholding of an image whose global intensity histogram
156 % doesn't contain distinctive peaks.
158 % The format of the AdaptiveThresholdImage method is:
160 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
161 % const size_t height,const double bias,ExceptionInfo *exception)
163 % A description of each parameter follows:
165 % o image: the image.
167 % o width: the width of the local neighborhood.
169 % o height: the height of the local neighborhood.
171 % o bias: the mean bias.
173 % o exception: return any errors or warnings in this structure.
176 MagickExport Image *AdaptiveThresholdImage(const Image *image,
177 const size_t width,const size_t height,const double bias,
178 ExceptionInfo *exception)
180 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
202 Initialize threshold image attributes.
204 assert(image != (Image *) NULL);
205 assert(image->signature == MagickCoreSignature);
206 if (image->debug != MagickFalse)
207 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
208 assert(exception != (ExceptionInfo *) NULL);
209 assert(exception->signature == MagickCoreSignature);
210 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
212 if (threshold_image == (Image *) NULL)
213 return((Image *) NULL);
214 status=SetImageStorageClass(threshold_image,DirectClass,exception);
215 if (status == MagickFalse)
217 threshold_image=DestroyImage(threshold_image);
218 return((Image *) NULL);
225 number_pixels=(MagickSizeType) width*height;
226 image_view=AcquireVirtualCacheView(image,exception);
227 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
228 #if defined(MAGICKCORE_OPENMP_SUPPORT)
229 #pragma omp parallel for schedule(static,4) shared(progress,status) \
230 magick_threads(image,threshold_image,image->rows,1)
232 for (y=0; y < (ssize_t) image->rows; y++)
235 channel_bias[MaxPixelChannels],
236 channel_sum[MaxPixelChannels];
238 register const Quantum
240 *magick_restrict pixels;
254 if (status == MagickFalse)
256 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
257 (height/2L),image->columns+width,height,exception);
258 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
260 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
265 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
266 GetPixelChannels(image)*(width/2);
267 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
269 PixelChannel channel=GetPixelChannelChannel(image,i);
270 PixelTrait traits=GetPixelChannelTraits(image,channel);
271 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
273 if ((traits == UndefinedPixelTrait) ||
274 (threshold_traits == UndefinedPixelTrait))
276 if (((threshold_traits & CopyPixelTrait) != 0) ||
277 (GetPixelWriteMask(image,p) == QuantumRange))
279 SetPixelChannel(threshold_image,channel,p[center+i],q);
283 channel_bias[channel]=0.0;
284 channel_sum[channel]=0.0;
285 for (v=0; v < (ssize_t) height; v++)
287 for (u=0; u < (ssize_t) width; u++)
289 if (u == (ssize_t) (width-1))
290 channel_bias[channel]+=pixels[i];
291 channel_sum[channel]+=pixels[i];
292 pixels+=GetPixelChannels(image);
294 pixels+=GetPixelChannels(image)*image->columns;
297 for (x=0; x < (ssize_t) image->columns; x++)
299 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
304 PixelChannel channel=GetPixelChannelChannel(image,i);
305 PixelTrait traits=GetPixelChannelTraits(image,channel);
306 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
308 if ((traits == UndefinedPixelTrait) ||
309 (threshold_traits == UndefinedPixelTrait))
311 if (((threshold_traits & CopyPixelTrait) != 0) ||
312 (GetPixelWriteMask(image,p) == QuantumRange))
314 SetPixelChannel(threshold_image,channel,p[center+i],q);
317 channel_sum[channel]-=channel_bias[channel];
318 channel_bias[channel]=0.0;
320 for (v=0; v < (ssize_t) height; v++)
322 channel_bias[channel]+=pixels[i];
323 pixels+=(width-1)*GetPixelChannels(image);
324 channel_sum[channel]+=pixels[i];
325 pixels+=GetPixelChannels(image)*(image->columns+1);
327 mean=(double) (channel_sum[channel]/number_pixels+bias);
328 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
329 p[center+i] <= mean ? 0 : QuantumRange),q);
331 p+=GetPixelChannels(image);
332 q+=GetPixelChannels(threshold_image);
334 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
336 if (image->progress_monitor != (MagickProgressMonitor) NULL)
341 #if defined(MAGICKCORE_OPENMP_SUPPORT)
342 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
344 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
346 if (proceed == MagickFalse)
350 threshold_image->type=image->type;
351 threshold_view=DestroyCacheView(threshold_view);
352 image_view=DestroyCacheView(image_view);
353 if (status == MagickFalse)
354 threshold_image=DestroyImage(threshold_image);
355 return(threshold_image);
359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363 % B i l e v e l I m a g e %
367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369 % BilevelImage() changes the value of individual pixels based on the
370 % intensity of each pixel channel. The result is a high-contrast image.
372 % More precisely each channel value of the image is 'thresholded' so that if
373 % it is equal to or less than the given value it is set to zero, while any
374 % value greater than that give is set to it maximum or QuantumRange.
376 % This function is what is used to implement the "-threshold" operator for
377 % the command line API.
379 % If the default channel setting is given the image is thresholded using just
380 % the gray 'intensity' of the image, rather than the individual channels.
382 % The format of the BilevelImage method is:
384 % MagickBooleanType BilevelImage(Image *image,const double threshold,
385 % ExceptionInfo *exception)
387 % A description of each parameter follows:
389 % o image: the image.
391 % o threshold: define the threshold values.
393 % o exception: return any errors or warnings in this structure.
395 % Aside: You can get the same results as operator using LevelImages()
396 % with the 'threshold' value for both the black_point and the white_point.
399 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
400 ExceptionInfo *exception)
402 #define ThresholdImageTag "Threshold/Image"
416 assert(image != (Image *) NULL);
417 assert(image->signature == MagickCoreSignature);
418 if (image->debug != MagickFalse)
419 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
420 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
422 if (IsGrayColorspace(image->colorspace) != MagickFalse)
423 (void) SetImageColorspace(image,sRGBColorspace,exception);
425 Bilevel threshold image.
429 image_view=AcquireAuthenticCacheView(image,exception);
430 #if defined(MAGICKCORE_OPENMP_SUPPORT)
431 #pragma omp parallel for schedule(static,4) shared(progress,status) \
432 magick_threads(image,image,image->rows,1)
434 for (y=0; y < (ssize_t) image->rows; y++)
442 if (status == MagickFalse)
444 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
445 if (q == (Quantum *) NULL)
450 for (x=0; x < (ssize_t) image->columns; x++)
458 if (GetPixelWriteMask(image,q) == QuantumRange)
460 q+=GetPixelChannels(image);
463 pixel=GetPixelIntensity(image,q);
464 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
466 PixelChannel channel=GetPixelChannelChannel(image,i);
467 PixelTrait traits=GetPixelChannelTraits(image,channel);
468 if ((traits & UpdatePixelTrait) == 0)
470 if (image->channel_mask != DefaultChannels)
472 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
474 q+=GetPixelChannels(image);
476 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
478 if (image->progress_monitor != (MagickProgressMonitor) NULL)
483 #if defined(MAGICKCORE_OPENMP_SUPPORT)
484 #pragma omp critical (MagickCore_BilevelImage)
486 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
488 if (proceed == MagickFalse)
492 image_view=DestroyCacheView(image_view);
497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501 % B l a c k T h r e s h o l d I m a g e %
505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
508 % the threshold into black while leaving all pixels at or above the threshold
511 % The format of the BlackThresholdImage method is:
513 % MagickBooleanType BlackThresholdImage(Image *image,
514 % const char *threshold,ExceptionInfo *exception)
516 % A description of each parameter follows:
518 % o image: the image.
520 % o threshold: define the threshold value.
522 % o exception: return any errors or warnings in this structure.
525 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
526 const char *thresholds,ExceptionInfo *exception)
528 #define ThresholdImageTag "Threshold/Image"
551 assert(image != (Image *) NULL);
552 assert(image->signature == MagickCoreSignature);
553 if (image->debug != MagickFalse)
554 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
555 if (thresholds == (const char *) NULL)
557 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
559 if (IsGrayColorspace(image->colorspace) != MagickFalse)
560 (void) SetImageColorspace(image,sRGBColorspace,exception);
561 GetPixelInfo(image,&threshold);
562 flags=ParseGeometry(thresholds,&geometry_info);
563 threshold.red=geometry_info.rho;
564 threshold.green=geometry_info.rho;
565 threshold.blue=geometry_info.rho;
566 threshold.black=geometry_info.rho;
567 threshold.alpha=100.0;
568 if ((flags & SigmaValue) != 0)
569 threshold.green=geometry_info.sigma;
570 if ((flags & XiValue) != 0)
571 threshold.blue=geometry_info.xi;
572 if ((flags & PsiValue) != 0)
573 threshold.alpha=geometry_info.psi;
574 if (threshold.colorspace == CMYKColorspace)
576 if ((flags & PsiValue) != 0)
577 threshold.black=geometry_info.psi;
578 if ((flags & ChiValue) != 0)
579 threshold.alpha=geometry_info.chi;
581 if ((flags & PercentValue) != 0)
583 threshold.red*=(MagickRealType) (QuantumRange/100.0);
584 threshold.green*=(MagickRealType) (QuantumRange/100.0);
585 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
586 threshold.black*=(MagickRealType) (QuantumRange/100.0);
587 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
590 White threshold image.
594 image_view=AcquireAuthenticCacheView(image,exception);
595 #if defined(MAGICKCORE_OPENMP_SUPPORT)
596 #pragma omp parallel for schedule(static,4) shared(progress,status) \
597 magick_threads(image,image,image->rows,1)
599 for (y=0; y < (ssize_t) image->rows; y++)
607 if (status == MagickFalse)
609 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
610 if (q == (Quantum *) NULL)
615 for (x=0; x < (ssize_t) image->columns; x++)
623 if (GetPixelWriteMask(image,q) == QuantumRange)
625 q+=GetPixelChannels(image);
628 pixel=GetPixelIntensity(image,q);
629 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
631 PixelChannel channel=GetPixelChannelChannel(image,i);
632 PixelTrait traits=GetPixelChannelTraits(image,channel);
633 if ((traits & UpdatePixelTrait) == 0)
635 if (image->channel_mask != DefaultChannels)
637 if (pixel < GetPixelInfoChannel(&threshold,channel))
640 q+=GetPixelChannels(image);
642 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
644 if (image->progress_monitor != (MagickProgressMonitor) NULL)
649 #if defined(MAGICKCORE_OPENMP_SUPPORT)
650 #pragma omp critical (MagickCore_BlackThresholdImage)
652 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
654 if (proceed == MagickFalse)
658 image_view=DestroyCacheView(image_view);
663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667 % C l a m p I m a g e %
671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
673 % ClampImage() set each pixel whose value is below zero to zero and any the
674 % pixel whose value is above the quantum range to the quantum range (e.g.
675 % 65535) otherwise the pixel value remains unchanged.
677 % The format of the ClampImage method is:
679 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
681 % A description of each parameter follows:
683 % o image: the image.
685 % o exception: return any errors or warnings in this structure.
689 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
691 #define ClampImageTag "Clamp/Image"
705 assert(image != (Image *) NULL);
706 assert(image->signature == MagickCoreSignature);
707 if (image->debug != MagickFalse)
708 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
709 if (image->storage_class == PseudoClass)
718 for (i=0; i < (ssize_t) image->colors; i++)
720 q->red=(double) ClampPixel(q->red);
721 q->green=(double) ClampPixel(q->green);
722 q->blue=(double) ClampPixel(q->blue);
723 q->alpha=(double) ClampPixel(q->alpha);
726 return(SyncImage(image,exception));
733 image_view=AcquireAuthenticCacheView(image,exception);
734 #if defined(MAGICKCORE_OPENMP_SUPPORT)
735 #pragma omp parallel for schedule(static,4) shared(progress,status) \
736 magick_threads(image,image,image->rows,1)
738 for (y=0; y < (ssize_t) image->rows; y++)
746 if (status == MagickFalse)
748 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
749 if (q == (Quantum *) NULL)
754 for (x=0; x < (ssize_t) image->columns; x++)
759 if (GetPixelWriteMask(image,q) == QuantumRange)
761 q+=GetPixelChannels(image);
764 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
766 PixelChannel channel=GetPixelChannelChannel(image,i);
767 PixelTrait traits=GetPixelChannelTraits(image,channel);
768 if ((traits & UpdatePixelTrait) == 0)
770 q[i]=ClampPixel(q[i]);
772 q+=GetPixelChannels(image);
774 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
776 if (image->progress_monitor != (MagickProgressMonitor) NULL)
781 #if defined(MAGICKCORE_OPENMP_SUPPORT)
782 #pragma omp critical (MagickCore_ClampImage)
784 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
785 if (proceed == MagickFalse)
789 image_view=DestroyCacheView(image_view);
794 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798 % D e s t r o y T h r e s h o l d M a p %
802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
804 % DestroyThresholdMap() de-allocate the given ThresholdMap
806 % The format of the ListThresholdMaps method is:
808 % ThresholdMap *DestroyThresholdMap(Threshold *map)
810 % A description of each parameter follows.
812 % o map: Pointer to the Threshold map to destroy
815 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
817 assert(map != (ThresholdMap *) NULL);
818 if (map->map_id != (char *) NULL)
819 map->map_id=DestroyString(map->map_id);
820 if (map->description != (char *) NULL)
821 map->description=DestroyString(map->description);
822 if (map->levels != (ssize_t *) NULL)
823 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
824 map=(ThresholdMap *) RelinquishMagickMemory(map);
829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
833 % G e t T h r e s h o l d M a p %
837 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839 % GetThresholdMap() loads and searches one or more threshold map files for the
840 % map matching the given name or alias.
842 % The format of the GetThresholdMap method is:
844 % ThresholdMap *GetThresholdMap(const char *map_id,
845 % ExceptionInfo *exception)
847 % A description of each parameter follows.
849 % o map_id: ID of the map to look for.
851 % o exception: return any errors or warnings in this structure.
854 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
855 ExceptionInfo *exception)
860 map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
861 if (map != (ThresholdMap *) NULL)
863 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
871 options=GetConfigureOptions(ThresholdsFilename,exception);
872 option=(const StringInfo *) GetNextValueInLinkedList(options);
873 while (option != (const StringInfo *) NULL)
875 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
876 GetStringInfoPath(option),map_id,exception);
877 if (map != (ThresholdMap *) NULL)
879 option=(const StringInfo *) GetNextValueInLinkedList(options);
881 options=DestroyConfigureOptions(options);
888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
892 + G e t T h r e s h o l d M a p F i l e %
896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
898 % GetThresholdMapFile() look for a given threshold map name or alias in the
899 % given XML file data, and return the allocated the map when found.
901 % The format of the ListThresholdMaps method is:
903 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
904 % const char *map_id,ExceptionInfo *exception)
906 % A description of each parameter follows.
908 % o xml: The threshold map list in XML format.
910 % o filename: The threshold map XML filename.
912 % o map_id: ID of the map to look for in XML list.
914 % o exception: return any errors or warnings in this structure.
917 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
918 const char *map_id,ExceptionInfo *exception)
942 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
943 "Loading threshold map file \"%s\" ...",filename);
944 map=(ThresholdMap *) NULL;
945 thresholds=NewXMLTree(xml,exception);
946 if (thresholds == (XMLTreeInfo *) NULL)
948 for (threshold=GetXMLTreeChild(thresholds,"threshold");
949 threshold != (XMLTreeInfo *) NULL;
950 threshold=GetNextXMLTreeTag(threshold))
952 attribute=GetXMLTreeAttribute(threshold,"map");
953 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
955 attribute=GetXMLTreeAttribute(threshold,"alias");
956 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
959 if (threshold == (XMLTreeInfo *) NULL)
961 thresholds=DestroyXMLTree(thresholds);
964 description=GetXMLTreeChild(threshold,"description");
965 if (description == (XMLTreeInfo *) NULL)
967 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
968 "XmlMissingElement", "<description>, map \"%s\"",map_id);
969 thresholds=DestroyXMLTree(thresholds);
972 levels=GetXMLTreeChild(threshold,"levels");
973 if (levels == (XMLTreeInfo *) NULL)
975 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
976 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
977 thresholds=DestroyXMLTree(thresholds);
980 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
981 if (map == (ThresholdMap *) NULL)
982 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
983 map->map_id=(char *) NULL;
984 map->description=(char *) NULL;
985 map->levels=(ssize_t *) NULL;
986 attribute=GetXMLTreeAttribute(threshold,"map");
987 if (attribute != (char *) NULL)
988 map->map_id=ConstantString(attribute);
989 content=GetXMLTreeContent(description);
990 if (content != (char *) NULL)
991 map->description=ConstantString(content);
992 attribute=GetXMLTreeAttribute(levels,"width");
993 if (attribute == (char *) NULL)
995 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
996 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
997 thresholds=DestroyXMLTree(thresholds);
998 map=DestroyThresholdMap(map);
1001 map->width=StringToUnsignedLong(attribute);
1002 if (map->width == 0)
1004 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1005 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1006 thresholds=DestroyXMLTree(thresholds);
1007 map=DestroyThresholdMap(map);
1010 attribute=GetXMLTreeAttribute(levels,"height");
1011 if (attribute == (char *) NULL)
1013 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1014 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1015 thresholds=DestroyXMLTree(thresholds);
1016 map=DestroyThresholdMap(map);
1019 map->height=StringToUnsignedLong(attribute);
1020 if (map->height == 0)
1022 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1023 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1024 thresholds=DestroyXMLTree(thresholds);
1025 map=DestroyThresholdMap(map);
1028 attribute=GetXMLTreeAttribute(levels,"divisor");
1029 if (attribute == (char *) NULL)
1031 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1032 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1033 thresholds=DestroyXMLTree(thresholds);
1034 map=DestroyThresholdMap(map);
1037 map->divisor=(ssize_t) StringToLong(attribute);
1038 if (map->divisor < 2)
1040 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1041 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1042 thresholds=DestroyXMLTree(thresholds);
1043 map=DestroyThresholdMap(map);
1046 content=GetXMLTreeContent(levels);
1047 if (content == (char *) NULL)
1049 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1050 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1051 thresholds=DestroyXMLTree(thresholds);
1052 map=DestroyThresholdMap(map);
1055 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1056 sizeof(*map->levels));
1057 if (map->levels == (ssize_t *) NULL)
1058 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1059 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1061 map->levels[i]=(ssize_t) strtol(content,&p,10);
1064 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1065 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1066 thresholds=DestroyXMLTree(thresholds);
1067 map=DestroyThresholdMap(map);
1070 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1072 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1073 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1074 (double) map->levels[i],map_id);
1075 thresholds=DestroyXMLTree(thresholds);
1076 map=DestroyThresholdMap(map);
1081 value=(double) strtol(content,&p,10);
1085 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1086 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1087 thresholds=DestroyXMLTree(thresholds);
1088 map=DestroyThresholdMap(map);
1091 thresholds=DestroyXMLTree(thresholds);
1096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100 + L i s t T h r e s h o l d M a p F i l e %
1104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1106 % ListThresholdMapFile() lists the threshold maps and their descriptions
1107 % in the given XML file data.
1109 % The format of the ListThresholdMaps method is:
1111 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1112 % const char *filename,ExceptionInfo *exception)
1114 % A description of each parameter follows.
1116 % o file: An pointer to the output FILE.
1118 % o xml: The threshold map list in XML format.
1120 % o filename: The threshold map XML filename.
1122 % o exception: return any errors or warnings in this structure.
1125 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1126 const char *filename,ExceptionInfo *exception)
1138 assert( xml != (char *) NULL );
1139 assert( file != (FILE *) NULL );
1140 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1141 "Loading threshold map file \"%s\" ...",filename);
1142 thresholds=NewXMLTree(xml,exception);
1143 if ( thresholds == (XMLTreeInfo *) NULL )
1144 return(MagickFalse);
1145 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1146 (void) FormatLocaleFile(file,
1147 "----------------------------------------------------\n");
1148 threshold=GetXMLTreeChild(thresholds,"threshold");
1149 for ( ; threshold != (XMLTreeInfo *) NULL;
1150 threshold=GetNextXMLTreeTag(threshold))
1152 map=GetXMLTreeAttribute(threshold,"map");
1153 if (map == (char *) NULL)
1155 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1156 "XmlMissingAttribute", "<map>");
1157 thresholds=DestroyXMLTree(thresholds);
1158 return(MagickFalse);
1160 alias=GetXMLTreeAttribute(threshold,"alias");
1161 description=GetXMLTreeChild(threshold,"description");
1162 if (description == (XMLTreeInfo *) NULL)
1164 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1165 "XmlMissingElement", "<description>, map \"%s\"",map);
1166 thresholds=DestroyXMLTree(thresholds);
1167 return(MagickFalse);
1169 content=GetXMLTreeContent(description);
1170 if (content == (char *) NULL)
1172 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1173 "XmlMissingContent", "<description>, map \"%s\"", map);
1174 thresholds=DestroyXMLTree(thresholds);
1175 return(MagickFalse);
1177 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1180 thresholds=DestroyXMLTree(thresholds);
1185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1189 % L i s t T h r e s h o l d M a p s %
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1195 % ListThresholdMaps() lists the threshold maps and their descriptions
1196 % as defined by "threshold.xml" to a file.
1198 % The format of the ListThresholdMaps method is:
1200 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1202 % A description of each parameter follows.
1204 % o file: An pointer to the output FILE.
1206 % o exception: return any errors or warnings in this structure.
1209 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1210 ExceptionInfo *exception)
1222 if (file == (FILE *) NULL)
1224 options=GetConfigureOptions(ThresholdsFilename,exception);
1225 (void) FormatLocaleFile(file,
1226 "\n Threshold Maps for Ordered Dither Operations\n");
1227 option=(const StringInfo *) GetNextValueInLinkedList(options);
1228 while (option != (const StringInfo *) NULL)
1230 (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1231 status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1232 GetStringInfoPath(option),exception);
1233 option=(const StringInfo *) GetNextValueInLinkedList(options);
1235 options=DestroyConfigureOptions(options);
1236 return(status != 0 ? MagickTrue : MagickFalse);
1240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1244 % O r d e r e d D i t h e r I m a g e %
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1250 % OrderedDitherImage() will perform a ordered dither based on a number
1251 % of pre-defined dithering threshold maps, but over multiple intensity
1252 % levels, which can be different for different channels, according to the
1255 % The format of the OrderedDitherImage method is:
1257 % MagickBooleanType OrderedDitherImage(Image *image,
1258 % const char *threshold_map,ExceptionInfo *exception)
1260 % A description of each parameter follows:
1262 % o image: the image.
1264 % o threshold_map: A string containing the name of the threshold dither
1265 % map to use, followed by zero or more numbers representing the number
1266 % of color levels tho dither between.
1268 % Any level number less than 2 will be equivalent to 2, and means only
1269 % binary dithering will be applied to each color channel.
1271 % No numbers also means a 2 level (bitmap) dither will be applied to all
1272 % channels, while a single number is the number of levels applied to each
1273 % channel in sequence. More numbers will be applied in turn to each of
1274 % the color channels.
1276 % For example: "o3x3,6" will generate a 6 level posterization of the
1277 % image with a ordered 3x3 diffused pixel dither being applied between
1278 % each level. While checker,8,8,4 will produce a 332 colormaped image
1279 % with only a single checkerboard hash pattern (50% grey) between each
1280 % color level, to basically double the number of color levels with
1281 % a bare minimim of dithering.
1283 % o exception: return any errors or warnings in this structure.
1286 MagickExport MagickBooleanType OrderedDitherImage(Image *image,
1287 const char *threshold_map,ExceptionInfo *exception)
1289 #define DitherImageTag "Dither/Image"
1295 token[MagickPathExtent];
1301 levels[CompositePixelChannel];
1318 assert(image != (Image *) NULL);
1319 assert(image->signature == MagickCoreSignature);
1320 if (image->debug != MagickFalse)
1321 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1322 assert(exception != (ExceptionInfo *) NULL);
1323 assert(exception->signature == MagickCoreSignature);
1324 if (threshold_map == (const char *) NULL)
1326 p=(char *) threshold_map;
1327 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1331 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1334 if ((p-threshold_map) >= (MagickPathExtent-1))
1336 token[p-threshold_map]=(*p);
1339 token[p-threshold_map]='\0';
1340 map=GetThresholdMap(token,exception);
1341 if (map == (ThresholdMap *) NULL)
1343 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1344 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1345 return(MagickFalse);
1347 for (i=0; i < MaxPixelChannels; i++)
1349 p=strchr((char *) threshold_map,',');
1350 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1352 GetNextToken(p,&p,MagickPathExtent,token);
1353 for (i=0; (i < MaxPixelChannels); i++)
1354 levels[i]=StringToDouble(token,(char **) NULL);
1355 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1357 GetNextToken(p,&p,MagickPathExtent,token);
1359 GetNextToken(p,&p,MagickPathExtent,token);
1360 levels[i]=StringToDouble(token,(char **) NULL);
1363 for (i=0; i < MaxPixelChannels; i++)
1364 if (fabs(levels[i]) >= 1)
1366 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1367 return(MagickFalse);
1370 image_view=AcquireAuthenticCacheView(image,exception);
1371 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1372 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1373 magick_threads(image,image,image->rows,1)
1375 for (y=0; y < (ssize_t) image->rows; y++)
1383 if (status == MagickFalse)
1385 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1386 if (q == (Quantum *) NULL)
1391 for (x=0; x < (ssize_t) image->columns; x++)
1400 if (GetPixelWriteMask(image,q) == QuantumRange)
1402 q+=GetPixelChannels(image);
1405 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1411 PixelChannel channel=GetPixelChannelChannel(image,i);
1412 PixelTrait traits=GetPixelChannelTraits(image,channel);
1413 if ((traits & UpdatePixelTrait) == 0)
1415 if (fabs(levels[n]) < MagickEpsilon)
1420 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1421 level=threshold/(map->divisor-1);
1422 threshold-=level*(map->divisor-1);
1423 q[i]=ClampToQuantum((double) (level+(threshold >=
1424 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1425 QuantumRange/levels[n]);
1428 q+=GetPixelChannels(image);
1430 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1432 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1437 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1438 #pragma omp critical (MagickCore_OrderedDitherImage)
1440 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1441 if (proceed == MagickFalse)
1445 image_view=DestroyCacheView(image_view);
1446 map=DestroyThresholdMap(map);
1451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455 % P e r c e p t i b l e I m a g e %
1459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1461 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1462 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1465 % The format of the PerceptibleImage method is:
1467 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1468 % ExceptionInfo *exception)
1470 % A description of each parameter follows:
1472 % o image: the image.
1474 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1476 % o exception: return any errors or warnings in this structure.
1480 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1481 const double epsilon)
1486 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1487 if ((sign*quantum) >= epsilon)
1489 return((Quantum) (sign*epsilon));
1492 MagickExport MagickBooleanType PerceptibleImage(Image *image,
1493 const double epsilon,ExceptionInfo *exception)
1495 #define PerceptibleImageTag "Perceptible/Image"
1509 assert(image != (Image *) NULL);
1510 assert(image->signature == MagickCoreSignature);
1511 if (image->debug != MagickFalse)
1512 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1513 if (image->storage_class == PseudoClass)
1522 for (i=0; i < (ssize_t) image->colors; i++)
1524 q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1526 q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1528 q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1530 q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1534 return(SyncImage(image,exception));
1541 image_view=AcquireAuthenticCacheView(image,exception);
1542 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1543 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1544 magick_threads(image,image,image->rows,1)
1546 for (y=0; y < (ssize_t) image->rows; y++)
1554 if (status == MagickFalse)
1556 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1557 if (q == (Quantum *) NULL)
1562 for (x=0; x < (ssize_t) image->columns; x++)
1567 if (GetPixelWriteMask(image,q) == QuantumRange)
1569 q+=GetPixelChannels(image);
1572 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1574 PixelChannel channel=GetPixelChannelChannel(image,i);
1575 PixelTrait traits=GetPixelChannelTraits(image,channel);
1576 if (traits == UndefinedPixelTrait)
1578 q[i]=PerceptibleThreshold(q[i],epsilon);
1580 q+=GetPixelChannels(image);
1582 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1584 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1589 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1590 #pragma omp critical (MagickCore_PerceptibleImage)
1592 proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1593 if (proceed == MagickFalse)
1597 image_view=DestroyCacheView(image_view);
1602 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1606 % R a n d o m T h r e s h o l d I m a g e %
1610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1612 % RandomThresholdImage() changes the value of individual pixels based on the
1613 % intensity of each pixel compared to a random threshold. The result is a
1614 % low-contrast, two color image.
1616 % The format of the RandomThresholdImage method is:
1618 % MagickBooleanType RandomThresholdImage(Image *image,
1619 % const char *thresholds,ExceptionInfo *exception)
1621 % A description of each parameter follows:
1623 % o image: the image.
1625 % o low,high: Specify the high and low thresholds. These values range from
1626 % 0 to QuantumRange.
1628 % o exception: return any errors or warnings in this structure.
1631 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1632 const double min_threshold, const double max_threshold,ExceptionInfo *exception)
1634 #define ThresholdImageTag "Threshold/Image"
1649 **magick_restrict random_info;
1654 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1659 assert(image != (Image *) NULL);
1660 assert(image->signature == MagickCoreSignature);
1661 if (image->debug != MagickFalse)
1662 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1663 assert(exception != (ExceptionInfo *) NULL);
1664 assert(exception->signature == MagickCoreSignature);
1665 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1666 return(MagickFalse);
1667 GetPixelInfo(image,&threshold);
1669 Random threshold image.
1673 random_info=AcquireRandomInfoThreadSet();
1674 image_view=AcquireAuthenticCacheView(image,exception);
1675 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1676 key=GetRandomSecretKey(random_info[0]);
1677 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1678 magick_threads(image,image,image->rows,key == ~0UL)
1680 for (y=0; y < (ssize_t) image->rows; y++)
1683 id = GetOpenMPThreadId();
1691 if (status == MagickFalse)
1693 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1694 if (q == (Quantum *) NULL)
1699 for (x=0; x < (ssize_t) image->columns; x++)
1704 if (GetPixelWriteMask(image,q) == QuantumRange)
1706 q+=GetPixelChannels(image);
1709 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1714 PixelChannel channel=GetPixelChannelChannel(image,i);
1715 PixelTrait traits=GetPixelChannelTraits(image,channel);
1716 if ((traits & UpdatePixelTrait) == 0)
1718 if ((double) q[i] < min_threshold)
1719 threshold=min_threshold;
1721 if ((double) q[i] > max_threshold)
1722 threshold=max_threshold;
1724 threshold=(double) (QuantumRange*
1725 GetPseudoRandomValue(random_info[id]));
1726 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
1728 q+=GetPixelChannels(image);
1730 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1732 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1737 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1738 #pragma omp critical (MagickCore_RandomThresholdImage)
1740 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1742 if (proceed == MagickFalse)
1746 image_view=DestroyCacheView(image_view);
1747 random_info=DestroyRandomInfoThreadSet(random_info);
1752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1756 % W h i t e T h r e s h o l d I m a g e %
1760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1762 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1763 % the threshold into white while leaving all pixels at or below the threshold
1766 % The format of the WhiteThresholdImage method is:
1768 % MagickBooleanType WhiteThresholdImage(Image *image,
1769 % const char *threshold,ExceptionInfo *exception)
1771 % A description of each parameter follows:
1773 % o image: the image.
1775 % o threshold: Define the threshold value.
1777 % o exception: return any errors or warnings in this structure.
1780 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1781 const char *thresholds,ExceptionInfo *exception)
1783 #define ThresholdImageTag "Threshold/Image"
1806 assert(image != (Image *) NULL);
1807 assert(image->signature == MagickCoreSignature);
1808 if (image->debug != MagickFalse)
1809 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1810 if (thresholds == (const char *) NULL)
1812 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1813 return(MagickFalse);
1814 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1815 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1816 GetPixelInfo(image,&threshold);
1817 flags=ParseGeometry(thresholds,&geometry_info);
1818 threshold.red=geometry_info.rho;
1819 threshold.green=geometry_info.rho;
1820 threshold.blue=geometry_info.rho;
1821 threshold.black=geometry_info.rho;
1822 threshold.alpha=100.0;
1823 if ((flags & SigmaValue) != 0)
1824 threshold.green=geometry_info.sigma;
1825 if ((flags & XiValue) != 0)
1826 threshold.blue=geometry_info.xi;
1827 if ((flags & PsiValue) != 0)
1828 threshold.alpha=geometry_info.psi;
1829 if (threshold.colorspace == CMYKColorspace)
1831 if ((flags & PsiValue) != 0)
1832 threshold.black=geometry_info.psi;
1833 if ((flags & ChiValue) != 0)
1834 threshold.alpha=geometry_info.chi;
1836 if ((flags & PercentValue) != 0)
1838 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1839 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1840 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1841 threshold.black*=(MagickRealType) (QuantumRange/100.0);
1842 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
1845 White threshold image.
1849 image_view=AcquireAuthenticCacheView(image,exception);
1850 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1851 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1852 magick_threads(image,image,image->rows,1)
1854 for (y=0; y < (ssize_t) image->rows; y++)
1862 if (status == MagickFalse)
1864 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1865 if (q == (Quantum *) NULL)
1870 for (x=0; x < (ssize_t) image->columns; x++)
1878 if (GetPixelWriteMask(image,q) == QuantumRange)
1880 q+=GetPixelChannels(image);
1883 pixel=GetPixelIntensity(image,q);
1884 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1886 PixelChannel channel=GetPixelChannelChannel(image,i);
1887 PixelTrait traits=GetPixelChannelTraits(image,channel);
1888 if ((traits & UpdatePixelTrait) == 0)
1890 if (image->channel_mask != DefaultChannels)
1891 pixel=(double) q[i];
1892 if (pixel > GetPixelInfoChannel(&threshold,channel))
1895 q+=GetPixelChannels(image);
1897 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1899 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1904 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1905 #pragma omp critical (MagickCore_WhiteThresholdImage)
1907 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1909 if (proceed == MagickFalse)
1913 image_view=DestroyCacheView(image_view);