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,
129 % const size_t width,const size_t height,
130 % const ssize_t offset,ExceptionInfo *exception)
132 % A description of each parameter follows:
134 % o image: the image.
136 % o width: the width of the local neighborhood.
138 % o height: the height of the local neighborhood.
140 % o offset: the mean offset.
142 % o exception: return any errors or warnings in this structure.
145 MagickExport Image *AdaptiveThresholdImage(const Image *image,
146 const size_t width,const size_t height,const ssize_t offset,
147 ExceptionInfo *exception)
149 #define ThresholdImageTag "Threshold/Image"
173 assert(image != (const Image *) NULL);
174 assert(image->signature == MagickSignature);
175 if (image->debug != MagickFalse)
176 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
177 assert(exception != (ExceptionInfo *) NULL);
178 assert(exception->signature == MagickSignature);
179 threshold_image=CloneImage(image,0,0,MagickTrue,exception);
180 if (threshold_image == (Image *) NULL)
181 return((Image *) NULL);
182 if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
184 InheritException(exception,&threshold_image->exception);
185 threshold_image=DestroyImage(threshold_image);
186 return((Image *) NULL);
189 Local adaptive threshold.
193 GetPixelInfo(image,&zero);
194 number_pixels=(MagickRealType) 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++)
205 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=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
220 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
225 for (x=0; x < (ssize_t) image->columns; x++)
231 register const Quantum
243 for (v=0; v < (ssize_t) height; v++)
245 for (u=0; u < (ssize_t) width; u++)
247 pixel.red+=GetPixelAlpha(image,r+u*GetPixelChannels(image));
248 pixel.green+=GetPixelGreen(image,r+u*GetPixelChannels(image));
249 pixel.blue+=GetPixelBlue(image,r+u*GetPixelChannels(image));
250 if (image->colorspace == CMYKColorspace)
251 pixel.black+=GetPixelBlack(image,r+u*GetPixelChannels(image));
252 pixel.alpha+=GetPixelAlpha(image,r+u*GetPixelChannels(image));
254 r+=(image->columns+width)*GetPixelChannels(image);
256 mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
257 mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
258 mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
259 mean.black=(MagickRealType) (pixel.black/number_pixels+offset);
260 mean.alpha=(MagickRealType) (pixel.alpha/number_pixels+offset);
261 SetPixelRed(threshold_image,(Quantum) (((MagickRealType)
262 GetPixelRed(threshold_image,q) <= mean.red) ? 0 : QuantumRange),q);
263 SetPixelGreen(threshold_image,(Quantum) (((MagickRealType)
264 GetPixelGreen(threshold_image,q) <= mean.green) ? 0 : QuantumRange),q);
265 SetPixelBlue(threshold_image,(Quantum) (((MagickRealType)
266 GetPixelBlue(threshold_image,q) <= mean.blue) ? 0 : QuantumRange),q);
267 if (image->colorspace == CMYKColorspace)
268 SetPixelBlack(threshold_image,(Quantum) (((MagickRealType)
269 GetPixelBlack(threshold_image,q) <= mean.black) ? 0 : QuantumRange),
271 SetPixelAlpha(threshold_image,(Quantum) (((MagickRealType)
272 GetPixelAlpha(threshold_image,q) <= mean.alpha) ? 0 : QuantumRange),q);
273 p+=GetPixelChannels(image);
274 q+=GetPixelChannels(threshold_image);
276 sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
277 if (sync == MagickFalse)
279 if (image->progress_monitor != (MagickProgressMonitor) NULL)
284 #if defined(MAGICKCORE_OPENMP_SUPPORT)
285 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
287 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
289 if (proceed == MagickFalse)
293 threshold_view=DestroyCacheView(threshold_view);
294 image_view=DestroyCacheView(image_view);
295 if (status == MagickFalse)
296 threshold_image=DestroyImage(threshold_image);
297 return(threshold_image);
301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
305 % B i l e v e l I m a g e %
309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
311 % BilevelImage() changes the value of individual pixels based on the
312 % intensity of each pixel channel. The result is a high-contrast image.
314 % More precisely each channel value of the image is 'thresholded' so that if
315 % it is equal to or less than the given value it is set to zero, while any
316 % value greater than that give is set to it maximum or QuantumRange.
318 % This function is what is used to implement the "-threshold" operator for
319 % the command line API.
321 % If the default channel setting is given the image is thresholded using just
322 % the gray 'intensity' of the image, rather than the individual channels.
324 % The format of the BilevelImageChannel method is:
326 % MagickBooleanType BilevelImage(Image *image,const double threshold)
327 % MagickBooleanType BilevelImageChannel(Image *image,
328 % const ChannelType channel,const double threshold)
330 % A description of each parameter follows:
332 % o image: the image.
334 % o channel: the channel type.
336 % o threshold: define the threshold values.
338 % Aside: You can get the same results as operator using LevelImageChannels()
339 % with the 'threshold' value for both the black_point and the white_point.
343 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
348 status=BilevelImageChannel(image,DefaultChannels,threshold);
352 MagickExport MagickBooleanType BilevelImageChannel(Image *image,
353 const ChannelType channel,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 (channel == DefaultChannels)
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) & ActivePixelTrait) != 0)
419 SetPixelRed(image,(Quantum) ((MagickRealType)
420 GetPixelRed(image,q) <= threshold ? 0 : QuantumRange),q);
421 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
422 SetPixelGreen(image,(Quantum) ((MagickRealType)
423 GetPixelGreen(image,q) <= threshold ? 0 : QuantumRange),q);
424 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
425 SetPixelBlue(image,(Quantum) ((MagickRealType)
426 GetPixelBlue(image,q) <= threshold ? 0 : QuantumRange),q);
427 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
428 (image->colorspace == CMYKColorspace))
429 SetPixelBlack(image,(Quantum) ((MagickRealType)
430 GetPixelBlack(image,q) <= threshold ? 0 : QuantumRange),q);
431 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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_BilevelImageChannel)
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,const char *threshold)
481 % MagickBooleanType BlackThresholdImageChannel(Image *image,
482 % const ChannelType channel,const char *threshold,
483 % ExceptionInfo *exception)
485 % A description of each parameter follows:
487 % o image: the image.
489 % o channel: the channel or channels to be thresholded.
491 % o threshold: Define the threshold value.
493 % o exception: return any errors or warnings in this structure.
496 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
497 const char *threshold)
502 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
507 MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
508 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
510 #define ThresholdImageTag "Threshold/Image"
533 assert(image != (Image *) NULL);
534 assert(image->signature == MagickSignature);
535 if (image->debug != MagickFalse)
536 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
537 if (thresholds == (const char *) NULL)
539 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
541 GetPixelInfo(image,&threshold);
542 flags=ParseGeometry(thresholds,&geometry_info);
543 threshold.red=geometry_info.rho;
544 threshold.green=geometry_info.sigma;
545 if ((flags & SigmaValue) == 0)
546 threshold.green=threshold.red;
547 threshold.blue=geometry_info.xi;
548 if ((flags & XiValue) == 0)
549 threshold.blue=threshold.red;
550 threshold.alpha=geometry_info.psi;
551 if ((flags & PsiValue) == 0)
552 threshold.alpha=threshold.red;
553 threshold.black=geometry_info.chi;
554 if ((flags & ChiValue) == 0)
555 threshold.black=threshold.red;
556 if ((flags & PercentValue) != 0)
558 threshold.red*=(QuantumRange/100.0);
559 threshold.green*=(QuantumRange/100.0);
560 threshold.blue*=(QuantumRange/100.0);
561 threshold.alpha*=(QuantumRange/100.0);
562 threshold.black*=(QuantumRange/100.0);
565 Black threshold image.
569 image_view=AcquireCacheView(image);
570 #if defined(MAGICKCORE_OPENMP_SUPPORT)
571 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
573 for (y=0; y < (ssize_t) image->rows; y++)
581 if (status == MagickFalse)
583 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
584 if (q == (const Quantum *) NULL)
589 for (x=0; x < (ssize_t) image->columns; x++)
591 if (channel != DefaultChannels)
593 if (GetPixelIntensity(image,q) < GetPixelInfoIntensity(&threshold))
595 SetPixelRed(image,0,q);
596 SetPixelGreen(image,0,q);
597 SetPixelBlue(image,0,q);
598 if (image->colorspace == CMYKColorspace)
599 SetPixelBlack(image,0,q);
604 if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
605 ((MagickRealType) GetPixelRed(image,q) < threshold.red))
606 SetPixelRed(image,0,q);
607 if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
608 ((MagickRealType) GetPixelGreen(image,q) < threshold.green))
609 SetPixelGreen(image,0,q);
610 if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
611 ((MagickRealType) GetPixelBlue(image,q) < threshold.blue))
612 SetPixelBlue(image,0,q);
613 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
614 (image->colorspace == CMYKColorspace) &&
615 ((MagickRealType) GetPixelBlack(image,q) < threshold.black))
616 SetPixelBlack(image,0,q);
617 if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
618 ((MagickRealType) GetPixelAlpha(image,q) < threshold.alpha))
619 SetPixelAlpha(image,0,q);
621 q+=GetPixelChannels(image);
623 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
625 if (image->progress_monitor != (MagickProgressMonitor) NULL)
630 #if defined(MAGICKCORE_OPENMP_SUPPORT)
631 #pragma omp critical (MagickCore_BlackThresholdImageChannel)
633 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
635 if (proceed == MagickFalse)
639 image_view=DestroyCacheView(image_view);
644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648 % C l a m p I m a g e %
652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654 % ClampImage() restricts the color range from 0 to the quantum depth.
656 % The format of the ClampImageChannel method is:
658 % MagickBooleanType ClampImage(Image *image)
659 % MagickBooleanType ClampImageChannel(Image *image,
660 % const ChannelType channel)
662 % A description of each parameter follows:
664 % o image: the image.
666 % o channel: the channel type.
670 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
672 #if defined(MAGICKCORE_HDRI_SUPPORT)
675 if (quantum >= QuantumRange)
676 return(QuantumRange);
683 MagickExport MagickBooleanType ClampImage(Image *image)
688 status=ClampImageChannel(image,DefaultChannels);
692 MagickExport MagickBooleanType ClampImageChannel(Image *image,
693 const ChannelType channel)
695 #define ClampImageTag "Clamp/Image"
712 assert(image != (Image *) NULL);
713 assert(image->signature == MagickSignature);
714 if (image->debug != MagickFalse)
715 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
716 if (image->storage_class == PseudoClass)
725 for (i=0; i < (ssize_t) image->colors; i++)
727 q->red=ClampToUnsignedQuantum(q->red);
728 q->green=ClampToUnsignedQuantum(q->green);
729 q->blue=ClampToUnsignedQuantum(q->blue);
730 q->alpha=ClampToUnsignedQuantum(q->alpha);
733 return(SyncImage(image));
740 exception=(&image->exception);
741 image_view=AcquireCacheView(image);
742 #if defined(MAGICKCORE_OPENMP_SUPPORT)
743 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
745 for (y=0; y < (ssize_t) image->rows; y++)
753 if (status == MagickFalse)
755 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
756 if (q == (const Quantum *) NULL)
761 for (x=0; x < (ssize_t) image->columns; x++)
763 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
764 SetPixelRed(image,ClampToUnsignedQuantum(GetPixelRed(image,q)),q);
765 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
766 SetPixelGreen(image,ClampToUnsignedQuantum(GetPixelGreen(image,q)),q);
767 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
768 SetPixelBlue(image,ClampToUnsignedQuantum(GetPixelBlue(image,q)),q);
769 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
770 (image->colorspace == CMYKColorspace))
771 SetPixelBlack(image,ClampToUnsignedQuantum(GetPixelBlack(image,q)),q);
772 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
773 SetPixelAlpha(image,ClampToUnsignedQuantum(GetPixelAlpha(image,q)),q);
774 q+=GetPixelChannels(image);
776 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
778 if (image->progress_monitor != (MagickProgressMonitor) NULL)
783 #if defined(MAGICKCORE_OPENMP_SUPPORT)
784 #pragma omp critical (MagickCore_ClampImageChannel)
786 proceed=SetImageProgress(image,ClampImageTag,progress++,
788 if (proceed == MagickFalse)
792 image_view=DestroyCacheView(image_view);
797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801 % D e s t r o y T h r e s h o l d M a p %
805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807 % DestroyThresholdMap() de-allocate the given ThresholdMap
809 % The format of the ListThresholdMaps method is:
811 % ThresholdMap *DestroyThresholdMap(Threshold *map)
813 % A description of each parameter follows.
815 % o map: Pointer to the Threshold map to destroy
818 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
820 assert(map != (ThresholdMap *) NULL);
821 if (map->map_id != (char *) NULL)
822 map->map_id=DestroyString(map->map_id);
823 if (map->description != (char *) NULL)
824 map->description=DestroyString(map->description);
825 if (map->levels != (ssize_t *) NULL)
826 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
827 map=(ThresholdMap *) RelinquishMagickMemory(map);
832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
836 + G e t T h r e s h o l d M a p F i l e %
840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
842 % GetThresholdMapFile() look for a given threshold map name or alias in the
843 % given XML file data, and return the allocated the map when found.
845 % The format of the ListThresholdMaps method is:
847 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
848 % const char *map_id,ExceptionInfo *exception)
850 % A description of each parameter follows.
852 % o xml: The threshold map list in XML format.
854 % o filename: The threshold map XML filename.
856 % o map_id: ID of the map to look for in XML list.
858 % o exception: return any errors or warnings in this structure.
861 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
862 const char *filename,const char *map_id,ExceptionInfo *exception)
880 map = (ThresholdMap *)NULL;
881 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
882 "Loading threshold map file \"%s\" ...",filename);
883 thresholds=NewXMLTree(xml,exception);
884 if ( thresholds == (XMLTreeInfo *)NULL )
887 for( threshold = GetXMLTreeChild(thresholds,"threshold");
888 threshold != (XMLTreeInfo *)NULL;
889 threshold = GetNextXMLTreeTag(threshold) ) {
890 attr = GetXMLTreeAttribute(threshold, "map");
891 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
893 attr = GetXMLTreeAttribute(threshold, "alias");
894 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
897 if ( threshold == (XMLTreeInfo *)NULL ) {
900 description = GetXMLTreeChild(threshold,"description");
901 if ( description == (XMLTreeInfo *)NULL ) {
902 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
903 "XmlMissingElement", "<description>, map \"%s\"", map_id);
904 thresholds = DestroyXMLTree(thresholds);
907 levels = GetXMLTreeChild(threshold,"levels");
908 if ( levels == (XMLTreeInfo *)NULL ) {
909 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
910 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
911 thresholds = DestroyXMLTree(thresholds);
915 /* The map has been found -- Allocate a Threshold Map to return */
916 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
917 if ( map == (ThresholdMap *)NULL )
918 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
919 map->map_id = (char *)NULL;
920 map->description = (char *)NULL;
921 map->levels = (ssize_t *) NULL;
923 /* Assign Basic Attributes */
924 attr = GetXMLTreeAttribute(threshold, "map");
925 if ( attr != (char *)NULL )
926 map->map_id = ConstantString(attr);
928 content = GetXMLTreeContent(description);
929 if ( content != (char *)NULL )
930 map->description = ConstantString(content);
932 attr = GetXMLTreeAttribute(levels, "width");
933 if ( attr == (char *)NULL ) {
934 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
935 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
936 thresholds = DestroyXMLTree(thresholds);
937 map = DestroyThresholdMap(map);
940 map->width = StringToUnsignedLong(attr);
941 if ( map->width == 0 ) {
942 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
943 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
944 thresholds = DestroyXMLTree(thresholds);
945 map = DestroyThresholdMap(map);
949 attr = GetXMLTreeAttribute(levels, "height");
950 if ( attr == (char *)NULL ) {
951 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
952 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
953 thresholds = DestroyXMLTree(thresholds);
954 map = DestroyThresholdMap(map);
957 map->height = StringToUnsignedLong(attr);
958 if ( map->height == 0 ) {
959 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
960 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
961 thresholds = DestroyXMLTree(thresholds);
962 map = DestroyThresholdMap(map);
966 attr = GetXMLTreeAttribute(levels, "divisor");
967 if ( attr == (char *)NULL ) {
968 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
969 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
970 thresholds = DestroyXMLTree(thresholds);
971 map = DestroyThresholdMap(map);
974 map->divisor = (ssize_t) StringToLong(attr);
975 if ( map->divisor < 2 ) {
976 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
977 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
978 thresholds = DestroyXMLTree(thresholds);
979 map = DestroyThresholdMap(map);
983 /* Allocate theshold levels array */
984 content = GetXMLTreeContent(levels);
985 if ( content == (char *)NULL ) {
986 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
987 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
988 thresholds = DestroyXMLTree(thresholds);
989 map = DestroyThresholdMap(map);
992 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
993 sizeof(*map->levels));
994 if ( map->levels == (ssize_t *)NULL )
995 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
996 { /* parse levels into integer array */
999 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
1000 map->levels[i] = (ssize_t)strtol(content, &p, 10);
1001 if ( p == content ) {
1002 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1003 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1004 thresholds = DestroyXMLTree(thresholds);
1005 map = DestroyThresholdMap(map);
1008 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
1009 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1010 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1011 (double) map->levels[i],map_id);
1012 thresholds = DestroyXMLTree(thresholds);
1013 map = DestroyThresholdMap(map);
1018 value=(double) strtol(content,&p,10);
1022 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1023 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
1024 thresholds=DestroyXMLTree(thresholds);
1025 map=DestroyThresholdMap(map);
1030 thresholds = DestroyXMLTree(thresholds);
1035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1039 % G e t T h r e s h o l d M a p %
1043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1045 % GetThresholdMap() load and search one or more threshold map files for the
1046 % a map matching the given name or aliase.
1048 % The format of the GetThresholdMap method is:
1050 % ThresholdMap *GetThresholdMap(const char *map_id,
1051 % ExceptionInfo *exception)
1053 % A description of each parameter follows.
1055 % o map_id: ID of the map to look for.
1057 % o exception: return any errors or warnings in this structure.
1060 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1061 ExceptionInfo *exception)
1072 map=(ThresholdMap *)NULL;
1073 options=GetConfigureOptions(ThresholdsFilename,exception);
1074 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1075 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1076 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1077 GetStringInfoPath(option),map_id,exception);
1078 options=DestroyConfigureOptions(options);
1083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1087 + L i s t T h r e s h o l d M a p F i l e %
1091 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093 % ListThresholdMapFile() lists the threshold maps and their descriptions
1094 % in the given XML file data.
1096 % The format of the ListThresholdMaps method is:
1098 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1099 % const char *filename,ExceptionInfo *exception)
1101 % A description of each parameter follows.
1103 % o file: An pointer to the output FILE.
1105 % o xml: The threshold map list in XML format.
1107 % o filename: The threshold map XML filename.
1109 % o exception: return any errors or warnings in this structure.
1112 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1113 const char *filename,ExceptionInfo *exception)
1115 XMLTreeInfo *thresholds,*threshold,*description;
1116 const char *map,*alias,*content;
1118 assert( xml != (char *)NULL );
1119 assert( file != (FILE *)NULL );
1121 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1122 "Loading threshold map file \"%s\" ...",filename);
1123 thresholds=NewXMLTree(xml,exception);
1124 if ( thresholds == (XMLTreeInfo *)NULL )
1125 return(MagickFalse);
1127 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1128 (void) FormatLocaleFile(file,
1129 "----------------------------------------------------\n");
1131 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1132 threshold != (XMLTreeInfo *)NULL;
1133 threshold = GetNextXMLTreeTag(threshold) )
1135 map = GetXMLTreeAttribute(threshold, "map");
1136 if (map == (char *) NULL) {
1137 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1138 "XmlMissingAttribute", "<map>");
1139 thresholds=DestroyXMLTree(thresholds);
1140 return(MagickFalse);
1142 alias = GetXMLTreeAttribute(threshold, "alias");
1143 /* alias is optional, no if test needed */
1144 description=GetXMLTreeChild(threshold,"description");
1145 if ( description == (XMLTreeInfo *)NULL ) {
1146 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1147 "XmlMissingElement", "<description>, map \"%s\"", map);
1148 thresholds=DestroyXMLTree(thresholds);
1149 return(MagickFalse);
1151 content=GetXMLTreeContent(description);
1152 if ( content == (char *)NULL ) {
1153 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1154 "XmlMissingContent", "<description>, map \"%s\"", map);
1155 thresholds=DestroyXMLTree(thresholds);
1156 return(MagickFalse);
1158 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1161 thresholds=DestroyXMLTree(thresholds);
1166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1170 % L i s t T h r e s h o l d M a p s %
1174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176 % ListThresholdMaps() lists the threshold maps and their descriptions
1177 % as defined by "threshold.xml" to a file.
1179 % The format of the ListThresholdMaps method is:
1181 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1183 % A description of each parameter follows.
1185 % o file: An pointer to the output FILE.
1187 % o exception: return any errors or warnings in this structure.
1190 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1191 ExceptionInfo *exception)
1203 if ( file == (FILE *)NULL )
1205 options=GetConfigureOptions(ThresholdsFilename,exception);
1207 (void) FormatLocaleFile(file,
1208 "\n Threshold Maps for Ordered Dither Operations\n");
1209 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1210 != (const StringInfo *) NULL)
1212 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1213 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1214 GetStringInfoPath(option),exception);
1216 options=DestroyConfigureOptions(options);
1217 return(status != 0 ? MagickTrue : MagickFalse);
1221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1225 % O r d e r e d P o s t e r i z e I m a g e %
1229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1231 % OrderedPosterizeImage() will perform a ordered dither based on a number
1232 % of pre-defined dithering threshold maps, but over multiple intensity
1233 % levels, which can be different for different channels, according to the
1236 % The format of the OrderedPosterizeImage method is:
1238 % MagickBooleanType OrderedPosterizeImage(Image *image,
1239 % const char *threshold_map,ExceptionInfo *exception)
1240 % MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1241 % const ChannelType channel,const char *threshold_map,
1242 % ExceptionInfo *exception)
1244 % A description of each parameter follows:
1246 % o image: the image.
1248 % o channel: the channel or channels to be thresholded.
1250 % o threshold_map: A string containing the name of the threshold dither
1251 % map to use, followed by zero or more numbers representing the number
1252 % of color levels tho dither between.
1254 % Any level number less than 2 will be equivalent to 2, and means only
1255 % binary dithering will be applied to each color channel.
1257 % No numbers also means a 2 level (bitmap) dither will be applied to all
1258 % channels, while a single number is the number of levels applied to each
1259 % channel in sequence. More numbers will be applied in turn to each of
1260 % the color channels.
1262 % For example: "o3x3,6" will generate a 6 level posterization of the
1263 % image with a ordered 3x3 diffused pixel dither being applied between
1264 % each level. While checker,8,8,4 will produce a 332 colormaped image
1265 % with only a single checkerboard hash pattern (50% grey) between each
1266 % color level, to basically double the number of color levels with
1267 % a bare minimim of dithering.
1269 % o exception: return any errors or warnings in this structure.
1272 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1273 const char *threshold_map,ExceptionInfo *exception)
1278 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1283 MagickExport MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1284 const ChannelType channel,const char *threshold_map,ExceptionInfo *exception)
1286 #define DitherImageTag "Dither/Image"
1306 assert(image != (Image *) NULL);
1307 assert(image->signature == MagickSignature);
1308 if (image->debug != MagickFalse)
1309 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1310 assert(exception != (ExceptionInfo *) NULL);
1311 assert(exception->signature == MagickSignature);
1312 if (threshold_map == (const char *) NULL)
1316 token[MaxTextExtent];
1321 p=(char *)threshold_map;
1322 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1326 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1328 if ((p-threshold_map) >= (MaxTextExtent-1))
1330 token[p-threshold_map] = *p;
1333 token[p-threshold_map] = '\0';
1334 map = GetThresholdMap(token, exception);
1335 if ( map == (ThresholdMap *)NULL ) {
1336 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1337 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1338 return(MagickFalse);
1341 /* Set channel levels from extra comma separated arguments
1342 Default to 2, the single value given, or individual channel values
1345 { /* parse directly as a comma separated list of integers */
1348 p = strchr((char *) threshold_map,',');
1349 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
1350 levels.black = (unsigned int) strtoul(p, &p, 10);
1354 levels.red = ((channel & RedChannel ) != 0) ? levels.black : 0;
1355 levels.green = ((channel & GreenChannel) != 0) ? levels.black : 0;
1356 levels.blue = ((channel & BlueChannel) != 0) ? levels.black : 0;
1357 levels.black = ((channel & BlackChannel) != 0 &&
1358 (image->colorspace == CMYKColorspace)) ? levels.black : 0;
1359 levels.alpha = ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) ? levels.black : 0;
1361 /* if more than a single number, each channel has a separate value */
1362 if ( p != (char *) NULL && *p == ',' ) {
1363 p=strchr((char *) threshold_map,',');
1365 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1366 levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1367 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1368 levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1369 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1370 levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1371 if ((GetPixelBlackTraits(image) & ActivePixelTrait) != 0 &&
1372 (image->colorspace == CMYKColorspace))
1373 levels.black=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1374 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1375 levels.alpha = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1379 /* Parse level values as a geometry */
1381 * How to map GeometryInfo structure elements into
1382 * LongPixelPacket structure elements, but according to channel?
1383 * Note the channels list may skip elements!!!!
1384 * EG -channel BA -ordered-dither map,2,3
1385 * will need to map g.rho -> l.blue, and g.sigma -> l.alpha
1386 * A simpler way is needed, probably converting geometry to a temporary
1387 * array, then using channel to advance the index into ssize_t pixel packet.
1392 printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1393 levels.red, levels.green, levels.blue, levels.alpha, levels.index);
1396 { /* Do the posterized ordered dithering of the image */
1400 /* d = number of psuedo-level divisions added between color levels */
1403 /* reduce levels to levels - 1 */
1404 levels.red = levels.red ? levels.red-1 : 0;
1405 levels.green = levels.green ? levels.green-1 : 0;
1406 levels.blue = levels.blue ? levels.blue-1 : 0;
1407 levels.black = levels.black ? levels.black-1 : 0;
1408 levels.alpha = levels.alpha ? levels.alpha-1 : 0;
1410 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1412 InheritException(exception,&image->exception);
1413 return(MagickFalse);
1417 image_view=AcquireCacheView(image);
1418 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1419 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1421 for (y=0; y < (ssize_t) image->rows; y++)
1429 if (status == MagickFalse)
1431 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1432 if (q == (const Quantum *) NULL)
1437 for (x=0; x < (ssize_t) image->columns; x++)
1445 Figure out the dither threshold for this pixel
1446 This must be a integer from 1 to map->divisor-1
1448 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1450 /* Dither each channel in the image as appropriate
1451 Notes on the integer Math...
1452 total number of divisions = (levels-1)*(divisor-1)+1)
1453 t1 = this colors psuedo_level =
1454 q->red * total_divisions / (QuantumRange+1)
1455 l = posterization level 0..levels
1456 t = dither threshold level 0..divisor-1 NB: 0 only on last
1457 Each color_level is of size QuantumRange / (levels-1)
1458 NB: All input levels and divisor are already had 1 subtracted
1459 Opacity is inverted so 'off' represents transparent.
1461 if (levels.red != 0) {
1462 t = (ssize_t) (QuantumScale*GetPixelRed(image,q)*(levels.red*d+1));
1464 SetPixelRed(image,RoundToQuantum((MagickRealType)
1465 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)),q);
1467 if (levels.green != 0) {
1468 t = (ssize_t) (QuantumScale*GetPixelGreen(image,q)*
1469 (levels.green*d+1));
1471 SetPixelGreen(image,RoundToQuantum((MagickRealType)
1472 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)),q);
1474 if (levels.blue != 0) {
1475 t = (ssize_t) (QuantumScale*GetPixelBlue(image,q)*
1478 SetPixelBlue(image,RoundToQuantum((MagickRealType)
1479 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)),q);
1481 if (levels.alpha != 0) {
1482 t = (ssize_t) ((1.0-QuantumScale*GetPixelAlpha(image,q))*
1483 (levels.alpha*d+1));
1485 SetPixelAlpha(image,RoundToQuantum((MagickRealType)
1486 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
1489 if (levels.black != 0) {
1490 t = (ssize_t) (QuantumScale*GetPixelBlack(image,q)*
1491 (levels.black*d+1));
1493 SetPixelBlack(image,RoundToQuantum((MagickRealType)
1494 ((l+(t>=threshold))*(MagickRealType) QuantumRange/levels.black)),q);
1496 q+=GetPixelChannels(image);
1498 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1500 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1505 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1506 #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
1508 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1509 if (proceed == MagickFalse)
1513 image_view=DestroyCacheView(image_view);
1515 map=DestroyThresholdMap(map);
1520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1524 % R a n d o m T h r e s h o l d I m a g e %
1528 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1530 % RandomThresholdImage() changes the value of individual pixels based on the
1531 % intensity of each pixel compared to a random threshold. The result is a
1532 % low-contrast, two color image.
1534 % The format of the RandomThresholdImage method is:
1536 % MagickBooleanType RandomThresholdImageChannel(Image *image,
1537 % const char *thresholds,ExceptionInfo *exception)
1538 % MagickBooleanType RandomThresholdImageChannel(Image *image,
1539 % const ChannelType channel,const char *thresholds,
1540 % ExceptionInfo *exception)
1542 % A description of each parameter follows:
1544 % o image: the image.
1546 % o channel: the channel or channels to be thresholded.
1548 % o thresholds: a geometry string containing low,high thresholds. If the
1549 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1550 % is performed instead.
1552 % o exception: return any errors or warnings in this structure.
1556 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1557 const char *thresholds,ExceptionInfo *exception)
1562 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
1567 MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
1568 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1570 #define ThresholdImageTag "Threshold/Image"
1595 **restrict random_info;
1600 assert(image != (Image *) NULL);
1601 assert(image->signature == MagickSignature);
1602 if (image->debug != MagickFalse)
1603 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1604 assert(exception != (ExceptionInfo *) NULL);
1605 assert(exception->signature == MagickSignature);
1606 if (thresholds == (const char *) NULL)
1608 GetPixelInfo(image,&threshold);
1610 max_threshold=(MagickRealType) QuantumRange;
1611 flags=ParseGeometry(thresholds,&geometry_info);
1612 min_threshold=geometry_info.rho;
1613 max_threshold=geometry_info.sigma;
1614 if ((flags & SigmaValue) == 0)
1615 max_threshold=min_threshold;
1616 if (strchr(thresholds,'%') != (char *) NULL)
1618 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1619 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1622 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
1623 (min_threshold <= 8))
1626 Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
1628 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
1632 Random threshold image.
1636 if (channel == CompositeChannels)
1638 if (AcquireImageColormap(image,2) == MagickFalse)
1639 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1641 random_info=AcquireRandomInfoThreadSet();
1642 image_view=AcquireCacheView(image);
1643 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1644 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1646 for (y=0; y < (ssize_t) image->rows; y++)
1649 id = GetOpenMPThreadId();
1660 if (status == MagickFalse)
1662 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1664 if (q == (const Quantum *) NULL)
1669 for (x=0; x < (ssize_t) image->columns; x++)
1677 intensity=(MagickRealType) GetPixelIntensity(image,q);
1678 if (intensity < min_threshold)
1679 threshold.black=min_threshold;
1681 if (intensity > max_threshold)
1682 threshold.black=max_threshold;
1684 threshold.black=(MagickRealType)(QuantumRange*
1685 GetPseudoRandomValue(random_info[id]));
1686 index=(Quantum) (intensity <= threshold.black ? 0 : 1);
1687 SetPixelIndex(image,index,q);
1688 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
1689 q+=GetPixelChannels(image);
1691 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1692 if (sync == MagickFalse)
1694 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1699 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1700 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1702 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1704 if (proceed == MagickFalse)
1708 image_view=DestroyCacheView(image_view);
1709 random_info=DestroyRandomInfoThreadSet(random_info);
1712 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1714 InheritException(exception,&image->exception);
1715 return(MagickFalse);
1717 random_info=AcquireRandomInfoThreadSet();
1718 image_view=AcquireCacheView(image);
1719 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1720 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1722 for (y=0; y < (ssize_t) image->rows; y++)
1725 id = GetOpenMPThreadId();
1733 if (status == MagickFalse)
1735 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1736 if (q == (const Quantum *) NULL)
1741 for (x=0; x < (ssize_t) image->columns; x++)
1743 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1745 if ((MagickRealType) GetPixelRed(image,q) < min_threshold)
1746 threshold.red=min_threshold;
1748 if ((MagickRealType) GetPixelRed(image,q) > max_threshold)
1749 threshold.red=max_threshold;
1751 threshold.red=(MagickRealType) (QuantumRange*
1752 GetPseudoRandomValue(random_info[id]));
1754 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1756 if ((MagickRealType) GetPixelGreen(image,q) < min_threshold)
1757 threshold.green=min_threshold;
1759 if ((MagickRealType) GetPixelGreen(image,q) > max_threshold)
1760 threshold.green=max_threshold;
1762 threshold.green=(MagickRealType) (QuantumRange*
1763 GetPseudoRandomValue(random_info[id]));
1765 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1767 if ((MagickRealType) GetPixelBlue(image,q) < min_threshold)
1768 threshold.blue=min_threshold;
1770 if ((MagickRealType) GetPixelBlue(image,q) > max_threshold)
1771 threshold.blue=max_threshold;
1773 threshold.blue=(MagickRealType) (QuantumRange*
1774 GetPseudoRandomValue(random_info[id]));
1776 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1777 (image->colorspace == CMYKColorspace))
1779 if ((MagickRealType) GetPixelBlack(image,q) < min_threshold)
1780 threshold.black=min_threshold;
1782 if ((MagickRealType) GetPixelBlack(image,q) > max_threshold)
1783 threshold.black=max_threshold;
1785 threshold.black=(MagickRealType) (QuantumRange*
1786 GetPseudoRandomValue(random_info[id]));
1788 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1790 if ((MagickRealType) GetPixelAlpha(image,q) < min_threshold)
1791 threshold.alpha=min_threshold;
1793 if ((MagickRealType) GetPixelAlpha(image,q) > max_threshold)
1794 threshold.alpha=max_threshold;
1796 threshold.alpha=(MagickRealType) (QuantumRange*
1797 GetPseudoRandomValue(random_info[id]));
1799 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1800 SetPixelRed(image,(Quantum) ((MagickRealType)
1801 GetPixelRed(image,q) <= threshold.red ? 0 : QuantumRange),q);
1802 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1803 SetPixelGreen(image,(Quantum) ((MagickRealType)
1804 GetPixelGreen(image,q) <= threshold.green ? 0 : QuantumRange),q);
1805 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1806 SetPixelBlue(image,(Quantum) ((MagickRealType)
1807 GetPixelBlue(image,q) <= threshold.blue ? 0 : QuantumRange),q);
1808 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1809 (image->colorspace == CMYKColorspace))
1810 SetPixelBlack(image,(Quantum) ((MagickRealType)
1811 GetPixelBlack(image,q) <= threshold.black ? 0 : QuantumRange),q);
1812 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1813 SetPixelAlpha(image,(Quantum) ((MagickRealType)
1814 GetPixelAlpha(image,q) <= threshold.alpha ? 0 : QuantumRange),q);
1815 q+=GetPixelChannels(image);
1817 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1819 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1824 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1825 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1827 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1829 if (proceed == MagickFalse)
1833 image_view=DestroyCacheView(image_view);
1834 random_info=DestroyRandomInfoThreadSet(random_info);
1839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1843 % W h i t e T h r e s h o l d I m a g e %
1847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1849 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1850 % the threshold into white while leaving all pixels at or below the threshold
1853 % The format of the WhiteThresholdImage method is:
1855 % MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
1856 % MagickBooleanType WhiteThresholdImageChannel(Image *image,
1857 % const ChannelType channel,const char *threshold,
1858 % ExceptionInfo *exception)
1860 % A description of each parameter follows:
1862 % o image: the image.
1864 % o channel: the channel or channels to be thresholded.
1866 % o threshold: Define the threshold value.
1868 % o exception: return any errors or warnings in this structure.
1871 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1872 const char *threshold)
1877 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
1882 MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
1883 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1885 #define ThresholdImageTag "Threshold/Image"
1908 assert(image != (Image *) NULL);
1909 assert(image->signature == MagickSignature);
1910 if (image->debug != MagickFalse)
1911 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1912 if (thresholds == (const char *) NULL)
1914 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1915 return(MagickFalse);
1916 flags=ParseGeometry(thresholds,&geometry_info);
1917 GetPixelInfo(image,&threshold);
1918 threshold.red=geometry_info.rho;
1919 threshold.green=geometry_info.sigma;
1920 if ((flags & SigmaValue) == 0)
1921 threshold.green=threshold.red;
1922 threshold.blue=geometry_info.xi;
1923 if ((flags & XiValue) == 0)
1924 threshold.blue=threshold.red;
1925 threshold.alpha=geometry_info.psi;
1926 if ((flags & PsiValue) == 0)
1927 threshold.alpha=threshold.red;
1928 threshold.black=geometry_info.chi;
1929 if ((flags & ChiValue) == 0)
1930 threshold.black=threshold.red;
1931 if ((flags & PercentValue) != 0)
1933 threshold.red*=(QuantumRange/100.0);
1934 threshold.green*=(QuantumRange/100.0);
1935 threshold.blue*=(QuantumRange/100.0);
1936 threshold.alpha*=(QuantumRange/100.0);
1937 threshold.black*=(QuantumRange/100.0);
1940 White threshold image.
1944 image_view=AcquireCacheView(image);
1945 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1946 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1948 for (y=0; y < (ssize_t) image->rows; y++)
1956 if (status == MagickFalse)
1958 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1959 if (q == (const Quantum *) NULL)
1964 for (x=0; x < (ssize_t) image->columns; x++)
1966 if (channel != DefaultChannels)
1968 if (GetPixelIntensity(image,q) > GetPixelInfoIntensity(&threshold))
1970 SetPixelRed(image,QuantumRange,q);
1971 SetPixelGreen(image,QuantumRange,q);
1972 SetPixelBlue(image,QuantumRange,q);
1973 if (image->colorspace == CMYKColorspace)
1974 SetPixelBlack(image,QuantumRange,q);
1979 if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
1980 ((MagickRealType) GetPixelRed(image,q) > threshold.red))
1981 SetPixelRed(image,QuantumRange,q);
1982 if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
1983 ((MagickRealType) GetPixelGreen(image,q) > threshold.green))
1984 SetPixelGreen(image,QuantumRange,q);
1985 if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
1986 ((MagickRealType) GetPixelBlue(image,q) > threshold.blue))
1987 SetPixelBlue(image,QuantumRange,q);
1988 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1989 (image->colorspace == CMYKColorspace) &&
1990 ((MagickRealType) GetPixelBlack(image,q)) > threshold.black)
1991 SetPixelBlack(image,QuantumRange,q);
1992 if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
1993 ((MagickRealType) GetPixelAlpha(image,q) > threshold.alpha))
1994 SetPixelAlpha(image,QuantumRange,q);
1996 q+=GetPixelChannels(image);
1998 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2000 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2005 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2006 #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
2008 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2010 if (proceed == MagickFalse)
2014 image_view=DestroyCacheView(image_view);