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) == MagickFalse)
186 InheritException(exception,&threshold_image->exception);
187 threshold_image=DestroyImage(threshold_image);
188 return((Image *) NULL);
195 number_pixels=(MagickSizeType) width*height;
196 image_view=AcquireCacheView(image);
197 threshold_view=AcquireCacheView(threshold_image);
198 #if defined(MAGICKCORE_OPENMP_SUPPORT)
199 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
201 for (y=0; y < (ssize_t) image->rows; y++)
203 register const Quantum
219 if (status == MagickFalse)
221 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
222 (height/2L),image->columns+width,height,exception);
223 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
225 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
230 channels=GetPixelChannels(image);
231 threshold_channels=GetPixelChannels(threshold_image);
232 center=channels*(image->columns+width)*(height/2L)+channels*(width/2);
233 for (x=0; x < (ssize_t) image->columns; x++)
238 for (i=0; i < (ssize_t) channels; i++)
251 register const Quantum
260 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
261 if (traits == UndefinedPixelTrait)
263 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
264 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
265 if (threshold_traits == UndefinedPixelTrait)
267 if ((threshold_traits & CopyPixelTrait) != 0)
269 q[channel]=p[center+i];
274 for (v=0; v < (ssize_t) height; v++)
276 for (u=0; u < (ssize_t) width; u++)
281 pixels+=image->columns*channels;
283 mean=pixel/number_pixels+bias;
284 q[channel]=(Quantum) (((MagickRealType) p[center+i] <= mean) ? 0 :
288 q+=threshold_channels;
290 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
292 if (image->progress_monitor != (MagickProgressMonitor) NULL)
297 #if defined(MAGICKCORE_OPENMP_SUPPORT)
298 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
300 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
302 if (proceed == MagickFalse)
306 threshold_image->type=image->type;
307 threshold_view=DestroyCacheView(threshold_view);
308 image_view=DestroyCacheView(image_view);
309 if (status == MagickFalse)
310 threshold_image=DestroyImage(threshold_image);
311 return(threshold_image);
315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319 % B i l e v e l I m a g e %
323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
325 % BilevelImage() changes the value of individual pixels based on the
326 % intensity of each pixel channel. The result is a high-contrast image.
328 % More precisely each channel value of the image is 'thresholded' so that if
329 % it is equal to or less than the given value it is set to zero, while any
330 % value greater than that give is set to it maximum or QuantumRange.
332 % This function is what is used to implement the "-threshold" operator for
333 % the command line API.
335 % If the default channel setting is given the image is thresholded using just
336 % the gray 'intensity' of the image, rather than the individual channels.
338 % The format of the BilevelImage method is:
340 % MagickBooleanType BilevelImage(Image *image,const double threshold)
342 % A description of each parameter follows:
344 % o image: the image.
346 % o threshold: define the threshold values.
348 % Aside: You can get the same results as operator using LevelImages()
349 % with the 'threshold' value for both the black_point and the white_point.
352 MagickExport MagickBooleanType BilevelImage(Image *image,
353 const double threshold)
355 #define ThresholdImageTag "Threshold/Image"
372 assert(image != (Image *) NULL);
373 assert(image->signature == MagickSignature);
374 if (image->debug != MagickFalse)
375 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
376 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
379 Bilevel threshold image.
383 exception=(&image->exception);
384 image_view=AcquireCacheView(image);
385 #if defined(MAGICKCORE_OPENMP_SUPPORT)
386 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
388 for (y=0; y < (ssize_t) image->rows; y++)
396 if (status == MagickFalse)
398 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
399 if (q == (const Quantum *) NULL)
404 if (image->sync != MagickFalse)
406 for (x=0; x < (ssize_t) image->columns; x++)
408 SetPixelRed(image,(Quantum) ((MagickRealType)
409 GetPixelIntensity(image,q) <= threshold ? 0 : QuantumRange),q);
410 SetPixelGreen(image,GetPixelRed(image,q),q);
411 SetPixelBlue(image,GetPixelRed(image,q),q);
412 q+=GetPixelChannels(image);
416 for (x=0; x < (ssize_t) image->columns; x++)
418 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
419 SetPixelRed(image,(Quantum) ((MagickRealType)
420 GetPixelRed(image,q) <= threshold ? 0 : QuantumRange),q);
421 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
422 SetPixelGreen(image,(Quantum) ((MagickRealType)
423 GetPixelGreen(image,q) <= threshold ? 0 : QuantumRange),q);
424 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
425 SetPixelBlue(image,(Quantum) ((MagickRealType)
426 GetPixelBlue(image,q) <= threshold ? 0 : QuantumRange),q);
427 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
428 (image->colorspace == CMYKColorspace))
429 SetPixelBlack(image,(Quantum) ((MagickRealType)
430 GetPixelBlack(image,q) <= threshold ? 0 : QuantumRange),q);
431 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
433 if (image->matte == MagickFalse)
434 SetPixelAlpha(image,(Quantum) ((MagickRealType)
435 GetPixelAlpha(image,q) <= threshold ? 0 : QuantumRange),q);
437 SetPixelAlpha(image,(Quantum) ((MagickRealType)
438 GetPixelAlpha(image,q) >= threshold ? OpaqueAlpha :
439 TransparentAlpha),q);
441 q+=GetPixelChannels(image);
443 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
445 if (image->progress_monitor != (MagickProgressMonitor) NULL)
450 #if defined(MAGICKCORE_OPENMP_SUPPORT)
451 #pragma omp critical (MagickCore_BilevelImage)
453 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
455 if (proceed == MagickFalse)
459 image_view=DestroyCacheView(image_view);
464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
468 % B l a c k T h r e s h o l d I m a g e %
472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
475 % the threshold into black while leaving all pixels at or above the threshold
478 % The format of the BlackThresholdImage method is:
480 % MagickBooleanType BlackThresholdImage(Image *image,
481 % const char *threshold,ExceptionInfo *exception)
483 % A description of each parameter follows:
485 % o image: the image.
487 % o threshold: Define the threshold value.
489 % o exception: return any errors or warnings in this structure.
492 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
493 const char *thresholds,ExceptionInfo *exception)
495 #define ThresholdImageTag "Threshold/Image"
518 assert(image != (Image *) NULL);
519 assert(image->signature == MagickSignature);
520 if (image->debug != MagickFalse)
521 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
522 if (thresholds == (const char *) NULL)
524 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
526 GetPixelInfo(image,&threshold);
527 flags=ParseGeometry(thresholds,&geometry_info);
528 threshold.red=geometry_info.rho;
529 threshold.green=geometry_info.sigma;
530 if ((flags & SigmaValue) == 0)
531 threshold.green=threshold.red;
532 threshold.blue=geometry_info.xi;
533 if ((flags & XiValue) == 0)
534 threshold.blue=threshold.red;
535 threshold.alpha=geometry_info.psi;
536 if ((flags & PsiValue) == 0)
537 threshold.alpha=threshold.red;
538 threshold.black=geometry_info.chi;
539 if ((flags & ChiValue) == 0)
540 threshold.black=threshold.red;
541 if ((flags & PercentValue) != 0)
543 threshold.red*=(QuantumRange/100.0);
544 threshold.green*=(QuantumRange/100.0);
545 threshold.blue*=(QuantumRange/100.0);
546 threshold.alpha*=(QuantumRange/100.0);
547 threshold.black*=(QuantumRange/100.0);
550 Black threshold image.
554 image_view=AcquireCacheView(image);
555 #if defined(MAGICKCORE_OPENMP_SUPPORT)
556 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
558 for (y=0; y < (ssize_t) image->rows; y++)
566 if (status == MagickFalse)
568 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
569 if (q == (const Quantum *) NULL)
574 for (x=0; x < (ssize_t) image->columns; x++)
576 if (image->sync != MagickFalse)
578 if (GetPixelIntensity(image,q) < GetPixelInfoIntensity(&threshold))
580 SetPixelRed(image,0,q);
581 SetPixelGreen(image,0,q);
582 SetPixelBlue(image,0,q);
583 if (image->colorspace == CMYKColorspace)
584 SetPixelBlack(image,0,q);
589 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
590 ((MagickRealType) GetPixelRed(image,q) < threshold.red))
591 SetPixelRed(image,0,q);
592 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
593 ((MagickRealType) GetPixelGreen(image,q) < threshold.green))
594 SetPixelGreen(image,0,q);
595 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
596 ((MagickRealType) GetPixelBlue(image,q) < threshold.blue))
597 SetPixelBlue(image,0,q);
598 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
599 (image->colorspace == CMYKColorspace) &&
600 ((MagickRealType) GetPixelBlack(image,q) < threshold.black))
601 SetPixelBlack(image,0,q);
602 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
603 ((MagickRealType) GetPixelAlpha(image,q) < threshold.alpha))
604 SetPixelAlpha(image,0,q);
606 q+=GetPixelChannels(image);
608 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
610 if (image->progress_monitor != (MagickProgressMonitor) NULL)
615 #if defined(MAGICKCORE_OPENMP_SUPPORT)
616 #pragma omp critical (MagickCore_BlackThresholdImage)
618 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
620 if (proceed == MagickFalse)
624 image_view=DestroyCacheView(image_view);
629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633 % C l a m p I m a g e %
637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
639 % ClampImage() restricts the color range from 0 to the quantum depth.
641 % The format of the ClampImage method is:
643 % MagickBooleanType ClampImage(Image *image)
645 % A description of each parameter follows:
647 % o image: the image.
651 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
653 #if defined(MAGICKCORE_HDRI_SUPPORT)
656 if (quantum >= QuantumRange)
657 return(QuantumRange);
664 MagickExport MagickBooleanType ClampImage(Image *image)
666 #define ClampImageTag "Clamp/Image"
683 assert(image != (Image *) NULL);
684 assert(image->signature == MagickSignature);
685 if (image->debug != MagickFalse)
686 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
687 if (image->storage_class == PseudoClass)
696 for (i=0; i < (ssize_t) image->colors; i++)
698 q->red=ClampToUnsignedQuantum(q->red);
699 q->green=ClampToUnsignedQuantum(q->green);
700 q->blue=ClampToUnsignedQuantum(q->blue);
701 q->alpha=ClampToUnsignedQuantum(q->alpha);
704 return(SyncImage(image));
711 exception=(&image->exception);
712 image_view=AcquireCacheView(image);
713 #if defined(MAGICKCORE_OPENMP_SUPPORT)
714 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
716 for (y=0; y < (ssize_t) image->rows; y++)
724 if (status == MagickFalse)
726 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
727 if (q == (const Quantum *) NULL)
732 for (x=0; x < (ssize_t) image->columns; x++)
734 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
735 SetPixelRed(image,ClampToUnsignedQuantum(GetPixelRed(image,q)),q);
736 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
737 SetPixelGreen(image,ClampToUnsignedQuantum(GetPixelGreen(image,q)),q);
738 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
739 SetPixelBlue(image,ClampToUnsignedQuantum(GetPixelBlue(image,q)),q);
740 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
741 (image->colorspace == CMYKColorspace))
742 SetPixelBlack(image,ClampToUnsignedQuantum(GetPixelBlack(image,q)),q);
743 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
744 SetPixelAlpha(image,ClampToUnsignedQuantum(GetPixelAlpha(image,q)),q);
745 q+=GetPixelChannels(image);
747 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
749 if (image->progress_monitor != (MagickProgressMonitor) NULL)
754 #if defined(MAGICKCORE_OPENMP_SUPPORT)
755 #pragma omp critical (MagickCore_ClampImage)
757 proceed=SetImageProgress(image,ClampImageTag,progress++,
759 if (proceed == MagickFalse)
763 image_view=DestroyCacheView(image_view);
768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772 % D e s t r o y T h r e s h o l d M a p %
776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
778 % DestroyThresholdMap() de-allocate the given ThresholdMap
780 % The format of the ListThresholdMaps method is:
782 % ThresholdMap *DestroyThresholdMap(Threshold *map)
784 % A description of each parameter follows.
786 % o map: Pointer to the Threshold map to destroy
789 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
791 assert(map != (ThresholdMap *) NULL);
792 if (map->map_id != (char *) NULL)
793 map->map_id=DestroyString(map->map_id);
794 if (map->description != (char *) NULL)
795 map->description=DestroyString(map->description);
796 if (map->levels != (ssize_t *) NULL)
797 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
798 map=(ThresholdMap *) RelinquishMagickMemory(map);
803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807 + G e t T h r e s h o l d M a p F i l e %
811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813 % GetThresholdMapFile() look for a given threshold map name or alias in the
814 % given XML file data, and return the allocated the map when found.
816 % The format of the ListThresholdMaps method is:
818 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
819 % const char *map_id,ExceptionInfo *exception)
821 % A description of each parameter follows.
823 % o xml: The threshold map list in XML format.
825 % o filename: The threshold map XML filename.
827 % o map_id: ID of the map to look for in XML list.
829 % o exception: return any errors or warnings in this structure.
832 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
833 const char *filename,const char *map_id,ExceptionInfo *exception)
851 map = (ThresholdMap *)NULL;
852 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
853 "Loading threshold map file \"%s\" ...",filename);
854 thresholds=NewXMLTree(xml,exception);
855 if ( thresholds == (XMLTreeInfo *)NULL )
858 for( threshold = GetXMLTreeChild(thresholds,"threshold");
859 threshold != (XMLTreeInfo *)NULL;
860 threshold = GetNextXMLTreeTag(threshold) ) {
861 attr = GetXMLTreeAttribute(threshold, "map");
862 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
864 attr = GetXMLTreeAttribute(threshold, "alias");
865 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
868 if ( threshold == (XMLTreeInfo *)NULL ) {
871 description = GetXMLTreeChild(threshold,"description");
872 if ( description == (XMLTreeInfo *)NULL ) {
873 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
874 "XmlMissingElement", "<description>, map \"%s\"", map_id);
875 thresholds = DestroyXMLTree(thresholds);
878 levels = GetXMLTreeChild(threshold,"levels");
879 if ( levels == (XMLTreeInfo *)NULL ) {
880 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
881 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
882 thresholds = DestroyXMLTree(thresholds);
886 /* The map has been found -- Allocate a Threshold Map to return */
887 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
888 if ( map == (ThresholdMap *)NULL )
889 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
890 map->map_id = (char *)NULL;
891 map->description = (char *)NULL;
892 map->levels = (ssize_t *) NULL;
894 /* Assign Basic Attributes */
895 attr = GetXMLTreeAttribute(threshold, "map");
896 if ( attr != (char *)NULL )
897 map->map_id = ConstantString(attr);
899 content = GetXMLTreeContent(description);
900 if ( content != (char *)NULL )
901 map->description = ConstantString(content);
903 attr = GetXMLTreeAttribute(levels, "width");
904 if ( attr == (char *)NULL ) {
905 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
906 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
907 thresholds = DestroyXMLTree(thresholds);
908 map = DestroyThresholdMap(map);
911 map->width = StringToUnsignedLong(attr);
912 if ( map->width == 0 ) {
913 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
914 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
915 thresholds = DestroyXMLTree(thresholds);
916 map = DestroyThresholdMap(map);
920 attr = GetXMLTreeAttribute(levels, "height");
921 if ( attr == (char *)NULL ) {
922 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
923 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
924 thresholds = DestroyXMLTree(thresholds);
925 map = DestroyThresholdMap(map);
928 map->height = StringToUnsignedLong(attr);
929 if ( map->height == 0 ) {
930 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
931 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
932 thresholds = DestroyXMLTree(thresholds);
933 map = DestroyThresholdMap(map);
937 attr = GetXMLTreeAttribute(levels, "divisor");
938 if ( attr == (char *)NULL ) {
939 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
940 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
941 thresholds = DestroyXMLTree(thresholds);
942 map = DestroyThresholdMap(map);
945 map->divisor = (ssize_t) StringToLong(attr);
946 if ( map->divisor < 2 ) {
947 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
948 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
949 thresholds = DestroyXMLTree(thresholds);
950 map = DestroyThresholdMap(map);
954 /* Allocate theshold levels array */
955 content = GetXMLTreeContent(levels);
956 if ( content == (char *)NULL ) {
957 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
958 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
959 thresholds = DestroyXMLTree(thresholds);
960 map = DestroyThresholdMap(map);
963 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
964 sizeof(*map->levels));
965 if ( map->levels == (ssize_t *)NULL )
966 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
967 { /* parse levels into integer array */
970 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
971 map->levels[i] = (ssize_t)strtol(content, &p, 10);
972 if ( p == content ) {
973 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
974 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
975 thresholds = DestroyXMLTree(thresholds);
976 map = DestroyThresholdMap(map);
979 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
980 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
981 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
982 (double) map->levels[i],map_id);
983 thresholds = DestroyXMLTree(thresholds);
984 map = DestroyThresholdMap(map);
989 value=(double) strtol(content,&p,10);
993 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
994 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
995 thresholds=DestroyXMLTree(thresholds);
996 map=DestroyThresholdMap(map);
1001 thresholds = DestroyXMLTree(thresholds);
1006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010 % G e t T h r e s h o l d M a p %
1014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1016 % GetThresholdMap() load and search one or more threshold map files for the
1017 % a map matching the given name or aliase.
1019 % The format of the GetThresholdMap method is:
1021 % ThresholdMap *GetThresholdMap(const char *map_id,
1022 % ExceptionInfo *exception)
1024 % A description of each parameter follows.
1026 % o map_id: ID of the map to look for.
1028 % o exception: return any errors or warnings in this structure.
1031 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1032 ExceptionInfo *exception)
1043 map=(ThresholdMap *)NULL;
1044 options=GetConfigureOptions(ThresholdsFilename,exception);
1045 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1046 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1047 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1048 GetStringInfoPath(option),map_id,exception);
1049 options=DestroyConfigureOptions(options);
1054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1058 + L i s t T h r e s h o l d M a p F i l e %
1062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1064 % ListThresholdMapFile() lists the threshold maps and their descriptions
1065 % in the given XML file data.
1067 % The format of the ListThresholdMaps method is:
1069 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1070 % const char *filename,ExceptionInfo *exception)
1072 % A description of each parameter follows.
1074 % o file: An pointer to the output FILE.
1076 % o xml: The threshold map list in XML format.
1078 % o filename: The threshold map XML filename.
1080 % o exception: return any errors or warnings in this structure.
1083 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1084 const char *filename,ExceptionInfo *exception)
1086 XMLTreeInfo *thresholds,*threshold,*description;
1087 const char *map,*alias,*content;
1089 assert( xml != (char *)NULL );
1090 assert( file != (FILE *)NULL );
1092 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1093 "Loading threshold map file \"%s\" ...",filename);
1094 thresholds=NewXMLTree(xml,exception);
1095 if ( thresholds == (XMLTreeInfo *)NULL )
1096 return(MagickFalse);
1098 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1099 (void) FormatLocaleFile(file,
1100 "----------------------------------------------------\n");
1102 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1103 threshold != (XMLTreeInfo *)NULL;
1104 threshold = GetNextXMLTreeTag(threshold) )
1106 map = GetXMLTreeAttribute(threshold, "map");
1107 if (map == (char *) NULL) {
1108 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1109 "XmlMissingAttribute", "<map>");
1110 thresholds=DestroyXMLTree(thresholds);
1111 return(MagickFalse);
1113 alias = GetXMLTreeAttribute(threshold, "alias");
1114 /* alias is optional, no if test needed */
1115 description=GetXMLTreeChild(threshold,"description");
1116 if ( description == (XMLTreeInfo *)NULL ) {
1117 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1118 "XmlMissingElement", "<description>, map \"%s\"", map);
1119 thresholds=DestroyXMLTree(thresholds);
1120 return(MagickFalse);
1122 content=GetXMLTreeContent(description);
1123 if ( content == (char *)NULL ) {
1124 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1125 "XmlMissingContent", "<description>, map \"%s\"", map);
1126 thresholds=DestroyXMLTree(thresholds);
1127 return(MagickFalse);
1129 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1132 thresholds=DestroyXMLTree(thresholds);
1137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1141 % L i s t T h r e s h o l d M a p s %
1145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1147 % ListThresholdMaps() lists the threshold maps and their descriptions
1148 % as defined by "threshold.xml" to a file.
1150 % The format of the ListThresholdMaps method is:
1152 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1154 % A description of each parameter follows.
1156 % o file: An pointer to the output FILE.
1158 % o exception: return any errors or warnings in this structure.
1161 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1162 ExceptionInfo *exception)
1174 if ( file == (FILE *)NULL )
1176 options=GetConfigureOptions(ThresholdsFilename,exception);
1178 (void) FormatLocaleFile(file,
1179 "\n Threshold Maps for Ordered Dither Operations\n");
1180 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1181 != (const StringInfo *) NULL)
1183 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1184 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1185 GetStringInfoPath(option),exception);
1187 options=DestroyConfigureOptions(options);
1188 return(status != 0 ? MagickTrue : MagickFalse);
1192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1196 % O r d e r e d P o s t e r i z e I m a g e %
1200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202 % OrderedPosterizeImage() will perform a ordered dither based on a number
1203 % of pre-defined dithering threshold maps, but over multiple intensity
1204 % levels, which can be different for different channels, according to the
1207 % The format of the OrderedPosterizeImage method is:
1209 % MagickBooleanType OrderedPosterizeImage(Image *image,
1210 % const char *threshold_map,ExceptionInfo *exception)
1212 % A description of each parameter follows:
1214 % o image: the image.
1216 % o threshold_map: A string containing the name of the threshold dither
1217 % map to use, followed by zero or more numbers representing the number
1218 % of color levels tho dither between.
1220 % Any level number less than 2 will be equivalent to 2, and means only
1221 % binary dithering will be applied to each color channel.
1223 % No numbers also means a 2 level (bitmap) dither will be applied to all
1224 % channels, while a single number is the number of levels applied to each
1225 % channel in sequence. More numbers will be applied in turn to each of
1226 % the color channels.
1228 % For example: "o3x3,6" will generate a 6 level posterization of the
1229 % image with a ordered 3x3 diffused pixel dither being applied between
1230 % each level. While checker,8,8,4 will produce a 332 colormaped image
1231 % with only a single checkerboard hash pattern (50% grey) between each
1232 % color level, to basically double the number of color levels with
1233 % a bare minimim of dithering.
1235 % o exception: return any errors or warnings in this structure.
1238 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1239 const char *threshold_map,ExceptionInfo *exception)
1241 #define DitherImageTag "Dither/Image"
1261 assert(image != (Image *) NULL);
1262 assert(image->signature == MagickSignature);
1263 if (image->debug != MagickFalse)
1264 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1265 assert(exception != (ExceptionInfo *) NULL);
1266 assert(exception->signature == MagickSignature);
1267 if (threshold_map == (const char *) NULL)
1271 token[MaxTextExtent];
1276 p=(char *)threshold_map;
1277 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1281 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1283 if ((p-threshold_map) >= (MaxTextExtent-1))
1285 token[p-threshold_map] = *p;
1288 token[p-threshold_map] = '\0';
1289 map = GetThresholdMap(token, exception);
1290 if ( map == (ThresholdMap *)NULL ) {
1291 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1292 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1293 return(MagickFalse);
1296 /* Set channel levels from extra comma separated arguments
1297 Default to 2, the single value given, or individual channel values
1300 { /* parse directly as a comma separated list of integers */
1303 p = strchr((char *) threshold_map,',');
1309 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
1310 levels.black = (unsigned int) strtoul(p, &p, 10);
1314 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1315 levels.red=levels.black;
1316 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1317 levels.green=levels.black;
1318 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1319 levels.blue=levels.black;
1320 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1321 (image->colorspace == CMYKColorspace))
1322 levels.black=levels.black;
1323 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1324 (image->matte != MagickFalse))
1325 levels.alpha=levels.black;
1327 /* if more than a single number, each channel has a separate value */
1328 if ( p != (char *) NULL && *p == ',' ) {
1329 p=strchr((char *) threshold_map,',');
1331 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1332 levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1333 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1334 levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1335 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1336 levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1337 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
1338 (image->colorspace == CMYKColorspace))
1339 levels.black=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1340 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1341 levels.alpha = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1345 /* Parse level values as a geometry */
1347 * How to map GeometryInfo structure elements into
1348 * LongPixelPacket structure elements, but according to channel?
1349 * Note the channels list may skip elements!!!!
1350 * EG -channel BA -ordered-dither map,2,3
1351 * will need to map g.rho -> l.blue, and g.sigma -> l.alpha
1352 * A simpler way is needed, probably converting geometry to a temporary
1353 * array, then using channel to advance the index into ssize_t pixel packet.
1358 printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1359 levels.red, levels.green, levels.blue, levels.alpha, levels.index);
1362 { /* Do the posterized ordered dithering of the image */
1366 /* d = number of psuedo-level divisions added between color levels */
1369 /* reduce levels to levels - 1 */
1370 levels.red = levels.red ? levels.red-1 : 0;
1371 levels.green = levels.green ? levels.green-1 : 0;
1372 levels.blue = levels.blue ? levels.blue-1 : 0;
1373 levels.black = levels.black ? levels.black-1 : 0;
1374 levels.alpha = levels.alpha ? levels.alpha-1 : 0;
1376 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1378 InheritException(exception,&image->exception);
1379 return(MagickFalse);
1383 image_view=AcquireCacheView(image);
1384 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1385 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1387 for (y=0; y < (ssize_t) image->rows; y++)
1395 if (status == MagickFalse)
1397 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1398 if (q == (const Quantum *) NULL)
1403 for (x=0; x < (ssize_t) image->columns; x++)
1411 Figure out the dither threshold for this pixel
1412 This must be a integer from 1 to map->divisor-1
1414 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1416 /* Dither each channel in the image as appropriate
1417 Notes on the integer Math...
1418 total number of divisions = (levels-1)*(divisor-1)+1)
1419 t1 = this colors psuedo_level =
1420 q->red * total_divisions / (QuantumRange+1)
1421 l = posterization level 0..levels
1422 t = dither threshold level 0..divisor-1 NB: 0 only on last
1423 Each color_level is of size QuantumRange / (levels-1)
1424 NB: All input levels and divisor are already had 1 subtracted
1425 Opacity is inverted so 'off' represents transparent.
1427 if (levels.red != 0) {
1428 t = (ssize_t) (QuantumScale*GetPixelRed(image,q)*(levels.red*d+1));
1430 SetPixelRed(image,RoundToQuantum((MagickRealType)
1431 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)),q);
1433 if (levels.green != 0) {
1434 t = (ssize_t) (QuantumScale*GetPixelGreen(image,q)*
1435 (levels.green*d+1));
1437 SetPixelGreen(image,RoundToQuantum((MagickRealType)
1438 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)),q);
1440 if (levels.blue != 0) {
1441 t = (ssize_t) (QuantumScale*GetPixelBlue(image,q)*
1444 SetPixelBlue(image,RoundToQuantum((MagickRealType)
1445 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)),q);
1447 if (levels.alpha != 0) {
1448 t = (ssize_t) ((1.0-QuantumScale*GetPixelAlpha(image,q))*
1449 (levels.alpha*d+1));
1451 SetPixelAlpha(image,RoundToQuantum((MagickRealType)
1452 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
1455 if (levels.black != 0) {
1456 t = (ssize_t) (QuantumScale*GetPixelBlack(image,q)*
1457 (levels.black*d+1));
1459 SetPixelBlack(image,RoundToQuantum((MagickRealType)
1460 ((l+(t>=threshold))*(MagickRealType) QuantumRange/levels.black)),q);
1462 q+=GetPixelChannels(image);
1464 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1466 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1471 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1472 #pragma omp critical (MagickCore_OrderedPosterizeImage)
1474 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1475 if (proceed == MagickFalse)
1479 image_view=DestroyCacheView(image_view);
1481 map=DestroyThresholdMap(map);
1486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1490 % R a n d o m T h r e s h o l d I m a g e %
1494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1496 % RandomThresholdImage() changes the value of individual pixels based on the
1497 % intensity of each pixel compared to a random threshold. The result is a
1498 % low-contrast, two color image.
1500 % The format of the RandomThresholdImage method is:
1502 % MagickBooleanType RandomThresholdImage(Image *image,
1503 % const char *thresholds,ExceptionInfo *exception)
1505 % A description of each parameter follows:
1507 % o image: the image.
1509 % o thresholds: a geometry string containing low,high thresholds. If the
1510 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1511 % is performed instead.
1513 % o exception: return any errors or warnings in this structure.
1516 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1517 const char *thresholds,ExceptionInfo *exception)
1519 #define ThresholdImageTag "Threshold/Image"
1544 **restrict random_info;
1549 assert(image != (Image *) NULL);
1550 assert(image->signature == MagickSignature);
1551 if (image->debug != MagickFalse)
1552 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1553 assert(exception != (ExceptionInfo *) NULL);
1554 assert(exception->signature == MagickSignature);
1555 if (thresholds == (const char *) NULL)
1557 GetPixelInfo(image,&threshold);
1559 max_threshold=(MagickRealType) QuantumRange;
1560 flags=ParseGeometry(thresholds,&geometry_info);
1561 min_threshold=geometry_info.rho;
1562 max_threshold=geometry_info.sigma;
1563 if ((flags & SigmaValue) == 0)
1564 max_threshold=min_threshold;
1565 if (strchr(thresholds,'%') != (char *) NULL)
1567 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1568 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1571 Random threshold image.
1575 if (image->sync != MagickFalse)
1577 if (AcquireImageColormap(image,2) == MagickFalse)
1578 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1580 random_info=AcquireRandomInfoThreadSet();
1581 image_view=AcquireCacheView(image);
1582 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1583 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1585 for (y=0; y < (ssize_t) image->rows; y++)
1588 id = GetOpenMPThreadId();
1599 if (status == MagickFalse)
1601 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1603 if (q == (const Quantum *) NULL)
1608 for (x=0; x < (ssize_t) image->columns; x++)
1616 intensity=(MagickRealType) GetPixelIntensity(image,q);
1617 if (intensity < min_threshold)
1618 threshold.black=min_threshold;
1620 if (intensity > max_threshold)
1621 threshold.black=max_threshold;
1623 threshold.black=(MagickRealType)(QuantumRange*
1624 GetPseudoRandomValue(random_info[id]));
1625 index=(Quantum) (intensity <= threshold.black ? 0 : 1);
1626 SetPixelIndex(image,index,q);
1627 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
1628 q+=GetPixelChannels(image);
1630 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1631 if (sync == MagickFalse)
1633 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1638 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1639 #pragma omp critical (MagickCore_RandomThresholdImage)
1641 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1643 if (proceed == MagickFalse)
1647 image_view=DestroyCacheView(image_view);
1648 random_info=DestroyRandomInfoThreadSet(random_info);
1651 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1653 InheritException(exception,&image->exception);
1654 return(MagickFalse);
1656 random_info=AcquireRandomInfoThreadSet();
1657 image_view=AcquireCacheView(image);
1658 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1659 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1661 for (y=0; y < (ssize_t) image->rows; y++)
1664 id = GetOpenMPThreadId();
1672 if (status == MagickFalse)
1674 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1675 if (q == (const Quantum *) NULL)
1680 for (x=0; x < (ssize_t) image->columns; x++)
1682 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1684 if ((MagickRealType) GetPixelRed(image,q) < min_threshold)
1685 threshold.red=min_threshold;
1687 if ((MagickRealType) GetPixelRed(image,q) > max_threshold)
1688 threshold.red=max_threshold;
1690 threshold.red=(MagickRealType) (QuantumRange*
1691 GetPseudoRandomValue(random_info[id]));
1693 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1695 if ((MagickRealType) GetPixelGreen(image,q) < min_threshold)
1696 threshold.green=min_threshold;
1698 if ((MagickRealType) GetPixelGreen(image,q) > max_threshold)
1699 threshold.green=max_threshold;
1701 threshold.green=(MagickRealType) (QuantumRange*
1702 GetPseudoRandomValue(random_info[id]));
1704 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1706 if ((MagickRealType) GetPixelBlue(image,q) < min_threshold)
1707 threshold.blue=min_threshold;
1709 if ((MagickRealType) GetPixelBlue(image,q) > max_threshold)
1710 threshold.blue=max_threshold;
1712 threshold.blue=(MagickRealType) (QuantumRange*
1713 GetPseudoRandomValue(random_info[id]));
1715 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1716 (image->colorspace == CMYKColorspace))
1718 if ((MagickRealType) GetPixelBlack(image,q) < min_threshold)
1719 threshold.black=min_threshold;
1721 if ((MagickRealType) GetPixelBlack(image,q) > max_threshold)
1722 threshold.black=max_threshold;
1724 threshold.black=(MagickRealType) (QuantumRange*
1725 GetPseudoRandomValue(random_info[id]));
1727 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1729 if ((MagickRealType) GetPixelAlpha(image,q) < min_threshold)
1730 threshold.alpha=min_threshold;
1732 if ((MagickRealType) GetPixelAlpha(image,q) > max_threshold)
1733 threshold.alpha=max_threshold;
1735 threshold.alpha=(MagickRealType) (QuantumRange*
1736 GetPseudoRandomValue(random_info[id]));
1738 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1739 SetPixelRed(image,(Quantum) ((MagickRealType)
1740 GetPixelRed(image,q) <= threshold.red ? 0 : QuantumRange),q);
1741 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1742 SetPixelGreen(image,(Quantum) ((MagickRealType)
1743 GetPixelGreen(image,q) <= threshold.green ? 0 : QuantumRange),q);
1744 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1745 SetPixelBlue(image,(Quantum) ((MagickRealType)
1746 GetPixelBlue(image,q) <= threshold.blue ? 0 : QuantumRange),q);
1747 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1748 (image->colorspace == CMYKColorspace))
1749 SetPixelBlack(image,(Quantum) ((MagickRealType)
1750 GetPixelBlack(image,q) <= threshold.black ? 0 : QuantumRange),q);
1751 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1752 SetPixelAlpha(image,(Quantum) ((MagickRealType)
1753 GetPixelAlpha(image,q) <= threshold.alpha ? 0 : QuantumRange),q);
1754 q+=GetPixelChannels(image);
1756 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1758 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1763 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1764 #pragma omp critical (MagickCore_RandomThresholdImage)
1766 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1768 if (proceed == MagickFalse)
1772 image_view=DestroyCacheView(image_view);
1773 random_info=DestroyRandomInfoThreadSet(random_info);
1778 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1782 % W h i t e T h r e s h o l d I m a g e %
1786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1788 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1789 % the threshold into white while leaving all pixels at or below the threshold
1792 % The format of the WhiteThresholdImage method is:
1794 % MagickBooleanType WhiteThresholdImage(Image *image,
1795 % const char *threshold,ExceptionInfo *exception)
1797 % A description of each parameter follows:
1799 % o image: the image.
1801 % o threshold: Define the threshold value.
1803 % o exception: return any errors or warnings in this structure.
1806 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1807 const char *thresholds,ExceptionInfo *exception)
1809 #define ThresholdImageTag "Threshold/Image"
1832 assert(image != (Image *) NULL);
1833 assert(image->signature == MagickSignature);
1834 if (image->debug != MagickFalse)
1835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1836 if (thresholds == (const char *) NULL)
1838 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1839 return(MagickFalse);
1840 flags=ParseGeometry(thresholds,&geometry_info);
1841 GetPixelInfo(image,&threshold);
1842 threshold.red=geometry_info.rho;
1843 threshold.green=geometry_info.sigma;
1844 if ((flags & SigmaValue) == 0)
1845 threshold.green=threshold.red;
1846 threshold.blue=geometry_info.xi;
1847 if ((flags & XiValue) == 0)
1848 threshold.blue=threshold.red;
1849 threshold.alpha=geometry_info.psi;
1850 if ((flags & PsiValue) == 0)
1851 threshold.alpha=threshold.red;
1852 threshold.black=geometry_info.chi;
1853 if ((flags & ChiValue) == 0)
1854 threshold.black=threshold.red;
1855 if ((flags & PercentValue) != 0)
1857 threshold.red*=(QuantumRange/100.0);
1858 threshold.green*=(QuantumRange/100.0);
1859 threshold.blue*=(QuantumRange/100.0);
1860 threshold.alpha*=(QuantumRange/100.0);
1861 threshold.black*=(QuantumRange/100.0);
1864 White threshold image.
1868 image_view=AcquireCacheView(image);
1869 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1870 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1872 for (y=0; y < (ssize_t) image->rows; y++)
1880 if (status == MagickFalse)
1882 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1883 if (q == (const Quantum *) NULL)
1888 for (x=0; x < (ssize_t) image->columns; x++)
1890 if (image->sync != MagickFalse)
1892 if (GetPixelIntensity(image,q) > GetPixelInfoIntensity(&threshold))
1894 SetPixelRed(image,QuantumRange,q);
1895 SetPixelGreen(image,QuantumRange,q);
1896 SetPixelBlue(image,QuantumRange,q);
1897 if (image->colorspace == CMYKColorspace)
1898 SetPixelBlack(image,QuantumRange,q);
1903 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
1904 ((MagickRealType) GetPixelRed(image,q) > threshold.red))
1905 SetPixelRed(image,QuantumRange,q);
1906 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
1907 ((MagickRealType) GetPixelGreen(image,q) > threshold.green))
1908 SetPixelGreen(image,QuantumRange,q);
1909 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
1910 ((MagickRealType) GetPixelBlue(image,q) > threshold.blue))
1911 SetPixelBlue(image,QuantumRange,q);
1912 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
1913 (image->colorspace == CMYKColorspace) &&
1914 ((MagickRealType) GetPixelBlack(image,q)) > threshold.black)
1915 SetPixelBlack(image,QuantumRange,q);
1916 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
1917 ((MagickRealType) GetPixelAlpha(image,q) > threshold.alpha))
1918 SetPixelAlpha(image,QuantumRange,q);
1920 q+=GetPixelChannels(image);
1922 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1924 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1929 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1930 #pragma omp critical (MagickCore_WhiteThresholdImage)
1932 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1934 if (proceed == MagickFalse)
1938 image_view=DestroyCacheView(image_view);