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-2014 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"
117 *MinimalThresholdMap =
118 "<?xml version=\"1.0\"?>"
120 " <threshold map=\"threshold\" alias=\"1x1\">"
121 " <description>Threshold 1x1 (non-dither)</description>"
122 " <levels width=\"1\" height=\"1\" divisor=\"2\">"
126 " <threshold map=\"checks\" alias=\"2x1\">"
127 " <description>Checkerboard 2x1 (dither)</description>"
128 " <levels width=\"2\" height=\"2\" divisor=\"3\">"
136 Forward declarations.
139 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 % A d a p t i v e T h r e s h o l d I m a g e %
150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152 % AdaptiveThresholdImage() selects an individual threshold for each pixel
153 % based on the range of intensity values in its local neighborhood. This
154 % allows for thresholding of an image whose global intensity histogram
155 % doesn't contain distinctive peaks.
157 % The format of the AdaptiveThresholdImage method is:
159 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
160 % const size_t height,const double bias,ExceptionInfo *exception)
162 % A description of each parameter follows:
164 % o image: the image.
166 % o width: the width of the local neighborhood.
168 % o height: the height of the local neighborhood.
170 % o bias: the mean bias.
172 % o exception: return any errors or warnings in this structure.
175 MagickExport Image *AdaptiveThresholdImage(const Image *image,
176 const size_t width,const size_t height,const double bias,
177 ExceptionInfo *exception)
179 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
201 Initialize threshold image attributes.
203 assert(image != (Image *) NULL);
204 assert(image->signature == MagickSignature);
205 if (image->debug != MagickFalse)
206 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
207 assert(exception != (ExceptionInfo *) NULL);
208 assert(exception->signature == MagickSignature);
209 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
211 if (threshold_image == (Image *) NULL)
212 return((Image *) NULL);
213 status=SetImageStorageClass(threshold_image,DirectClass,exception);
214 if (status == MagickFalse)
216 threshold_image=DestroyImage(threshold_image);
217 return((Image *) NULL);
224 number_pixels=(MagickSizeType) width*height;
225 image_view=AcquireVirtualCacheView(image,exception);
226 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
227 #if defined(MAGICKCORE_OPENMP_SUPPORT)
228 #pragma omp parallel for schedule(static,4) shared(progress,status) \
229 magick_threads(image,threshold_image,image->rows,1)
231 for (y=0; y < (ssize_t) image->rows; y++)
234 channel_bias[MaxPixelChannels],
235 channel_sum[MaxPixelChannels];
237 register const Quantum
253 if (status == MagickFalse)
255 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
256 (height/2L),image->columns+width,height,exception);
257 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
259 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
264 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
265 GetPixelChannels(image)*(width/2);
266 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
268 PixelChannel channel=GetPixelChannelChannel(image,i);
269 PixelTrait traits=GetPixelChannelTraits(image,channel);
270 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
272 if ((traits == UndefinedPixelTrait) ||
273 (threshold_traits == UndefinedPixelTrait))
275 if (((threshold_traits & CopyPixelTrait) != 0) ||
276 (GetPixelReadMask(image,p) == 0))
278 SetPixelChannel(threshold_image,channel,p[center+i],q);
282 channel_bias[channel]=0.0;
283 channel_sum[channel]=0.0;
284 for (v=0; v < (ssize_t) height; v++)
286 for (u=0; u < (ssize_t) width; u++)
288 if (u == (ssize_t) (width-1))
289 channel_bias[channel]+=pixels[i];
290 channel_sum[channel]+=pixels[i];
291 pixels+=GetPixelChannels(image);
293 pixels+=(image->columns-1)*GetPixelChannels(image);
296 for (x=0; x < (ssize_t) image->columns; x++)
298 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
303 PixelChannel channel=GetPixelChannelChannel(image,i);
304 PixelTrait traits=GetPixelChannelTraits(image,channel);
305 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
307 if ((traits == UndefinedPixelTrait) ||
308 (threshold_traits == UndefinedPixelTrait))
310 if (((threshold_traits & CopyPixelTrait) != 0) ||
311 (GetPixelReadMask(image,p) == 0))
313 SetPixelChannel(threshold_image,channel,p[center+i],q);
316 channel_sum[channel]-=channel_bias[channel];
317 channel_bias[channel]=0.0;
319 for (v=0; v < (ssize_t) height; v++)
321 channel_bias[channel]+=pixels[i];
322 pixels+=(width-1)*GetPixelChannels(image);
323 channel_sum[channel]+=pixels[i];
324 pixels+=(image->columns-1)*GetPixelChannels(image);
326 mean=(double) (channel_sum[channel]/number_pixels+bias);
327 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
328 p[center+i] <= mean ? 0 : QuantumRange),q);
330 p+=GetPixelChannels(image);
331 q+=GetPixelChannels(threshold_image);
333 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
335 if (image->progress_monitor != (MagickProgressMonitor) NULL)
340 #if defined(MAGICKCORE_OPENMP_SUPPORT)
341 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
343 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
345 if (proceed == MagickFalse)
349 threshold_image->type=image->type;
350 threshold_view=DestroyCacheView(threshold_view);
351 image_view=DestroyCacheView(image_view);
352 if (status == MagickFalse)
353 threshold_image=DestroyImage(threshold_image);
354 return(threshold_image);
358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362 % B i l e v e l I m a g e %
366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368 % BilevelImage() changes the value of individual pixels based on the
369 % intensity of each pixel channel. The result is a high-contrast image.
371 % More precisely each channel value of the image is 'thresholded' so that if
372 % it is equal to or less than the given value it is set to zero, while any
373 % value greater than that give is set to it maximum or QuantumRange.
375 % This function is what is used to implement the "-threshold" operator for
376 % the command line API.
378 % If the default channel setting is given the image is thresholded using just
379 % the gray 'intensity' of the image, rather than the individual channels.
381 % The format of the BilevelImage method is:
383 % MagickBooleanType BilevelImage(Image *image,const double threshold,
384 % ExceptionInfo *exception)
386 % A description of each parameter follows:
388 % o image: the image.
390 % o threshold: define the threshold values.
392 % o exception: return any errors or warnings in this structure.
394 % Aside: You can get the same results as operator using LevelImages()
395 % with the 'threshold' value for both the black_point and the white_point.
398 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
399 ExceptionInfo *exception)
401 #define ThresholdImageTag "Threshold/Image"
415 assert(image != (Image *) NULL);
416 assert(image->signature == MagickSignature);
417 if (image->debug != MagickFalse)
418 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
419 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
421 if (IsGrayColorspace(image->colorspace) != MagickFalse)
422 (void) SetImageColorspace(image,sRGBColorspace,exception);
424 Bilevel threshold image.
428 image_view=AcquireAuthenticCacheView(image,exception);
429 #if defined(MAGICKCORE_OPENMP_SUPPORT)
430 #pragma omp parallel for schedule(static,4) shared(progress,status) \
431 magick_threads(image,image,image->rows,1)
433 for (y=0; y < (ssize_t) image->rows; y++)
441 if (status == MagickFalse)
443 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
444 if (q == (Quantum *) NULL)
449 for (x=0; x < (ssize_t) image->columns; x++)
457 if (GetPixelReadMask(image,q) == 0)
459 q+=GetPixelChannels(image);
462 pixel=GetPixelIntensity(image,q);
463 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
465 PixelChannel channel=GetPixelChannelChannel(image,i);
466 PixelTrait traits=GetPixelChannelTraits(image,channel);
467 if ((traits & UpdatePixelTrait) == 0)
469 if (image->channel_mask != DefaultChannels)
471 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
473 q+=GetPixelChannels(image);
475 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
477 if (image->progress_monitor != (MagickProgressMonitor) NULL)
482 #if defined(MAGICKCORE_OPENMP_SUPPORT)
483 #pragma omp critical (MagickCore_BilevelImage)
485 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
487 if (proceed == MagickFalse)
491 image_view=DestroyCacheView(image_view);
496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 % B l a c k T h r e s h o l d I m a g e %
504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
507 % the threshold into black while leaving all pixels at or above the threshold
510 % The format of the BlackThresholdImage method is:
512 % MagickBooleanType BlackThresholdImage(Image *image,
513 % const char *threshold,ExceptionInfo *exception)
515 % A description of each parameter follows:
517 % o image: the image.
519 % o threshold: define the threshold value.
521 % o exception: return any errors or warnings in this structure.
524 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
525 const char *thresholds,ExceptionInfo *exception)
527 #define ThresholdImageTag "Threshold/Image"
550 assert(image != (Image *) NULL);
551 assert(image->signature == MagickSignature);
552 if (image->debug != MagickFalse)
553 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
554 if (thresholds == (const char *) NULL)
556 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
558 if (IsGrayColorspace(image->colorspace) != MagickFalse)
559 (void) SetImageColorspace(image,sRGBColorspace,exception);
560 GetPixelInfo(image,&threshold);
561 flags=ParseGeometry(thresholds,&geometry_info);
562 threshold.red=geometry_info.rho;
563 threshold.green=geometry_info.rho;
564 threshold.blue=geometry_info.rho;
565 threshold.black=geometry_info.rho;
566 threshold.alpha=100.0;
567 if ((flags & SigmaValue) != 0)
568 threshold.green=geometry_info.sigma;
569 if ((flags & XiValue) != 0)
570 threshold.blue=geometry_info.xi;
571 if ((flags & PsiValue) != 0)
572 threshold.alpha=geometry_info.psi;
573 if (threshold.colorspace == CMYKColorspace)
575 if ((flags & PsiValue) != 0)
576 threshold.black=geometry_info.psi;
577 if ((flags & ChiValue) != 0)
578 threshold.alpha=geometry_info.chi;
580 if ((flags & PercentValue) != 0)
582 threshold.red*=(MagickRealType) (QuantumRange/100.0);
583 threshold.green*=(MagickRealType) (QuantumRange/100.0);
584 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
585 threshold.black*=(MagickRealType) (QuantumRange/100.0);
586 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
589 White threshold image.
593 image_view=AcquireAuthenticCacheView(image,exception);
594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
595 #pragma omp parallel for schedule(static,4) shared(progress,status) \
596 magick_threads(image,image,image->rows,1)
598 for (y=0; y < (ssize_t) image->rows; y++)
606 if (status == MagickFalse)
608 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
609 if (q == (Quantum *) NULL)
614 for (x=0; x < (ssize_t) image->columns; x++)
622 if (GetPixelReadMask(image,q) == 0)
624 q+=GetPixelChannels(image);
627 pixel=GetPixelIntensity(image,q);
628 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
630 PixelChannel channel=GetPixelChannelChannel(image,i);
631 PixelTrait traits=GetPixelChannelTraits(image,channel);
632 if ((traits & UpdatePixelTrait) == 0)
634 if (image->channel_mask != DefaultChannels)
636 if (pixel <= GetPixelInfoChannel(&threshold,channel))
639 q+=GetPixelChannels(image);
641 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
643 if (image->progress_monitor != (MagickProgressMonitor) NULL)
648 #if defined(MAGICKCORE_OPENMP_SUPPORT)
649 #pragma omp critical (MagickCore_BlackThresholdImage)
651 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
653 if (proceed == MagickFalse)
657 image_view=DestroyCacheView(image_view);
662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
666 % C l a m p I m a g e %
670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 % ClampImage() set each pixel whose value is below zero to zero and any the
673 % pixel whose value is above the quantum range to the quantum range (e.g.
674 % 65535) otherwise the pixel value remains unchanged.
676 % The format of the ClampImage method is:
678 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
680 % A description of each parameter follows:
682 % o image: the image.
684 % o exception: return any errors or warnings in this structure.
688 static inline Quantum ClampPixel(const MagickRealType value)
690 #if !defined(MAGICKCORE_HDRI_SUPPORT)
691 return((Quantum) value);
695 if (value >= (MagickRealType) QuantumRange)
696 return((Quantum) QuantumRange);
701 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
703 #define ClampImageTag "Clamp/Image"
717 assert(image != (Image *) NULL);
718 assert(image->signature == MagickSignature);
719 if (image->debug != MagickFalse)
720 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
721 if (image->storage_class == PseudoClass)
730 for (i=0; i < (ssize_t) image->colors; i++)
732 q->red=(double) ClampPixel(q->red);
733 q->green=(double) ClampPixel(q->green);
734 q->blue=(double) ClampPixel(q->blue);
735 q->alpha=(double) ClampPixel(q->alpha);
738 return(SyncImage(image,exception));
745 image_view=AcquireAuthenticCacheView(image,exception);
746 #if defined(MAGICKCORE_OPENMP_SUPPORT)
747 #pragma omp parallel for schedule(static,4) shared(progress,status) \
748 magick_threads(image,image,image->rows,1)
750 for (y=0; y < (ssize_t) image->rows; y++)
758 if (status == MagickFalse)
760 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
761 if (q == (Quantum *) NULL)
766 for (x=0; x < (ssize_t) image->columns; x++)
771 if (GetPixelReadMask(image,q) == 0)
773 q+=GetPixelChannels(image);
776 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
778 PixelChannel channel=GetPixelChannelChannel(image,i);
779 PixelTrait traits=GetPixelChannelTraits(image,channel);
780 if ((traits & UpdatePixelTrait) == 0)
782 q[i]=ClampPixel(q[i]);
784 q+=GetPixelChannels(image);
786 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
788 if (image->progress_monitor != (MagickProgressMonitor) NULL)
793 #if defined(MAGICKCORE_OPENMP_SUPPORT)
794 #pragma omp critical (MagickCore_ClampImage)
796 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
797 if (proceed == MagickFalse)
801 image_view=DestroyCacheView(image_view);
806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
810 % D e s t r o y T h r e s h o l d M a p %
814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816 % DestroyThresholdMap() de-allocate the given ThresholdMap
818 % The format of the ListThresholdMaps method is:
820 % ThresholdMap *DestroyThresholdMap(Threshold *map)
822 % A description of each parameter follows.
824 % o map: Pointer to the Threshold map to destroy
827 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
829 assert(map != (ThresholdMap *) NULL);
830 if (map->map_id != (char *) NULL)
831 map->map_id=DestroyString(map->map_id);
832 if (map->description != (char *) NULL)
833 map->description=DestroyString(map->description);
834 if (map->levels != (ssize_t *) NULL)
835 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
836 map=(ThresholdMap *) RelinquishMagickMemory(map);
841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845 % G e t T h r e s h o l d M a p %
849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 % GetThresholdMap() loads and searches one or more threshold map files for the
852 % map matching the given name or alias.
854 % The format of the GetThresholdMap method is:
856 % ThresholdMap *GetThresholdMap(const char *map_id,
857 % ExceptionInfo *exception)
859 % A description of each parameter follows.
861 % o map_id: ID of the map to look for.
863 % o exception: return any errors or warnings in this structure.
866 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
867 ExceptionInfo *exception)
878 map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
879 if (map != (ThresholdMap *) NULL)
881 options=GetConfigureOptions(ThresholdsFilename,exception);
882 option=(const StringInfo *) GetNextValueInLinkedList(options);
883 while (option != (const StringInfo *) NULL)
885 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
886 GetStringInfoPath(option),map_id,exception);
887 if (map != (ThresholdMap *) NULL)
889 option=(const StringInfo *) GetNextValueInLinkedList(options);
891 options=DestroyConfigureOptions(options);
896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 + G e t T h r e s h o l d M a p F i l e %
904 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
906 % GetThresholdMapFile() look for a given threshold map name or alias in the
907 % given XML file data, and return the allocated the map when found.
909 % The format of the ListThresholdMaps method is:
911 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
912 % const char *map_id,ExceptionInfo *exception)
914 % A description of each parameter follows.
916 % o xml: The threshold map list in XML format.
918 % o filename: The threshold map XML filename.
920 % o map_id: ID of the map to look for in XML list.
922 % o exception: return any errors or warnings in this structure.
925 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
926 const char *map_id,ExceptionInfo *exception)
950 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
951 "Loading threshold map file \"%s\" ...",filename);
952 map=(ThresholdMap *) NULL;
953 thresholds=NewXMLTree(xml,exception);
954 if (thresholds == (XMLTreeInfo *) NULL)
956 for (threshold=GetXMLTreeChild(thresholds,"threshold");
957 threshold != (XMLTreeInfo *) NULL;
958 threshold=GetNextXMLTreeTag(threshold))
960 attribute=GetXMLTreeAttribute(threshold,"map");
961 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
963 attribute=GetXMLTreeAttribute(threshold,"alias");
964 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
967 if (threshold == (XMLTreeInfo *) NULL)
969 thresholds=DestroyXMLTree(thresholds);
972 description=GetXMLTreeChild(threshold,"description");
973 if (description == (XMLTreeInfo *) NULL)
975 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
976 "XmlMissingElement", "<description>, map \"%s\"",map_id);
977 thresholds=DestroyXMLTree(thresholds);
980 levels=GetXMLTreeChild(threshold,"levels");
981 if (levels == (XMLTreeInfo *) NULL)
983 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
984 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
985 thresholds=DestroyXMLTree(thresholds);
988 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
989 if (map == (ThresholdMap *) NULL)
990 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
991 map->map_id=(char *) NULL;
992 map->description=(char *) NULL;
993 map->levels=(ssize_t *) NULL;
994 attribute=GetXMLTreeAttribute(threshold,"map");
995 if (attribute != (char *) NULL)
996 map->map_id=ConstantString(attribute);
997 content=GetXMLTreeContent(description);
998 if (content != (char *) NULL)
999 map->description=ConstantString(content);
1000 attribute=GetXMLTreeAttribute(levels,"width");
1001 if (attribute == (char *) NULL)
1003 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1004 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1005 thresholds=DestroyXMLTree(thresholds);
1006 map=DestroyThresholdMap(map);
1009 map->width=StringToUnsignedLong(attribute);
1010 if (map->width == 0)
1012 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1013 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1014 thresholds=DestroyXMLTree(thresholds);
1015 map=DestroyThresholdMap(map);
1018 attribute=GetXMLTreeAttribute(levels,"height");
1019 if (attribute == (char *) NULL)
1021 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1022 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1023 thresholds=DestroyXMLTree(thresholds);
1024 map=DestroyThresholdMap(map);
1027 map->height=StringToUnsignedLong(attribute);
1028 if (map->height == 0)
1030 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1031 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1032 thresholds=DestroyXMLTree(thresholds);
1033 map=DestroyThresholdMap(map);
1036 attribute=GetXMLTreeAttribute(levels,"divisor");
1037 if (attribute == (char *) NULL)
1039 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1040 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1041 thresholds=DestroyXMLTree(thresholds);
1042 map=DestroyThresholdMap(map);
1045 map->divisor=(ssize_t) StringToLong(attribute);
1046 if (map->divisor < 2)
1048 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1049 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1050 thresholds=DestroyXMLTree(thresholds);
1051 map=DestroyThresholdMap(map);
1054 content=GetXMLTreeContent(levels);
1055 if (content == (char *) NULL)
1057 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1058 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1059 thresholds=DestroyXMLTree(thresholds);
1060 map=DestroyThresholdMap(map);
1063 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1064 sizeof(*map->levels));
1065 if (map->levels == (ssize_t *) NULL)
1066 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1067 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1069 map->levels[i]=(ssize_t) strtol(content,&p,10);
1072 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1073 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1074 thresholds=DestroyXMLTree(thresholds);
1075 map=DestroyThresholdMap(map);
1078 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1080 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1081 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1082 (double) map->levels[i],map_id);
1083 thresholds=DestroyXMLTree(thresholds);
1084 map=DestroyThresholdMap(map);
1089 value=(double) strtol(content,&p,10);
1093 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1094 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1095 thresholds=DestroyXMLTree(thresholds);
1096 map=DestroyThresholdMap(map);
1099 thresholds=DestroyXMLTree(thresholds);
1104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108 + L i s t T h r e s h o l d M a p F i l e %
1112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114 % ListThresholdMapFile() lists the threshold maps and their descriptions
1115 % in the given XML file data.
1117 % The format of the ListThresholdMaps method is:
1119 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1120 % const char *filename,ExceptionInfo *exception)
1122 % A description of each parameter follows.
1124 % o file: An pointer to the output FILE.
1126 % o xml: The threshold map list in XML format.
1128 % o filename: The threshold map XML filename.
1130 % o exception: return any errors or warnings in this structure.
1133 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1134 const char *filename,ExceptionInfo *exception)
1146 assert( xml != (char *)NULL );
1147 assert( file != (FILE *)NULL );
1148 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1149 "Loading threshold map file \"%s\" ...",filename);
1150 thresholds=NewXMLTree(xml,exception);
1151 if ( thresholds == (XMLTreeInfo *)NULL )
1152 return(MagickFalse);
1153 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1154 (void) FormatLocaleFile(file,
1155 "----------------------------------------------------\n");
1156 threshold=GetXMLTreeChild(thresholds,"threshold");
1157 for ( ; threshold != (XMLTreeInfo *) NULL;
1158 threshold=GetNextXMLTreeTag(threshold))
1160 map=GetXMLTreeAttribute(threshold,"map");
1161 if (map == (char *) NULL)
1163 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1164 "XmlMissingAttribute", "<map>");
1165 thresholds=DestroyXMLTree(thresholds);
1166 return(MagickFalse);
1168 alias=GetXMLTreeAttribute(threshold,"alias");
1169 description=GetXMLTreeChild(threshold,"description");
1170 if (description == (XMLTreeInfo *) NULL)
1172 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1173 "XmlMissingElement", "<description>, map \"%s\"",map);
1174 thresholds=DestroyXMLTree(thresholds);
1175 return(MagickFalse);
1177 content=GetXMLTreeContent(description);
1178 if (content == (char *) NULL)
1180 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1181 "XmlMissingContent", "<description>, map \"%s\"", map);
1182 thresholds=DestroyXMLTree(thresholds);
1183 return(MagickFalse);
1185 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1188 thresholds=DestroyXMLTree(thresholds);
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1197 % L i s t T h r e s h o l d M a p s %
1201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1203 % ListThresholdMaps() lists the threshold maps and their descriptions
1204 % as defined by "threshold.xml" to a file.
1206 % The format of the ListThresholdMaps method is:
1208 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1210 % A description of each parameter follows.
1212 % o file: An pointer to the output FILE.
1214 % o exception: return any errors or warnings in this structure.
1217 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1218 ExceptionInfo *exception)
1230 if (file == (FILE *) NULL)
1232 options=GetConfigureOptions(ThresholdsFilename,exception);
1233 (void) FormatLocaleFile(file,
1234 "\n Threshold Maps for Ordered Dither Operations\n");
1235 option=(const StringInfo *) GetNextValueInLinkedList(options);
1236 while (option != (const StringInfo *) NULL)
1238 (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1239 status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1240 GetStringInfoPath(option),exception);
1241 option=(const StringInfo *) GetNextValueInLinkedList(options);
1243 options=DestroyConfigureOptions(options);
1244 return(status != 0 ? MagickTrue : MagickFalse);
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1252 % O r d e r e d P o s t e r i z e I m a g e %
1256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1258 % OrderedPosterizeImage() will perform a ordered dither based on a number
1259 % of pre-defined dithering threshold maps, but over multiple intensity
1260 % levels, which can be different for different channels, according to the
1263 % The format of the OrderedPosterizeImage method is:
1265 % MagickBooleanType OrderedPosterizeImage(Image *image,
1266 % const char *threshold_map,ExceptionInfo *exception)
1268 % A description of each parameter follows:
1270 % o image: the image.
1272 % o threshold_map: A string containing the name of the threshold dither
1273 % map to use, followed by zero or more numbers representing the number
1274 % of color levels tho dither between.
1276 % Any level number less than 2 will be equivalent to 2, and means only
1277 % binary dithering will be applied to each color channel.
1279 % No numbers also means a 2 level (bitmap) dither will be applied to all
1280 % channels, while a single number is the number of levels applied to each
1281 % channel in sequence. More numbers will be applied in turn to each of
1282 % the color channels.
1284 % For example: "o3x3,6" will generate a 6 level posterization of the
1285 % image with a ordered 3x3 diffused pixel dither being applied between
1286 % each level. While checker,8,8,4 will produce a 332 colormaped image
1287 % with only a single checkerboard hash pattern (50% grey) between each
1288 % color level, to basically double the number of color levels with
1289 % a bare minimim of dithering.
1291 % o exception: return any errors or warnings in this structure.
1294 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1295 const char *threshold_map,ExceptionInfo *exception)
1297 #define DitherImageTag "Dither/Image"
1303 token[MaxTextExtent];
1309 levels[CompositePixelChannel];
1326 assert(image != (Image *) NULL);
1327 assert(image->signature == MagickSignature);
1328 if (image->debug != MagickFalse)
1329 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1330 assert(exception != (ExceptionInfo *) NULL);
1331 assert(exception->signature == MagickSignature);
1332 if (threshold_map == (const char *) NULL)
1334 p=(char *) threshold_map;
1335 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1339 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1342 if ((p-threshold_map) >= (MaxTextExtent-1))
1344 token[p-threshold_map]=(*p);
1347 token[p-threshold_map]='\0';
1348 map=GetThresholdMap(token,exception);
1349 if (map == (ThresholdMap *) NULL)
1351 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1352 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1353 return(MagickFalse);
1355 for (i=0; i < MaxPixelChannels; i++)
1357 p=strchr((char *) threshold_map,',');
1358 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1359 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1361 GetMagickToken(p,&p,token);
1363 GetMagickToken(p,&p,token);
1364 levels[i]=StringToDouble(token,(char **) NULL);
1366 for (i=0; i < MaxPixelChannels; i++)
1367 if (fabs(levels[i]) >= 1)
1369 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1370 return(MagickFalse);
1373 image_view=AcquireAuthenticCacheView(image,exception);
1374 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1375 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1376 magick_threads(image,image,image->rows,1)
1378 for (y=0; y < (ssize_t) image->rows; y++)
1386 if (status == MagickFalse)
1388 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1389 if (q == (Quantum *) NULL)
1394 for (x=0; x < (ssize_t) image->columns; x++)
1403 if (GetPixelReadMask(image,q) == 0)
1405 q+=GetPixelChannels(image);
1408 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1414 PixelChannel channel=GetPixelChannelChannel(image,i);
1415 PixelTrait traits=GetPixelChannelTraits(image,channel);
1416 if ((traits & UpdatePixelTrait) == 0)
1418 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_OrderedPosterizeImage)
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 == MagickSignature);
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 (GetPixelReadMask(image,q) == 0)
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 thresholds: a geometry string containing low,high thresholds. If the
1626 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1627 % is performed instead.
1629 % o exception: return any errors or warnings in this structure.
1632 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1633 const char *thresholds,ExceptionInfo *exception)
1635 #define ThresholdImageTag "Threshold/Image"
1660 **restrict random_info;
1665 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1670 assert(image != (Image *) NULL);
1671 assert(image->signature == MagickSignature);
1672 if (image->debug != MagickFalse)
1673 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1674 assert(exception != (ExceptionInfo *) NULL);
1675 assert(exception->signature == MagickSignature);
1676 if (thresholds == (const char *) NULL)
1678 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1679 return(MagickFalse);
1680 GetPixelInfo(image,&threshold);
1682 max_threshold=(double) QuantumRange;
1683 flags=ParseGeometry(thresholds,&geometry_info);
1684 min_threshold=geometry_info.rho;
1685 max_threshold=geometry_info.sigma;
1686 if ((flags & SigmaValue) == 0)
1687 max_threshold=min_threshold;
1688 if (strchr(thresholds,'%') != (char *) NULL)
1690 max_threshold*=(double) (0.01*QuantumRange);
1691 min_threshold*=(double) (0.01*QuantumRange);
1694 Random threshold image.
1698 random_info=AcquireRandomInfoThreadSet();
1699 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1700 key=GetRandomSecretKey(random_info[0]);
1702 image_view=AcquireAuthenticCacheView(image,exception);
1703 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1704 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1705 magick_threads(image,image,image->rows,key == ~0UL)
1707 for (y=0; y < (ssize_t) image->rows; y++)
1710 id = GetOpenMPThreadId();
1718 if (status == MagickFalse)
1720 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1721 if (q == (Quantum *) NULL)
1726 for (x=0; x < (ssize_t) image->columns; x++)
1731 if (GetPixelReadMask(image,q) == 0)
1733 q+=GetPixelChannels(image);
1736 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1741 PixelChannel channel=GetPixelChannelChannel(image,i);
1742 PixelTrait traits=GetPixelChannelTraits(image,channel);
1743 if ((traits & UpdatePixelTrait) == 0)
1745 if ((double) q[i] < min_threshold)
1746 threshold=min_threshold;
1748 if ((double) q[i] > max_threshold)
1749 threshold=max_threshold;
1751 threshold=(double) (QuantumRange*
1752 GetPseudoRandomValue(random_info[id]));
1753 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
1755 q+=GetPixelChannels(image);
1757 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1759 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1764 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1765 #pragma omp critical (MagickCore_RandomThresholdImage)
1767 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1769 if (proceed == MagickFalse)
1773 image_view=DestroyCacheView(image_view);
1774 random_info=DestroyRandomInfoThreadSet(random_info);
1779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1783 % W h i t e T h r e s h o l d I m a g e %
1787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1789 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1790 % the threshold into white while leaving all pixels at or below the threshold
1793 % The format of the WhiteThresholdImage method is:
1795 % MagickBooleanType WhiteThresholdImage(Image *image,
1796 % const char *threshold,ExceptionInfo *exception)
1798 % A description of each parameter follows:
1800 % o image: the image.
1802 % o threshold: Define the threshold value.
1804 % o exception: return any errors or warnings in this structure.
1807 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1808 const char *thresholds,ExceptionInfo *exception)
1810 #define ThresholdImageTag "Threshold/Image"
1833 assert(image != (Image *) NULL);
1834 assert(image->signature == MagickSignature);
1835 if (image->debug != MagickFalse)
1836 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1837 if (thresholds == (const char *) NULL)
1839 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1840 return(MagickFalse);
1841 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1842 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1843 GetPixelInfo(image,&threshold);
1844 flags=ParseGeometry(thresholds,&geometry_info);
1845 threshold.red=geometry_info.rho;
1846 threshold.green=geometry_info.rho;
1847 threshold.blue=geometry_info.rho;
1848 threshold.black=geometry_info.rho;
1849 threshold.alpha=100.0;
1850 if ((flags & SigmaValue) != 0)
1851 threshold.green=geometry_info.sigma;
1852 if ((flags & XiValue) != 0)
1853 threshold.blue=geometry_info.xi;
1854 if ((flags & PsiValue) != 0)
1855 threshold.alpha=geometry_info.psi;
1856 if (threshold.colorspace == CMYKColorspace)
1858 if ((flags & PsiValue) != 0)
1859 threshold.black=geometry_info.psi;
1860 if ((flags & ChiValue) != 0)
1861 threshold.alpha=geometry_info.chi;
1863 if ((flags & PercentValue) != 0)
1865 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1866 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1867 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1868 threshold.black*=(MagickRealType) (QuantumRange/100.0);
1869 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
1872 White threshold image.
1876 image_view=AcquireAuthenticCacheView(image,exception);
1877 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1878 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1879 magick_threads(image,image,image->rows,1)
1881 for (y=0; y < (ssize_t) image->rows; y++)
1889 if (status == MagickFalse)
1891 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1892 if (q == (Quantum *) NULL)
1897 for (x=0; x < (ssize_t) image->columns; x++)
1905 if (GetPixelReadMask(image,q) == 0)
1907 q+=GetPixelChannels(image);
1910 pixel=GetPixelIntensity(image,q);
1911 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1913 PixelChannel channel=GetPixelChannelChannel(image,i);
1914 PixelTrait traits=GetPixelChannelTraits(image,channel);
1915 if ((traits & UpdatePixelTrait) == 0)
1917 if (image->channel_mask != DefaultChannels)
1918 pixel=(double) q[i];
1919 if (pixel > GetPixelInfoChannel(&threshold,channel))
1922 q+=GetPixelChannels(image);
1924 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1926 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1931 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1932 #pragma omp critical (MagickCore_WhiteThresholdImage)
1934 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1936 if (proceed == MagickFalse)
1940 image_view=DestroyCacheView(image_view);