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-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/property.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/configure.h"
52 #include "MagickCore/constitute.h"
53 #include "MagickCore/decorate.h"
54 #include "MagickCore/draw.h"
55 #include "MagickCore/enhance.h"
56 #include "MagickCore/exception.h"
57 #include "MagickCore/exception-private.h"
58 #include "MagickCore/effect.h"
59 #include "MagickCore/fx.h"
60 #include "MagickCore/gem.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/monitor.h"
67 #include "MagickCore/monitor-private.h"
68 #include "MagickCore/montage.h"
69 #include "MagickCore/option.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/quantize.h"
72 #include "MagickCore/quantum.h"
73 #include "MagickCore/random_.h"
74 #include "MagickCore/random-private.h"
75 #include "MagickCore/resize.h"
76 #include "MagickCore/resource_.h"
77 #include "MagickCore/segment.h"
78 #include "MagickCore/shear.h"
79 #include "MagickCore/signature-private.h"
80 #include "MagickCore/string_.h"
81 #include "MagickCore/string-private.h"
82 #include "MagickCore/thread-private.h"
83 #include "MagickCore/threshold.h"
84 #include "MagickCore/transform.h"
85 #include "MagickCore/xml-tree.h"
90 #define ThresholdsFilename "thresholds.xml"
111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115 % A d a p t i v e T h r e s h o l d I m a g e %
119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 % AdaptiveThresholdImage() selects an individual threshold for each pixel
122 % based on the range of intensity values in its local neighborhood. This
123 % allows for thresholding of an image whose global intensity histogram
124 % doesn't contain distinctive peaks.
126 % The format of the AdaptiveThresholdImage method is:
128 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
129 % const size_t height,const double bias,ExceptionInfo *exception)
131 % A description of each parameter follows:
133 % o image: the image.
135 % o width: the width of the local neighborhood.
137 % o height: the height of the local neighborhood.
139 % o bias: the mean bias.
141 % o exception: return any errors or warnings in this structure.
144 MagickExport Image *AdaptiveThresholdImage(const Image *image,
145 const size_t width,const size_t height,const double bias,
146 ExceptionInfo *exception)
148 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
170 Initialize threshold image attributes.
172 assert(image != (Image *) NULL);
173 assert(image->signature == MagickSignature);
174 if (image->debug != MagickFalse)
175 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
176 assert(exception != (ExceptionInfo *) NULL);
177 assert(exception->signature == MagickSignature);
178 if ((width % 2) == 0)
179 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
180 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
182 if (threshold_image == (Image *) NULL)
183 return((Image *) NULL);
184 if (SetImageStorageClass(threshold_image,DirectClass,exception) == MagickFalse)
186 threshold_image=DestroyImage(threshold_image);
187 return((Image *) NULL);
194 number_pixels=(MagickSizeType) width*height;
195 image_view=AcquireCacheView(image);
196 threshold_view=AcquireCacheView(threshold_image);
197 #if defined(MAGICKCORE_OPENMP_SUPPORT)
198 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
200 for (y=0; y < (ssize_t) image->rows; y++)
202 register const Quantum
214 if (status == MagickFalse)
216 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
217 (height/2L),image->columns+width,height,exception);
218 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
220 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
225 center=GetPixelChannels(image)*(image->columns+width)*(height/2L)+
226 GetPixelChannels(image)*(width/2);
227 for (x=0; x < (ssize_t) image->columns; x++)
232 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
245 register const Quantum
254 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
255 if (traits == UndefinedPixelTrait)
257 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
258 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
259 if (threshold_traits == UndefinedPixelTrait)
261 if ((threshold_traits & CopyPixelTrait) != 0)
263 q[channel]=p[center+i];
268 for (v=0; v < (ssize_t) height; v++)
270 for (u=0; u < (ssize_t) width; u++)
273 pixels+=GetPixelChannels(image);
275 pixels+=image->columns*GetPixelChannels(image);
277 mean=pixel/number_pixels+bias;
278 q[channel]=(Quantum) (((MagickRealType) p[center+i] <= mean) ? 0 :
281 p+=GetPixelChannels(image);
282 q+=GetPixelChannels(threshold_image);
284 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
286 if (image->progress_monitor != (MagickProgressMonitor) NULL)
291 #if defined(MAGICKCORE_OPENMP_SUPPORT)
292 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
294 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
296 if (proceed == MagickFalse)
300 threshold_image->type=image->type;
301 threshold_view=DestroyCacheView(threshold_view);
302 image_view=DestroyCacheView(image_view);
303 if (status == MagickFalse)
304 threshold_image=DestroyImage(threshold_image);
305 return(threshold_image);
309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313 % B i l e v e l I m a g e %
317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319 % BilevelImage() changes the value of individual pixels based on the
320 % intensity of each pixel channel. The result is a high-contrast image.
322 % More precisely each channel value of the image is 'thresholded' so that if
323 % it is equal to or less than the given value it is set to zero, while any
324 % value greater than that give is set to it maximum or QuantumRange.
326 % This function is what is used to implement the "-threshold" operator for
327 % the command line API.
329 % If the default channel setting is given the image is thresholded using just
330 % the gray 'intensity' of the image, rather than the individual channels.
332 % The format of the BilevelImage method is:
334 % MagickBooleanType BilevelImage(Image *image,const double threshold)
336 % A description of each parameter follows:
338 % o image: the image.
340 % o threshold: define the threshold values.
342 % Aside: You can get the same results as operator using LevelImages()
343 % with the 'threshold' value for both the black_point and the white_point.
346 MagickExport MagickBooleanType BilevelImage(Image *image,
347 const double threshold)
349 #define ThresholdImageTag "Threshold/Image"
366 assert(image != (Image *) NULL);
367 assert(image->signature == MagickSignature);
368 if (image->debug != MagickFalse)
369 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
370 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
373 Bilevel threshold image.
377 exception=(&image->exception);
378 image_view=AcquireCacheView(image);
379 #if defined(MAGICKCORE_OPENMP_SUPPORT)
380 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
382 for (y=0; y < (ssize_t) image->rows; y++)
390 if (status == MagickFalse)
392 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
393 if (q == (const Quantum *) NULL)
398 if (image->sync != MagickFalse)
400 for (x=0; x < (ssize_t) image->columns; x++)
402 SetPixelRed(image,(Quantum) ((MagickRealType)
403 GetPixelIntensity(image,q) <= threshold ? 0 : QuantumRange),q);
404 SetPixelGreen(image,GetPixelRed(image,q),q);
405 SetPixelBlue(image,GetPixelRed(image,q),q);
406 q+=GetPixelChannels(image);
410 for (x=0; x < (ssize_t) image->columns; x++)
412 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
413 SetPixelRed(image,(Quantum) ((MagickRealType)
414 GetPixelRed(image,q) <= threshold ? 0 : QuantumRange),q);
415 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
416 SetPixelGreen(image,(Quantum) ((MagickRealType)
417 GetPixelGreen(image,q) <= threshold ? 0 : QuantumRange),q);
418 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
419 SetPixelBlue(image,(Quantum) ((MagickRealType)
420 GetPixelBlue(image,q) <= threshold ? 0 : QuantumRange),q);
421 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
422 (image->colorspace == CMYKColorspace))
423 SetPixelBlack(image,(Quantum) ((MagickRealType)
424 GetPixelBlack(image,q) <= threshold ? 0 : QuantumRange),q);
425 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
427 if (image->matte == MagickFalse)
428 SetPixelAlpha(image,(Quantum) ((MagickRealType)
429 GetPixelAlpha(image,q) <= threshold ? 0 : QuantumRange),q);
431 SetPixelAlpha(image,(Quantum) ((MagickRealType)
432 GetPixelAlpha(image,q) >= threshold ? OpaqueAlpha :
433 TransparentAlpha),q);
435 q+=GetPixelChannels(image);
437 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
439 if (image->progress_monitor != (MagickProgressMonitor) NULL)
444 #if defined(MAGICKCORE_OPENMP_SUPPORT)
445 #pragma omp critical (MagickCore_BilevelImage)
447 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
449 if (proceed == MagickFalse)
453 image_view=DestroyCacheView(image_view);
458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
462 % B l a c k T h r e s h o l d I m a g e %
466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
468 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
469 % the threshold into black while leaving all pixels at or above the threshold
472 % The format of the BlackThresholdImage method is:
474 % MagickBooleanType BlackThresholdImage(Image *image,
475 % const char *threshold,ExceptionInfo *exception)
477 % A description of each parameter follows:
479 % o image: the image.
481 % o threshold: Define the threshold value.
483 % o exception: return any errors or warnings in this structure.
486 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
487 const char *thresholds,ExceptionInfo *exception)
489 #define ThresholdImageTag "Threshold/Image"
512 assert(image != (Image *) NULL);
513 assert(image->signature == MagickSignature);
514 if (image->debug != MagickFalse)
515 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
516 if (thresholds == (const char *) NULL)
518 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
520 GetPixelInfo(image,&threshold);
521 flags=ParseGeometry(thresholds,&geometry_info);
522 threshold.red=geometry_info.rho;
523 threshold.green=geometry_info.sigma;
524 if ((flags & SigmaValue) == 0)
525 threshold.green=threshold.red;
526 threshold.blue=geometry_info.xi;
527 if ((flags & XiValue) == 0)
528 threshold.blue=threshold.red;
529 threshold.alpha=geometry_info.psi;
530 if ((flags & PsiValue) == 0)
531 threshold.alpha=threshold.red;
532 threshold.black=geometry_info.chi;
533 if ((flags & ChiValue) == 0)
534 threshold.black=threshold.red;
535 if ((flags & PercentValue) != 0)
537 threshold.red*=(QuantumRange/100.0);
538 threshold.green*=(QuantumRange/100.0);
539 threshold.blue*=(QuantumRange/100.0);
540 threshold.alpha*=(QuantumRange/100.0);
541 threshold.black*=(QuantumRange/100.0);
544 Black threshold image.
548 image_view=AcquireCacheView(image);
549 #if defined(MAGICKCORE_OPENMP_SUPPORT)
550 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
552 for (y=0; y < (ssize_t) image->rows; y++)
560 if (status == MagickFalse)
562 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
563 if (q == (const Quantum *) NULL)
568 for (x=0; x < (ssize_t) image->columns; x++)
570 if (image->sync != MagickFalse)
572 if (GetPixelIntensity(image,q) < GetPixelInfoIntensity(&threshold))
574 SetPixelRed(image,0,q);
575 SetPixelGreen(image,0,q);
576 SetPixelBlue(image,0,q);
577 if (image->colorspace == CMYKColorspace)
578 SetPixelBlack(image,0,q);
583 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
584 ((MagickRealType) GetPixelRed(image,q) < threshold.red))
585 SetPixelRed(image,0,q);
586 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
587 ((MagickRealType) GetPixelGreen(image,q) < threshold.green))
588 SetPixelGreen(image,0,q);
589 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
590 ((MagickRealType) GetPixelBlue(image,q) < threshold.blue))
591 SetPixelBlue(image,0,q);
592 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
593 (image->colorspace == CMYKColorspace) &&
594 ((MagickRealType) GetPixelBlack(image,q) < threshold.black))
595 SetPixelBlack(image,0,q);
596 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
597 ((MagickRealType) GetPixelAlpha(image,q) < threshold.alpha))
598 SetPixelAlpha(image,0,q);
600 q+=GetPixelChannels(image);
602 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
604 if (image->progress_monitor != (MagickProgressMonitor) NULL)
609 #if defined(MAGICKCORE_OPENMP_SUPPORT)
610 #pragma omp critical (MagickCore_BlackThresholdImage)
612 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
614 if (proceed == MagickFalse)
618 image_view=DestroyCacheView(image_view);
623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
627 % C l a m p I m a g e %
631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633 % ClampImage() restricts the color range from 0 to the quantum depth.
635 % The format of the ClampImage method is:
637 % MagickBooleanType ClampImage(Image *image)
639 % A description of each parameter follows:
641 % o image: the image.
645 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
647 #if defined(MAGICKCORE_HDRI_SUPPORT)
650 if (quantum >= QuantumRange)
651 return(QuantumRange);
658 MagickExport MagickBooleanType ClampImage(Image *image)
660 #define ClampImageTag "Clamp/Image"
677 assert(image != (Image *) NULL);
678 assert(image->signature == MagickSignature);
679 if (image->debug != MagickFalse)
680 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
681 if (image->storage_class == PseudoClass)
690 for (i=0; i < (ssize_t) image->colors; i++)
692 q->red=ClampToUnsignedQuantum(q->red);
693 q->green=ClampToUnsignedQuantum(q->green);
694 q->blue=ClampToUnsignedQuantum(q->blue);
695 q->alpha=ClampToUnsignedQuantum(q->alpha);
698 return(SyncImage(image));
705 exception=(&image->exception);
706 image_view=AcquireCacheView(image);
707 #if defined(MAGICKCORE_OPENMP_SUPPORT)
708 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
710 for (y=0; y < (ssize_t) image->rows; y++)
718 if (status == MagickFalse)
720 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
721 if (q == (const Quantum *) NULL)
726 for (x=0; x < (ssize_t) image->columns; x++)
728 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
729 SetPixelRed(image,ClampToUnsignedQuantum(GetPixelRed(image,q)),q);
730 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
731 SetPixelGreen(image,ClampToUnsignedQuantum(GetPixelGreen(image,q)),q);
732 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
733 SetPixelBlue(image,ClampToUnsignedQuantum(GetPixelBlue(image,q)),q);
734 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
735 (image->colorspace == CMYKColorspace))
736 SetPixelBlack(image,ClampToUnsignedQuantum(GetPixelBlack(image,q)),q);
737 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
738 SetPixelAlpha(image,ClampToUnsignedQuantum(GetPixelAlpha(image,q)),q);
739 q+=GetPixelChannels(image);
741 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
743 if (image->progress_monitor != (MagickProgressMonitor) NULL)
748 #if defined(MAGICKCORE_OPENMP_SUPPORT)
749 #pragma omp critical (MagickCore_ClampImage)
751 proceed=SetImageProgress(image,ClampImageTag,progress++,
753 if (proceed == MagickFalse)
757 image_view=DestroyCacheView(image_view);
762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
766 % D e s t r o y T h r e s h o l d M a p %
770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772 % DestroyThresholdMap() de-allocate the given ThresholdMap
774 % The format of the ListThresholdMaps method is:
776 % ThresholdMap *DestroyThresholdMap(Threshold *map)
778 % A description of each parameter follows.
780 % o map: Pointer to the Threshold map to destroy
783 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
785 assert(map != (ThresholdMap *) NULL);
786 if (map->map_id != (char *) NULL)
787 map->map_id=DestroyString(map->map_id);
788 if (map->description != (char *) NULL)
789 map->description=DestroyString(map->description);
790 if (map->levels != (ssize_t *) NULL)
791 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
792 map=(ThresholdMap *) RelinquishMagickMemory(map);
797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801 + G e t T h r e s h o l d M a p F i l e %
805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807 % GetThresholdMapFile() look for a given threshold map name or alias in the
808 % given XML file data, and return the allocated the map when found.
810 % The format of the ListThresholdMaps method is:
812 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
813 % const char *map_id,ExceptionInfo *exception)
815 % A description of each parameter follows.
817 % o xml: The threshold map list in XML format.
819 % o filename: The threshold map XML filename.
821 % o map_id: ID of the map to look for in XML list.
823 % o exception: return any errors or warnings in this structure.
826 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
827 const char *filename,const char *map_id,ExceptionInfo *exception)
845 map = (ThresholdMap *)NULL;
846 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
847 "Loading threshold map file \"%s\" ...",filename);
848 thresholds=NewXMLTree(xml,exception);
849 if ( thresholds == (XMLTreeInfo *)NULL )
852 for( threshold = GetXMLTreeChild(thresholds,"threshold");
853 threshold != (XMLTreeInfo *)NULL;
854 threshold = GetNextXMLTreeTag(threshold) ) {
855 attr = GetXMLTreeAttribute(threshold, "map");
856 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
858 attr = GetXMLTreeAttribute(threshold, "alias");
859 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
862 if ( threshold == (XMLTreeInfo *)NULL ) {
865 description = GetXMLTreeChild(threshold,"description");
866 if ( description == (XMLTreeInfo *)NULL ) {
867 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
868 "XmlMissingElement", "<description>, map \"%s\"", map_id);
869 thresholds = DestroyXMLTree(thresholds);
872 levels = GetXMLTreeChild(threshold,"levels");
873 if ( levels == (XMLTreeInfo *)NULL ) {
874 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
875 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
876 thresholds = DestroyXMLTree(thresholds);
880 /* The map has been found -- Allocate a Threshold Map to return */
881 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
882 if ( map == (ThresholdMap *)NULL )
883 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
884 map->map_id = (char *)NULL;
885 map->description = (char *)NULL;
886 map->levels = (ssize_t *) NULL;
888 /* Assign Basic Attributes */
889 attr = GetXMLTreeAttribute(threshold, "map");
890 if ( attr != (char *)NULL )
891 map->map_id = ConstantString(attr);
893 content = GetXMLTreeContent(description);
894 if ( content != (char *)NULL )
895 map->description = ConstantString(content);
897 attr = GetXMLTreeAttribute(levels, "width");
898 if ( attr == (char *)NULL ) {
899 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
900 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
901 thresholds = DestroyXMLTree(thresholds);
902 map = DestroyThresholdMap(map);
905 map->width = StringToUnsignedLong(attr);
906 if ( map->width == 0 ) {
907 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
908 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
909 thresholds = DestroyXMLTree(thresholds);
910 map = DestroyThresholdMap(map);
914 attr = GetXMLTreeAttribute(levels, "height");
915 if ( attr == (char *)NULL ) {
916 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
917 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
918 thresholds = DestroyXMLTree(thresholds);
919 map = DestroyThresholdMap(map);
922 map->height = StringToUnsignedLong(attr);
923 if ( map->height == 0 ) {
924 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
925 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
926 thresholds = DestroyXMLTree(thresholds);
927 map = DestroyThresholdMap(map);
931 attr = GetXMLTreeAttribute(levels, "divisor");
932 if ( attr == (char *)NULL ) {
933 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
934 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
935 thresholds = DestroyXMLTree(thresholds);
936 map = DestroyThresholdMap(map);
939 map->divisor = (ssize_t) StringToLong(attr);
940 if ( map->divisor < 2 ) {
941 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
942 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
943 thresholds = DestroyXMLTree(thresholds);
944 map = DestroyThresholdMap(map);
948 /* Allocate theshold levels array */
949 content = GetXMLTreeContent(levels);
950 if ( content == (char *)NULL ) {
951 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
952 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
953 thresholds = DestroyXMLTree(thresholds);
954 map = DestroyThresholdMap(map);
957 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
958 sizeof(*map->levels));
959 if ( map->levels == (ssize_t *)NULL )
960 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
961 { /* parse levels into integer array */
964 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
965 map->levels[i] = (ssize_t)strtol(content, &p, 10);
966 if ( p == content ) {
967 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
968 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
969 thresholds = DestroyXMLTree(thresholds);
970 map = DestroyThresholdMap(map);
973 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
974 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
975 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
976 (double) map->levels[i],map_id);
977 thresholds = DestroyXMLTree(thresholds);
978 map = DestroyThresholdMap(map);
983 value=(double) strtol(content,&p,10);
987 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
988 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
989 thresholds=DestroyXMLTree(thresholds);
990 map=DestroyThresholdMap(map);
995 thresholds = DestroyXMLTree(thresholds);
1000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1004 % G e t T h r e s h o l d M a p %
1008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010 % GetThresholdMap() load and search one or more threshold map files for the
1011 % a map matching the given name or aliase.
1013 % The format of the GetThresholdMap method is:
1015 % ThresholdMap *GetThresholdMap(const char *map_id,
1016 % ExceptionInfo *exception)
1018 % A description of each parameter follows.
1020 % o map_id: ID of the map to look for.
1022 % o exception: return any errors or warnings in this structure.
1025 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1026 ExceptionInfo *exception)
1037 map=(ThresholdMap *)NULL;
1038 options=GetConfigureOptions(ThresholdsFilename,exception);
1039 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1040 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1041 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1042 GetStringInfoPath(option),map_id,exception);
1043 options=DestroyConfigureOptions(options);
1048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1052 + L i s t T h r e s h o l d M a p F i l e %
1056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1058 % ListThresholdMapFile() lists the threshold maps and their descriptions
1059 % in the given XML file data.
1061 % The format of the ListThresholdMaps method is:
1063 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1064 % const char *filename,ExceptionInfo *exception)
1066 % A description of each parameter follows.
1068 % o file: An pointer to the output FILE.
1070 % o xml: The threshold map list in XML format.
1072 % o filename: The threshold map XML filename.
1074 % o exception: return any errors or warnings in this structure.
1077 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1078 const char *filename,ExceptionInfo *exception)
1080 XMLTreeInfo *thresholds,*threshold,*description;
1081 const char *map,*alias,*content;
1083 assert( xml != (char *)NULL );
1084 assert( file != (FILE *)NULL );
1086 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1087 "Loading threshold map file \"%s\" ...",filename);
1088 thresholds=NewXMLTree(xml,exception);
1089 if ( thresholds == (XMLTreeInfo *)NULL )
1090 return(MagickFalse);
1092 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1093 (void) FormatLocaleFile(file,
1094 "----------------------------------------------------\n");
1096 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1097 threshold != (XMLTreeInfo *)NULL;
1098 threshold = GetNextXMLTreeTag(threshold) )
1100 map = GetXMLTreeAttribute(threshold, "map");
1101 if (map == (char *) NULL) {
1102 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1103 "XmlMissingAttribute", "<map>");
1104 thresholds=DestroyXMLTree(thresholds);
1105 return(MagickFalse);
1107 alias = GetXMLTreeAttribute(threshold, "alias");
1108 /* alias is optional, no if test needed */
1109 description=GetXMLTreeChild(threshold,"description");
1110 if ( description == (XMLTreeInfo *)NULL ) {
1111 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1112 "XmlMissingElement", "<description>, map \"%s\"", map);
1113 thresholds=DestroyXMLTree(thresholds);
1114 return(MagickFalse);
1116 content=GetXMLTreeContent(description);
1117 if ( content == (char *)NULL ) {
1118 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1119 "XmlMissingContent", "<description>, map \"%s\"", map);
1120 thresholds=DestroyXMLTree(thresholds);
1121 return(MagickFalse);
1123 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1126 thresholds=DestroyXMLTree(thresholds);
1131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1135 % L i s t T h r e s h o l d M a p s %
1139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1141 % ListThresholdMaps() lists the threshold maps and their descriptions
1142 % as defined by "threshold.xml" to a file.
1144 % The format of the ListThresholdMaps method is:
1146 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1148 % A description of each parameter follows.
1150 % o file: An pointer to the output FILE.
1152 % o exception: return any errors or warnings in this structure.
1155 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1156 ExceptionInfo *exception)
1168 if ( file == (FILE *)NULL )
1170 options=GetConfigureOptions(ThresholdsFilename,exception);
1172 (void) FormatLocaleFile(file,
1173 "\n Threshold Maps for Ordered Dither Operations\n");
1174 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1175 != (const StringInfo *) NULL)
1177 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1178 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1179 GetStringInfoPath(option),exception);
1181 options=DestroyConfigureOptions(options);
1182 return(status != 0 ? MagickTrue : MagickFalse);
1186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1190 % O r d e r e d P o s t e r i z e I m a g e %
1194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1196 % OrderedPosterizeImage() will perform a ordered dither based on a number
1197 % of pre-defined dithering threshold maps, but over multiple intensity
1198 % levels, which can be different for different channels, according to the
1201 % The format of the OrderedPosterizeImage method is:
1203 % MagickBooleanType OrderedPosterizeImage(Image *image,
1204 % const char *threshold_map,ExceptionInfo *exception)
1206 % A description of each parameter follows:
1208 % o image: the image.
1210 % o threshold_map: A string containing the name of the threshold dither
1211 % map to use, followed by zero or more numbers representing the number
1212 % of color levels tho dither between.
1214 % Any level number less than 2 will be equivalent to 2, and means only
1215 % binary dithering will be applied to each color channel.
1217 % No numbers also means a 2 level (bitmap) dither will be applied to all
1218 % channels, while a single number is the number of levels applied to each
1219 % channel in sequence. More numbers will be applied in turn to each of
1220 % the color channels.
1222 % For example: "o3x3,6" will generate a 6 level posterization of the
1223 % image with a ordered 3x3 diffused pixel dither being applied between
1224 % each level. While checker,8,8,4 will produce a 332 colormaped image
1225 % with only a single checkerboard hash pattern (50% grey) between each
1226 % color level, to basically double the number of color levels with
1227 % a bare minimim of dithering.
1229 % o exception: return any errors or warnings in this structure.
1232 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1233 const char *threshold_map,ExceptionInfo *exception)
1235 #define DitherImageTag "Dither/Image"
1255 assert(image != (Image *) NULL);
1256 assert(image->signature == MagickSignature);
1257 if (image->debug != MagickFalse)
1258 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1259 assert(exception != (ExceptionInfo *) NULL);
1260 assert(exception->signature == MagickSignature);
1261 if (threshold_map == (const char *) NULL)
1265 token[MaxTextExtent];
1270 p=(char *)threshold_map;
1271 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1275 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1277 if ((p-threshold_map) >= (MaxTextExtent-1))
1279 token[p-threshold_map] = *p;
1282 token[p-threshold_map] = '\0';
1283 map = GetThresholdMap(token, exception);
1284 if ( map == (ThresholdMap *)NULL ) {
1285 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1286 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1287 return(MagickFalse);
1290 /* Set channel levels from extra comma separated arguments
1291 Default to 2, the single value given, or individual channel values
1294 { /* parse directly as a comma separated list of integers */
1297 p = strchr((char *) threshold_map,',');
1303 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
1304 levels.black = (unsigned int) strtoul(p, &p, 10);
1308 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1309 levels.red=levels.black;
1310 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1311 levels.green=levels.black;
1312 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1313 levels.blue=levels.black;
1314 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1315 (image->colorspace == CMYKColorspace))
1316 levels.black=levels.black;
1317 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1318 (image->matte != MagickFalse))
1319 levels.alpha=levels.black;
1321 /* if more than a single number, each channel has a separate value */
1322 if ( p != (char *) NULL && *p == ',' ) {
1323 p=strchr((char *) threshold_map,',');
1325 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1326 levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1327 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1328 levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1329 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1330 levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1331 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
1332 (image->colorspace == CMYKColorspace))
1333 levels.black=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1334 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1335 levels.alpha = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1339 /* Parse level values as a geometry */
1341 * How to map GeometryInfo structure elements into
1342 * LongPixelPacket structure elements, but according to channel?
1343 * Note the channels list may skip elements!!!!
1344 * EG -channel BA -ordered-dither map,2,3
1345 * will need to map g.rho -> l.blue, and g.sigma -> l.alpha
1346 * A simpler way is needed, probably converting geometry to a temporary
1347 * array, then using channel to advance the index into ssize_t pixel packet.
1352 printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1353 levels.red, levels.green, levels.blue, levels.alpha, levels.index);
1356 { /* Do the posterized ordered dithering of the image */
1360 /* d = number of psuedo-level divisions added between color levels */
1363 /* reduce levels to levels - 1 */
1364 levels.red = levels.red ? levels.red-1 : 0;
1365 levels.green = levels.green ? levels.green-1 : 0;
1366 levels.blue = levels.blue ? levels.blue-1 : 0;
1367 levels.black = levels.black ? levels.black-1 : 0;
1368 levels.alpha = levels.alpha ? levels.alpha-1 : 0;
1370 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1371 return(MagickFalse);
1374 image_view=AcquireCacheView(image);
1375 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1376 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
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 == (const Quantum *) NULL)
1394 for (x=0; x < (ssize_t) image->columns; x++)
1402 Figure out the dither threshold for this pixel
1403 This must be a integer from 1 to map->divisor-1
1405 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1407 /* Dither each channel in the image as appropriate
1408 Notes on the integer Math...
1409 total number of divisions = (levels-1)*(divisor-1)+1)
1410 t1 = this colors psuedo_level =
1411 q->red * total_divisions / (QuantumRange+1)
1412 l = posterization level 0..levels
1413 t = dither threshold level 0..divisor-1 NB: 0 only on last
1414 Each color_level is of size QuantumRange / (levels-1)
1415 NB: All input levels and divisor are already had 1 subtracted
1416 Opacity is inverted so 'off' represents transparent.
1418 if (levels.red != 0) {
1419 t = (ssize_t) (QuantumScale*GetPixelRed(image,q)*(levels.red*d+1));
1421 SetPixelRed(image,RoundToQuantum((MagickRealType)
1422 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)),q);
1424 if (levels.green != 0) {
1425 t = (ssize_t) (QuantumScale*GetPixelGreen(image,q)*
1426 (levels.green*d+1));
1428 SetPixelGreen(image,RoundToQuantum((MagickRealType)
1429 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)),q);
1431 if (levels.blue != 0) {
1432 t = (ssize_t) (QuantumScale*GetPixelBlue(image,q)*
1435 SetPixelBlue(image,RoundToQuantum((MagickRealType)
1436 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)),q);
1438 if (levels.alpha != 0) {
1439 t = (ssize_t) ((1.0-QuantumScale*GetPixelAlpha(image,q))*
1440 (levels.alpha*d+1));
1442 SetPixelAlpha(image,RoundToQuantum((MagickRealType)
1443 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
1446 if (levels.black != 0) {
1447 t = (ssize_t) (QuantumScale*GetPixelBlack(image,q)*
1448 (levels.black*d+1));
1450 SetPixelBlack(image,RoundToQuantum((MagickRealType)
1451 ((l+(t>=threshold))*(MagickRealType) QuantumRange/levels.black)),q);
1453 q+=GetPixelChannels(image);
1455 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1457 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1462 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1463 #pragma omp critical (MagickCore_OrderedPosterizeImage)
1465 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1466 if (proceed == MagickFalse)
1470 image_view=DestroyCacheView(image_view);
1472 map=DestroyThresholdMap(map);
1477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1481 % R a n d o m T h r e s h o l d I m a g e %
1485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487 % RandomThresholdImage() changes the value of individual pixels based on the
1488 % intensity of each pixel compared to a random threshold. The result is a
1489 % low-contrast, two color image.
1491 % The format of the RandomThresholdImage method is:
1493 % MagickBooleanType RandomThresholdImage(Image *image,
1494 % const char *thresholds,ExceptionInfo *exception)
1496 % A description of each parameter follows:
1498 % o image: the image.
1500 % o thresholds: a geometry string containing low,high thresholds. If the
1501 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1502 % is performed instead.
1504 % o exception: return any errors or warnings in this structure.
1507 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1508 const char *thresholds,ExceptionInfo *exception)
1510 #define ThresholdImageTag "Threshold/Image"
1535 **restrict random_info;
1540 assert(image != (Image *) NULL);
1541 assert(image->signature == MagickSignature);
1542 if (image->debug != MagickFalse)
1543 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1544 assert(exception != (ExceptionInfo *) NULL);
1545 assert(exception->signature == MagickSignature);
1546 if (thresholds == (const char *) NULL)
1548 GetPixelInfo(image,&threshold);
1550 max_threshold=(MagickRealType) QuantumRange;
1551 flags=ParseGeometry(thresholds,&geometry_info);
1552 min_threshold=geometry_info.rho;
1553 max_threshold=geometry_info.sigma;
1554 if ((flags & SigmaValue) == 0)
1555 max_threshold=min_threshold;
1556 if (strchr(thresholds,'%') != (char *) NULL)
1558 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1559 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1562 Random threshold image.
1566 if (image->sync != MagickFalse)
1568 if (AcquireImageColormap(image,2) == MagickFalse)
1569 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1571 random_info=AcquireRandomInfoThreadSet();
1572 image_view=AcquireCacheView(image);
1573 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1574 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1576 for (y=0; y < (ssize_t) image->rows; y++)
1579 id = GetOpenMPThreadId();
1590 if (status == MagickFalse)
1592 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1594 if (q == (const Quantum *) NULL)
1599 for (x=0; x < (ssize_t) image->columns; x++)
1607 intensity=(MagickRealType) GetPixelIntensity(image,q);
1608 if (intensity < min_threshold)
1609 threshold.black=min_threshold;
1611 if (intensity > max_threshold)
1612 threshold.black=max_threshold;
1614 threshold.black=(MagickRealType)(QuantumRange*
1615 GetPseudoRandomValue(random_info[id]));
1616 index=(Quantum) (intensity <= threshold.black ? 0 : 1);
1617 SetPixelIndex(image,index,q);
1618 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
1619 q+=GetPixelChannels(image);
1621 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1622 if (sync == MagickFalse)
1624 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1629 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1630 #pragma omp critical (MagickCore_RandomThresholdImage)
1632 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1634 if (proceed == MagickFalse)
1638 image_view=DestroyCacheView(image_view);
1639 random_info=DestroyRandomInfoThreadSet(random_info);
1642 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1643 return(MagickFalse);
1644 random_info=AcquireRandomInfoThreadSet();
1645 image_view=AcquireCacheView(image);
1646 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1647 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1649 for (y=0; y < (ssize_t) image->rows; y++)
1652 id = GetOpenMPThreadId();
1660 if (status == MagickFalse)
1662 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1663 if (q == (const Quantum *) NULL)
1668 for (x=0; x < (ssize_t) image->columns; x++)
1670 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1672 if ((MagickRealType) GetPixelRed(image,q) < min_threshold)
1673 threshold.red=min_threshold;
1675 if ((MagickRealType) GetPixelRed(image,q) > max_threshold)
1676 threshold.red=max_threshold;
1678 threshold.red=(MagickRealType) (QuantumRange*
1679 GetPseudoRandomValue(random_info[id]));
1681 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1683 if ((MagickRealType) GetPixelGreen(image,q) < min_threshold)
1684 threshold.green=min_threshold;
1686 if ((MagickRealType) GetPixelGreen(image,q) > max_threshold)
1687 threshold.green=max_threshold;
1689 threshold.green=(MagickRealType) (QuantumRange*
1690 GetPseudoRandomValue(random_info[id]));
1692 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1694 if ((MagickRealType) GetPixelBlue(image,q) < min_threshold)
1695 threshold.blue=min_threshold;
1697 if ((MagickRealType) GetPixelBlue(image,q) > max_threshold)
1698 threshold.blue=max_threshold;
1700 threshold.blue=(MagickRealType) (QuantumRange*
1701 GetPseudoRandomValue(random_info[id]));
1703 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1704 (image->colorspace == CMYKColorspace))
1706 if ((MagickRealType) GetPixelBlack(image,q) < min_threshold)
1707 threshold.black=min_threshold;
1709 if ((MagickRealType) GetPixelBlack(image,q) > max_threshold)
1710 threshold.black=max_threshold;
1712 threshold.black=(MagickRealType) (QuantumRange*
1713 GetPseudoRandomValue(random_info[id]));
1715 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1717 if ((MagickRealType) GetPixelAlpha(image,q) < min_threshold)
1718 threshold.alpha=min_threshold;
1720 if ((MagickRealType) GetPixelAlpha(image,q) > max_threshold)
1721 threshold.alpha=max_threshold;
1723 threshold.alpha=(MagickRealType) (QuantumRange*
1724 GetPseudoRandomValue(random_info[id]));
1726 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1727 SetPixelRed(image,(Quantum) ((MagickRealType)
1728 GetPixelRed(image,q) <= threshold.red ? 0 : QuantumRange),q);
1729 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1730 SetPixelGreen(image,(Quantum) ((MagickRealType)
1731 GetPixelGreen(image,q) <= threshold.green ? 0 : QuantumRange),q);
1732 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1733 SetPixelBlue(image,(Quantum) ((MagickRealType)
1734 GetPixelBlue(image,q) <= threshold.blue ? 0 : QuantumRange),q);
1735 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1736 (image->colorspace == CMYKColorspace))
1737 SetPixelBlack(image,(Quantum) ((MagickRealType)
1738 GetPixelBlack(image,q) <= threshold.black ? 0 : QuantumRange),q);
1739 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1740 SetPixelAlpha(image,(Quantum) ((MagickRealType)
1741 GetPixelAlpha(image,q) <= threshold.alpha ? 0 : QuantumRange),q);
1742 q+=GetPixelChannels(image);
1744 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1746 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1751 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1752 #pragma omp critical (MagickCore_RandomThresholdImage)
1754 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1756 if (proceed == MagickFalse)
1760 image_view=DestroyCacheView(image_view);
1761 random_info=DestroyRandomInfoThreadSet(random_info);
1766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1770 % W h i t e T h r e s h o l d I m a g e %
1774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1776 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1777 % the threshold into white while leaving all pixels at or below the threshold
1780 % The format of the WhiteThresholdImage method is:
1782 % MagickBooleanType WhiteThresholdImage(Image *image,
1783 % const char *threshold,ExceptionInfo *exception)
1785 % A description of each parameter follows:
1787 % o image: the image.
1789 % o threshold: Define the threshold value.
1791 % o exception: return any errors or warnings in this structure.
1794 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1795 const char *thresholds,ExceptionInfo *exception)
1797 #define ThresholdImageTag "Threshold/Image"
1820 assert(image != (Image *) NULL);
1821 assert(image->signature == MagickSignature);
1822 if (image->debug != MagickFalse)
1823 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1824 if (thresholds == (const char *) NULL)
1826 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1827 return(MagickFalse);
1828 flags=ParseGeometry(thresholds,&geometry_info);
1829 GetPixelInfo(image,&threshold);
1830 threshold.red=geometry_info.rho;
1831 threshold.green=geometry_info.sigma;
1832 if ((flags & SigmaValue) == 0)
1833 threshold.green=threshold.red;
1834 threshold.blue=geometry_info.xi;
1835 if ((flags & XiValue) == 0)
1836 threshold.blue=threshold.red;
1837 threshold.alpha=geometry_info.psi;
1838 if ((flags & PsiValue) == 0)
1839 threshold.alpha=threshold.red;
1840 threshold.black=geometry_info.chi;
1841 if ((flags & ChiValue) == 0)
1842 threshold.black=threshold.red;
1843 if ((flags & PercentValue) != 0)
1845 threshold.red*=(QuantumRange/100.0);
1846 threshold.green*=(QuantumRange/100.0);
1847 threshold.blue*=(QuantumRange/100.0);
1848 threshold.alpha*=(QuantumRange/100.0);
1849 threshold.black*=(QuantumRange/100.0);
1852 White threshold image.
1856 image_view=AcquireCacheView(image);
1857 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1858 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1860 for (y=0; y < (ssize_t) image->rows; y++)
1868 if (status == MagickFalse)
1870 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1871 if (q == (const Quantum *) NULL)
1876 for (x=0; x < (ssize_t) image->columns; x++)
1878 if (image->sync != MagickFalse)
1880 if (GetPixelIntensity(image,q) > GetPixelInfoIntensity(&threshold))
1882 SetPixelRed(image,QuantumRange,q);
1883 SetPixelGreen(image,QuantumRange,q);
1884 SetPixelBlue(image,QuantumRange,q);
1885 if (image->colorspace == CMYKColorspace)
1886 SetPixelBlack(image,QuantumRange,q);
1891 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
1892 ((MagickRealType) GetPixelRed(image,q) > threshold.red))
1893 SetPixelRed(image,QuantumRange,q);
1894 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
1895 ((MagickRealType) GetPixelGreen(image,q) > threshold.green))
1896 SetPixelGreen(image,QuantumRange,q);
1897 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
1898 ((MagickRealType) GetPixelBlue(image,q) > threshold.blue))
1899 SetPixelBlue(image,QuantumRange,q);
1900 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1901 (image->colorspace == CMYKColorspace) &&
1902 ((MagickRealType) GetPixelBlack(image,q)) > threshold.black)
1903 SetPixelBlack(image,QuantumRange,q);
1904 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1905 ((MagickRealType) GetPixelAlpha(image,q) > threshold.alpha))
1906 SetPixelAlpha(image,QuantumRange,q);
1908 q+=GetPixelChannels(image);
1910 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1912 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1917 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1918 #pragma omp critical (MagickCore_WhiteThresholdImage)
1920 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1922 if (proceed == MagickFalse)
1926 image_view=DestroyCacheView(image_view);