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 "magick/studio.h"
44 #include "magick/property.h"
45 #include "magick/blob.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colormap.h"
50 #include "magick/colorspace.h"
51 #include "magick/configure.h"
52 #include "magick/constitute.h"
53 #include "magick/decorate.h"
54 #include "magick/draw.h"
55 #include "magick/enhance.h"
56 #include "magick/exception.h"
57 #include "magick/exception-private.h"
58 #include "magick/effect.h"
59 #include "magick/fx.h"
60 #include "magick/gem.h"
61 #include "magick/geometry.h"
62 #include "magick/image-private.h"
63 #include "magick/list.h"
64 #include "magick/log.h"
65 #include "magick/memory_.h"
66 #include "magick/monitor.h"
67 #include "magick/monitor-private.h"
68 #include "magick/montage.h"
69 #include "magick/option.h"
70 #include "magick/pixel-private.h"
71 #include "magick/quantize.h"
72 #include "magick/quantum.h"
73 #include "magick/random_.h"
74 #include "magick/random-private.h"
75 #include "magick/resize.h"
76 #include "magick/resource_.h"
77 #include "magick/segment.h"
78 #include "magick/shear.h"
79 #include "magick/signature-private.h"
80 #include "magick/string_.h"
81 #include "magick/string-private.h"
82 #include "magick/thread-private.h"
83 #include "magick/threshold.h"
84 #include "magick/transform.h"
85 #include "magick/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 GetMagickPixelPacket(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 IndexPacket
208 register const PixelPacket
212 *restrict threshold_indexes;
220 if (status == MagickFalse)
222 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
223 height/2L,image->columns+width,height,exception);
224 q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
226 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
231 indexes=GetCacheViewVirtualIndexQueue(image_view);
232 threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
233 for (x=0; x < (ssize_t) image->columns; x++)
239 register const PixelPacket
251 for (v=0; v < (ssize_t) height; v++)
253 for (u=0; u < (ssize_t) width; u++)
256 pixel.green+=r[u].green;
257 pixel.blue+=r[u].blue;
258 pixel.opacity+=r[u].opacity;
259 if (image->colorspace == CMYKColorspace)
260 pixel.index=(MagickRealType) GetIndexPixelComponent(
263 r+=image->columns+width;
265 mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
266 mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
267 mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
268 mean.opacity=(MagickRealType) (pixel.opacity/number_pixels+offset);
269 if (image->colorspace == CMYKColorspace)
270 mean.index=(MagickRealType) (pixel.index/number_pixels+offset);
271 SetRedPixelComponent(q,((MagickRealType) GetRedPixelComponent(q) <=
272 mean.red) ? 0 : QuantumRange);
273 SetGreenPixelComponent(q,((MagickRealType) GetGreenPixelComponent(q) <=
274 mean.green) ? 0 : QuantumRange);
275 SetBluePixelComponent(q,((MagickRealType) GetBluePixelComponent(q) <=
276 mean.blue) ? 0 : QuantumRange);
277 SetOpacityPixelComponent(q,((MagickRealType) GetOpacityPixelComponent(q)
278 <= mean.opacity) ? 0 : QuantumRange);
279 if (image->colorspace == CMYKColorspace)
280 SetIndexPixelComponent(threshold_indexes+x,(((MagickRealType)
281 GetIndexPixelComponent(threshold_indexes+x) <= mean.index) ? 0 :
286 sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
287 if (sync == MagickFalse)
289 if (image->progress_monitor != (MagickProgressMonitor) NULL)
294 #if defined(MAGICKCORE_OPENMP_SUPPORT)
295 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
297 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
299 if (proceed == MagickFalse)
303 threshold_view=DestroyCacheView(threshold_view);
304 image_view=DestroyCacheView(image_view);
305 if (status == MagickFalse)
306 threshold_image=DestroyImage(threshold_image);
307 return(threshold_image);
311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315 % B i l e v e l I m a g e %
319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
321 % BilevelImage() changes the value of individual pixels based on the
322 % intensity of each pixel channel. The result is a high-contrast image.
324 % More precisely each channel value of the image is 'thresholded' so that if
325 % it is equal to or less than the given value it is set to zero, while any
326 % value greater than that give is set to it maximum or QuantumRange.
328 % This function is what is used to implement the "-threshold" operator for
329 % the command line API.
331 % If the default channel setting is given the image is thresholded using just
332 % the gray 'intensity' of the image, rather than the individual channels.
334 % The format of the BilevelImageChannel method is:
336 % MagickBooleanType BilevelImage(Image *image,const double threshold)
337 % MagickBooleanType BilevelImageChannel(Image *image,
338 % const ChannelType channel,const double threshold)
340 % A description of each parameter follows:
342 % o image: the image.
344 % o channel: the channel type.
346 % o threshold: define the threshold values.
348 % Aside: You can get the same results as operator using LevelImageChannels()
349 % with the 'threshold' value for both the black_point and the white_point.
353 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
358 status=BilevelImageChannel(image,DefaultChannels,threshold);
362 MagickExport MagickBooleanType BilevelImageChannel(Image *image,
363 const ChannelType channel,const double threshold)
365 #define ThresholdImageTag "Threshold/Image"
382 assert(image != (Image *) NULL);
383 assert(image->signature == MagickSignature);
384 if (image->debug != MagickFalse)
385 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
386 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
389 Bilevel threshold image.
393 exception=(&image->exception);
394 image_view=AcquireCacheView(image);
395 #if defined(MAGICKCORE_OPENMP_SUPPORT)
396 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
398 for (y=0; y < (ssize_t) image->rows; y++)
409 if (status == MagickFalse)
411 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
412 if (q == (PixelPacket *) NULL)
417 indexes=GetCacheViewAuthenticIndexQueue(image_view);
418 if (channel == DefaultChannels)
420 for (x=0; x < (ssize_t) image->columns; x++)
422 SetRedPixelComponent(q,(MagickRealType) PixelIntensityToQuantum(q) <=
423 threshold ? 0 : QuantumRange);
424 SetGreenPixelComponent(q,GetRedPixelComponent(q));
425 SetBluePixelComponent(q,GetRedPixelComponent(q));
430 for (x=0; x < (ssize_t) image->columns; x++)
432 if ((channel & RedChannel) != 0)
433 SetRedPixelComponent(q,(MagickRealType) GetRedPixelComponent(q) <=
434 threshold ? 0 : QuantumRange);
435 if ((channel & GreenChannel) != 0)
436 SetGreenPixelComponent(q,(MagickRealType) GetGreenPixelComponent(q) <=
437 threshold ? 0 : QuantumRange);
438 if ((channel & BlueChannel) != 0)
439 SetBluePixelComponent(q,(MagickRealType) GetBluePixelComponent(q) <=
440 threshold ? 0 : QuantumRange);
441 if ((channel & OpacityChannel) != 0)
443 if (image->matte == MagickFalse)
444 SetOpacityPixelComponent(q,(MagickRealType)
445 GetOpacityPixelComponent(q) <= threshold ? 0 : QuantumRange);
447 SetOpacityPixelComponent(q,(MagickRealType)
448 GetOpacityPixelComponent(q) <= threshold ? OpaqueOpacity :
451 if (((channel & IndexChannel) != 0) &&
452 (image->colorspace == CMYKColorspace))
453 SetIndexPixelComponent(indexes+x,(MagickRealType)
454 GetIndexPixelComponent(indexes+x) <= threshold ? 0 : QuantumRange);
457 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
459 if (image->progress_monitor != (MagickProgressMonitor) NULL)
464 #if defined(MAGICKCORE_OPENMP_SUPPORT)
465 #pragma omp critical (MagickCore_BilevelImageChannel)
467 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
469 if (proceed == MagickFalse)
473 image_view=DestroyCacheView(image_view);
478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482 % B l a c k T h r e s h o l d I m a g e %
486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
489 % the threshold into black while leaving all pixels at or above the threshold
492 % The format of the BlackThresholdImage method is:
494 % MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
495 % MagickBooleanType BlackThresholdImageChannel(Image *image,
496 % const ChannelType channel,const char *threshold,
497 % ExceptionInfo *exception)
499 % A description of each parameter follows:
501 % o image: the image.
503 % o channel: the channel or channels to be thresholded.
505 % o threshold: Define the threshold value.
507 % o exception: return any errors or warnings in this structure.
510 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
511 const char *threshold)
516 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
521 MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
522 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
524 #define ThresholdImageTag "Threshold/Image"
547 assert(image != (Image *) NULL);
548 assert(image->signature == MagickSignature);
549 if (image->debug != MagickFalse)
550 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
551 if (thresholds == (const char *) NULL)
553 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
555 GetMagickPixelPacket(image,&threshold);
556 flags=ParseGeometry(thresholds,&geometry_info);
557 threshold.red=geometry_info.rho;
558 threshold.green=geometry_info.sigma;
559 if ((flags & SigmaValue) == 0)
560 threshold.green=threshold.red;
561 threshold.blue=geometry_info.xi;
562 if ((flags & XiValue) == 0)
563 threshold.blue=threshold.red;
564 threshold.opacity=geometry_info.psi;
565 if ((flags & PsiValue) == 0)
566 threshold.opacity=threshold.red;
567 threshold.index=geometry_info.chi;
568 if ((flags & ChiValue) == 0)
569 threshold.index=threshold.red;
570 if ((flags & PercentValue) != 0)
572 threshold.red*=(QuantumRange/100.0);
573 threshold.green*=(QuantumRange/100.0);
574 threshold.blue*=(QuantumRange/100.0);
575 threshold.opacity*=(QuantumRange/100.0);
576 threshold.index*=(QuantumRange/100.0);
579 Black threshold image.
583 image_view=AcquireCacheView(image);
584 #if defined(MAGICKCORE_OPENMP_SUPPORT)
585 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
587 for (y=0; y < (ssize_t) image->rows; y++)
598 if (status == MagickFalse)
600 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
601 if (q == (PixelPacket *) NULL)
606 indexes=GetCacheViewAuthenticIndexQueue(image_view);
607 for (x=0; x < (ssize_t) image->columns; x++)
609 if (channel != DefaultChannels)
611 if (PixelIntensity(q) < MagickPixelIntensity(&threshold))
613 SetRedPixelComponent(q,0);
614 SetGreenPixelComponent(q,0);
615 SetBluePixelComponent(q,0);
616 if (image->colorspace == CMYKColorspace)
617 SetIndexPixelComponent(indexes+x,0);
622 if (((channel & RedChannel) != 0) &&
623 ((MagickRealType) GetRedPixelComponent(q) < threshold.red))
624 SetRedPixelComponent(q,0);
625 if (((channel & GreenChannel) != 0) &&
626 ((MagickRealType) GetGreenPixelComponent(q) < threshold.green))
627 SetGreenPixelComponent(q,0);
628 if (((channel & BlueChannel) != 0) &&
629 ((MagickRealType) GetBluePixelComponent(q) < threshold.blue))
630 SetBluePixelComponent(q,0);
631 if (((channel & OpacityChannel) != 0) &&
632 ((MagickRealType) GetOpacityPixelComponent(q) < threshold.opacity))
633 SetOpacityPixelComponent(q,0);
634 if (((channel & IndexChannel) != 0) &&
635 (image->colorspace == CMYKColorspace) &&
636 ((MagickRealType) GetIndexPixelComponent(indexes+x) <
638 SetIndexPixelComponent(indexes+x,0);
642 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
644 if (image->progress_monitor != (MagickProgressMonitor) NULL)
649 #if defined(MAGICKCORE_OPENMP_SUPPORT)
650 #pragma omp critical (MagickCore_BlackThresholdImageChannel)
652 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
654 if (proceed == MagickFalse)
658 image_view=DestroyCacheView(image_view);
663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667 % C l a m p I m a g e %
671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
673 % ClampImage() restricts the color range from 0 to the quantum depth.
675 % The format of the ClampImageChannel method is:
677 % MagickBooleanType ClampImage(Image *image)
678 % MagickBooleanType ClampImageChannel(Image *image,
679 % const ChannelType channel)
681 % A description of each parameter follows:
683 % o image: the image.
685 % o channel: the channel type.
689 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
691 #if defined(MAGICKCORE_HDRI_SUPPORT)
694 if (quantum >= QuantumRange)
695 return(QuantumRange);
702 MagickExport MagickBooleanType ClampImage(Image *image)
707 status=ClampImageChannel(image,DefaultChannels);
711 MagickExport MagickBooleanType ClampImageChannel(Image *image,
712 const ChannelType channel)
714 #define ClampImageTag "Clamp/Image"
731 assert(image != (Image *) NULL);
732 assert(image->signature == MagickSignature);
733 if (image->debug != MagickFalse)
734 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
735 if (image->storage_class == PseudoClass)
744 for (i=0; i < (ssize_t) image->colors; i++)
746 SetRedPixelComponent(q,ClampToUnsignedQuantum(
747 GetRedPixelComponent(q)));
748 SetGreenPixelComponent(q,ClampToUnsignedQuantum(
749 GetGreenPixelComponent(q)));
750 SetBluePixelComponent(q,ClampToUnsignedQuantum(
751 GetBluePixelComponent(q)));
752 SetOpacityPixelComponent(q,ClampToUnsignedQuantum(
753 GetOpacityPixelComponent(q)));
756 return(SyncImage(image));
763 exception=(&image->exception);
764 image_view=AcquireCacheView(image);
765 #if defined(MAGICKCORE_OPENMP_SUPPORT)
766 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
768 for (y=0; y < (ssize_t) image->rows; y++)
779 if (status == MagickFalse)
781 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
782 if (q == (PixelPacket *) NULL)
787 indexes=GetCacheViewAuthenticIndexQueue(image_view);
788 for (x=0; x < (ssize_t) image->columns; x++)
790 if ((channel & RedChannel) != 0)
791 SetRedPixelComponent(q,ClampToUnsignedQuantum(
792 GetRedPixelComponent(q)));
793 if ((channel & GreenChannel) != 0)
794 SetGreenPixelComponent(q,ClampToUnsignedQuantum(
795 GetGreenPixelComponent(q)));
796 if ((channel & BlueChannel) != 0)
797 SetBluePixelComponent(q,ClampToUnsignedQuantum(
798 GetBluePixelComponent(q)));
799 if ((channel & OpacityChannel) != 0)
800 SetOpacityPixelComponent(q,ClampToUnsignedQuantum(
801 GetOpacityPixelComponent(q)));
802 if (((channel & IndexChannel) != 0) &&
803 (image->colorspace == CMYKColorspace))
804 SetIndexPixelComponent(indexes+x,ClampToUnsignedQuantum(
805 GetIndexPixelComponent(indexes+x)));
808 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
810 if (image->progress_monitor != (MagickProgressMonitor) NULL)
815 #if defined(MAGICKCORE_OPENMP_SUPPORT)
816 #pragma omp critical (MagickCore_ClampImageChannel)
818 proceed=SetImageProgress(image,ClampImageTag,progress++,
820 if (proceed == MagickFalse)
824 image_view=DestroyCacheView(image_view);
829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
833 % D e s t r o y T h r e s h o l d M a p %
837 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839 % DestroyThresholdMap() de-allocate the given ThresholdMap
841 % The format of the ListThresholdMaps method is:
843 % ThresholdMap *DestroyThresholdMap(Threshold *map)
845 % A description of each parameter follows.
847 % o map: Pointer to the Threshold map to destroy
850 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
852 assert(map != (ThresholdMap *) NULL);
853 if (map->map_id != (char *) NULL)
854 map->map_id=DestroyString(map->map_id);
855 if (map->description != (char *) NULL)
856 map->description=DestroyString(map->description);
857 if (map->levels != (ssize_t *) NULL)
858 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
859 map=(ThresholdMap *) RelinquishMagickMemory(map);
864 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868 + G e t T h r e s h o l d M a p F i l e %
872 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
874 % GetThresholdMapFile() look for a given threshold map name or alias in the
875 % given XML file data, and return the allocated the map when found.
877 % The format of the ListThresholdMaps method is:
879 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
880 % const char *map_id,ExceptionInfo *exception)
882 % A description of each parameter follows.
884 % o xml: The threshold map list in XML format.
886 % o filename: The threshold map XML filename.
888 % o map_id: ID of the map to look for in XML list.
890 % o exception: return any errors or warnings in this structure.
893 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
894 const char *filename,const char *map_id,ExceptionInfo *exception)
912 map = (ThresholdMap *)NULL;
913 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
914 "Loading threshold map file \"%s\" ...",filename);
915 thresholds=NewXMLTree(xml,exception);
916 if ( thresholds == (XMLTreeInfo *)NULL )
919 for( threshold = GetXMLTreeChild(thresholds,"threshold");
920 threshold != (XMLTreeInfo *)NULL;
921 threshold = GetNextXMLTreeTag(threshold) ) {
922 attr = GetXMLTreeAttribute(threshold, "map");
923 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
925 attr = GetXMLTreeAttribute(threshold, "alias");
926 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
929 if ( threshold == (XMLTreeInfo *)NULL ) {
932 description = GetXMLTreeChild(threshold,"description");
933 if ( description == (XMLTreeInfo *)NULL ) {
934 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
935 "XmlMissingElement", "<description>, map \"%s\"", map_id);
936 thresholds = DestroyXMLTree(thresholds);
939 levels = GetXMLTreeChild(threshold,"levels");
940 if ( levels == (XMLTreeInfo *)NULL ) {
941 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
942 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
943 thresholds = DestroyXMLTree(thresholds);
947 /* The map has been found -- Allocate a Threshold Map to return */
948 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
949 if ( map == (ThresholdMap *)NULL )
950 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
951 map->map_id = (char *)NULL;
952 map->description = (char *)NULL;
953 map->levels = (ssize_t *) NULL;
955 /* Assign Basic Attributes */
956 attr = GetXMLTreeAttribute(threshold, "map");
957 if ( attr != (char *)NULL )
958 map->map_id = ConstantString(attr);
960 content = GetXMLTreeContent(description);
961 if ( content != (char *)NULL )
962 map->description = ConstantString(content);
964 attr = GetXMLTreeAttribute(levels, "width");
965 if ( attr == (char *)NULL ) {
966 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
967 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
968 thresholds = DestroyXMLTree(thresholds);
969 map = DestroyThresholdMap(map);
972 map->width = StringToUnsignedLong(attr);
973 if ( map->width == 0 ) {
974 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
975 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
976 thresholds = DestroyXMLTree(thresholds);
977 map = DestroyThresholdMap(map);
981 attr = GetXMLTreeAttribute(levels, "height");
982 if ( attr == (char *)NULL ) {
983 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
984 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
985 thresholds = DestroyXMLTree(thresholds);
986 map = DestroyThresholdMap(map);
989 map->height = StringToUnsignedLong(attr);
990 if ( map->height == 0 ) {
991 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
992 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
993 thresholds = DestroyXMLTree(thresholds);
994 map = DestroyThresholdMap(map);
998 attr = GetXMLTreeAttribute(levels, "divisor");
999 if ( attr == (char *)NULL ) {
1000 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1001 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
1002 thresholds = DestroyXMLTree(thresholds);
1003 map = DestroyThresholdMap(map);
1006 map->divisor = (ssize_t) StringToLong(attr);
1007 if ( map->divisor < 2 ) {
1008 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1009 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
1010 thresholds = DestroyXMLTree(thresholds);
1011 map = DestroyThresholdMap(map);
1015 /* Allocate theshold levels array */
1016 content = GetXMLTreeContent(levels);
1017 if ( content == (char *)NULL ) {
1018 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1019 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
1020 thresholds = DestroyXMLTree(thresholds);
1021 map = DestroyThresholdMap(map);
1024 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1025 sizeof(*map->levels));
1026 if ( map->levels == (ssize_t *)NULL )
1027 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1028 { /* parse levels into integer array */
1031 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
1032 map->levels[i] = (ssize_t)strtol(content, &p, 10);
1033 if ( p == content ) {
1034 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1035 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1036 thresholds = DestroyXMLTree(thresholds);
1037 map = DestroyThresholdMap(map);
1040 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
1041 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1042 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1043 (double) map->levels[i],map_id);
1044 thresholds = DestroyXMLTree(thresholds);
1045 map = DestroyThresholdMap(map);
1050 value=(double) strtol(content,&p,10);
1054 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1055 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
1056 thresholds=DestroyXMLTree(thresholds);
1057 map=DestroyThresholdMap(map);
1062 thresholds = DestroyXMLTree(thresholds);
1067 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 % G e t T h r e s h o l d M a p %
1075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1077 % GetThresholdMap() load and search one or more threshold map files for the
1078 % a map matching the given name or aliase.
1080 % The format of the GetThresholdMap method is:
1082 % ThresholdMap *GetThresholdMap(const char *map_id,
1083 % ExceptionInfo *exception)
1085 % A description of each parameter follows.
1087 % o map_id: ID of the map to look for.
1089 % o exception: return any errors or warnings in this structure.
1092 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1093 ExceptionInfo *exception)
1104 map=(ThresholdMap *)NULL;
1105 options=GetConfigureOptions(ThresholdsFilename,exception);
1106 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1107 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1108 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1109 GetStringInfoPath(option),map_id,exception);
1110 options=DestroyConfigureOptions(options);
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1119 + L i s t T h r e s h o l d M a p F i l e %
1123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125 % ListThresholdMapFile() lists the threshold maps and their descriptions
1126 % in the given XML file data.
1128 % The format of the ListThresholdMaps method is:
1130 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1131 % const char *filename,ExceptionInfo *exception)
1133 % A description of each parameter follows.
1135 % o file: An pointer to the output FILE.
1137 % o xml: The threshold map list in XML format.
1139 % o filename: The threshold map XML filename.
1141 % o exception: return any errors or warnings in this structure.
1144 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1145 const char *filename,ExceptionInfo *exception)
1147 XMLTreeInfo *thresholds,*threshold,*description;
1148 const char *map,*alias,*content;
1150 assert( xml != (char *)NULL );
1151 assert( file != (FILE *)NULL );
1153 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1154 "Loading threshold map file \"%s\" ...",filename);
1155 thresholds=NewXMLTree(xml,exception);
1156 if ( thresholds == (XMLTreeInfo *)NULL )
1157 return(MagickFalse);
1159 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1160 (void) FormatLocaleFile(file,
1161 "----------------------------------------------------\n");
1163 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1164 threshold != (XMLTreeInfo *)NULL;
1165 threshold = GetNextXMLTreeTag(threshold) )
1167 map = GetXMLTreeAttribute(threshold, "map");
1168 if (map == (char *) NULL) {
1169 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1170 "XmlMissingAttribute", "<map>");
1171 thresholds=DestroyXMLTree(thresholds);
1172 return(MagickFalse);
1174 alias = GetXMLTreeAttribute(threshold, "alias");
1175 /* alias is optional, no if test needed */
1176 description=GetXMLTreeChild(threshold,"description");
1177 if ( description == (XMLTreeInfo *)NULL ) {
1178 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1179 "XmlMissingElement", "<description>, map \"%s\"", map);
1180 thresholds=DestroyXMLTree(thresholds);
1181 return(MagickFalse);
1183 content=GetXMLTreeContent(description);
1184 if ( content == (char *)NULL ) {
1185 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1186 "XmlMissingContent", "<description>, map \"%s\"", map);
1187 thresholds=DestroyXMLTree(thresholds);
1188 return(MagickFalse);
1190 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1193 thresholds=DestroyXMLTree(thresholds);
1198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202 % L i s t T h r e s h o l d M a p s %
1206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208 % ListThresholdMaps() lists the threshold maps and their descriptions
1209 % as defined by "threshold.xml" to a file.
1211 % The format of the ListThresholdMaps method is:
1213 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1215 % A description of each parameter follows.
1217 % o file: An pointer to the output FILE.
1219 % o exception: return any errors or warnings in this structure.
1222 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1223 ExceptionInfo *exception)
1235 if ( file == (FILE *)NULL )
1237 options=GetConfigureOptions(ThresholdsFilename,exception);
1239 (void) FormatLocaleFile(file,
1240 "\n Threshold Maps for Ordered Dither Operations\n");
1241 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1242 != (const StringInfo *) NULL)
1244 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1245 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1246 GetStringInfoPath(option),exception);
1248 options=DestroyConfigureOptions(options);
1249 return(status != 0 ? MagickTrue : MagickFalse);
1253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1257 % O r d e r e d D i t h e r I m a g e %
1261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1263 % OrderedDitherImage() uses the ordered dithering technique of reducing color
1264 % images to monochrome using positional information to retain as much
1265 % information as possible.
1267 % WARNING: This function is deprecated, and is now just a call to
1268 % the more more powerful OrderedPosterizeImage(); function.
1270 % The format of the OrderedDitherImage method is:
1272 % MagickBooleanType OrderedDitherImage(Image *image)
1273 % MagickBooleanType OrderedDitherImageChannel(Image *image,
1274 % const ChannelType channel,ExceptionInfo *exception)
1276 % A description of each parameter follows:
1278 % o image: the image.
1280 % o channel: the channel or channels to be thresholded.
1282 % o exception: return any errors or warnings in this structure.
1286 MagickExport MagickBooleanType OrderedDitherImage(Image *image)
1291 status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
1295 MagickExport MagickBooleanType OrderedDitherImageChannel(Image *image,
1296 const ChannelType channel,ExceptionInfo *exception)
1302 Call the augumented function OrderedPosterizeImage()
1304 status=OrderedPosterizeImageChannel(image,channel,"o8x8",exception);
1309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1313 % O r d e r e d P o s t e r i z e I m a g e %
1317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1319 % OrderedPosterizeImage() will perform a ordered dither based on a number
1320 % of pre-defined dithering threshold maps, but over multiple intensity
1321 % levels, which can be different for different channels, according to the
1324 % The format of the OrderedPosterizeImage method is:
1326 % MagickBooleanType OrderedPosterizeImage(Image *image,
1327 % const char *threshold_map,ExceptionInfo *exception)
1328 % MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1329 % const ChannelType channel,const char *threshold_map,
1330 % ExceptionInfo *exception)
1332 % A description of each parameter follows:
1334 % o image: the image.
1336 % o channel: the channel or channels to be thresholded.
1338 % o threshold_map: A string containing the name of the threshold dither
1339 % map to use, followed by zero or more numbers representing the number
1340 % of color levels tho dither between.
1342 % Any level number less than 2 will be equivalent to 2, and means only
1343 % binary dithering will be applied to each color channel.
1345 % No numbers also means a 2 level (bitmap) dither will be applied to all
1346 % channels, while a single number is the number of levels applied to each
1347 % channel in sequence. More numbers will be applied in turn to each of
1348 % the color channels.
1350 % For example: "o3x3,6" will generate a 6 level posterization of the
1351 % image with a ordered 3x3 diffused pixel dither being applied between
1352 % each level. While checker,8,8,4 will produce a 332 colormaped image
1353 % with only a single checkerboard hash pattern (50% grey) between each
1354 % color level, to basically double the number of color levels with
1355 % a bare minimim of dithering.
1357 % o exception: return any errors or warnings in this structure.
1360 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1361 const char *threshold_map,ExceptionInfo *exception)
1366 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1371 MagickExport MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1372 const ChannelType channel,const char *threshold_map,ExceptionInfo *exception)
1374 #define DitherImageTag "Dither/Image"
1394 assert(image != (Image *) NULL);
1395 assert(image->signature == MagickSignature);
1396 if (image->debug != MagickFalse)
1397 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1398 assert(exception != (ExceptionInfo *) NULL);
1399 assert(exception->signature == MagickSignature);
1400 if (threshold_map == (const char *) NULL)
1404 token[MaxTextExtent];
1409 p=(char *)threshold_map;
1410 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1414 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1416 if ((p-threshold_map) >= (MaxTextExtent-1))
1418 token[p-threshold_map] = *p;
1421 token[p-threshold_map] = '\0';
1422 map = GetThresholdMap(token, exception);
1423 if ( map == (ThresholdMap *)NULL ) {
1424 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1425 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1426 return(MagickFalse);
1429 /* Set channel levels from extra comma separated arguments
1430 Default to 2, the single value given, or individual channel values
1433 { /* parse directly as a comma separated list of integers */
1436 p = strchr((char *) threshold_map,',');
1437 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
1438 levels.index = (unsigned int) strtoul(p, &p, 10);
1442 levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
1443 levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
1444 levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
1445 levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
1446 levels.index = ((channel & IndexChannel) != 0
1447 && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
1449 /* if more than a single number, each channel has a separate value */
1450 if ( p != (char *) NULL && *p == ',' ) {
1451 p=strchr((char *) threshold_map,',');
1453 if ((channel & RedChannel) != 0)
1454 levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1455 if ((channel & GreenChannel) != 0)
1456 levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1457 if ((channel & BlueChannel) != 0)
1458 levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1459 if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
1460 levels.index=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1461 if ((channel & OpacityChannel) != 0)
1462 levels.opacity = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
1466 /* Parse level values as a geometry */
1468 * How to map GeometryInfo structure elements into
1469 * LongPixelPacket structure elements, but according to channel?
1470 * Note the channels list may skip elements!!!!
1471 * EG -channel BA -ordered-dither map,2,3
1472 * will need to map g.rho -> l.blue, and g.sigma -> l.opacity
1473 * A simpler way is needed, probably converting geometry to a temporary
1474 * array, then using channel to advance the index into ssize_t pixel packet.
1479 printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
1480 levels.red, levels.green, levels.blue, levels.opacity, levels.index);
1483 { /* Do the posterized ordered dithering of the image */
1487 /* d = number of psuedo-level divisions added between color levels */
1490 /* reduce levels to levels - 1 */
1491 levels.red = levels.red ? levels.red-1 : 0;
1492 levels.green = levels.green ? levels.green-1 : 0;
1493 levels.blue = levels.blue ? levels.blue-1 : 0;
1494 levels.opacity = levels.opacity ? levels.opacity-1 : 0;
1495 levels.index = levels.index ? levels.index-1 : 0;
1497 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1499 InheritException(exception,&image->exception);
1500 return(MagickFalse);
1504 image_view=AcquireCacheView(image);
1505 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1506 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1508 for (y=0; y < (ssize_t) image->rows; y++)
1510 register IndexPacket
1516 register PixelPacket
1519 if (status == MagickFalse)
1521 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1522 if (q == (PixelPacket *) NULL)
1527 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1528 for (x=0; x < (ssize_t) image->columns; x++)
1536 Figure out the dither threshold for this pixel
1537 This must be a integer from 1 to map->divisor-1
1539 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1541 /* Dither each channel in the image as appropriate
1542 Notes on the integer Math...
1543 total number of divisions = (levels-1)*(divisor-1)+1)
1544 t1 = this colors psuedo_level =
1545 q->red * total_divisions / (QuantumRange+1)
1546 l = posterization level 0..levels
1547 t = dither threshold level 0..divisor-1 NB: 0 only on last
1548 Each color_level is of size QuantumRange / (levels-1)
1549 NB: All input levels and divisor are already had 1 subtracted
1550 Opacity is inverted so 'off' represents transparent.
1553 t = (ssize_t) (QuantumScale*GetRedPixelComponent(q)*(levels.red*d+1));
1555 SetRedPixelComponent(q,RoundToQuantum((MagickRealType)
1556 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)));
1559 t = (ssize_t) (QuantumScale*GetGreenPixelComponent(q)*
1560 (levels.green*d+1));
1562 SetGreenPixelComponent(q,RoundToQuantum((MagickRealType)
1563 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)));
1566 t = (ssize_t) (QuantumScale*GetBluePixelComponent(q)*
1569 SetBluePixelComponent(q,RoundToQuantum((MagickRealType)
1570 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)));
1572 if (levels.opacity) {
1573 t = (ssize_t) ((1.0-QuantumScale*GetOpacityPixelComponent(q))*
1574 (levels.opacity*d+1));
1576 SetOpacityPixelComponent(q,RoundToQuantum((MagickRealType)
1577 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
1581 t = (ssize_t) (QuantumScale*GetIndexPixelComponent(indexes+x)*
1582 (levels.index*d+1));
1584 SetIndexPixelComponent(indexes+x,RoundToQuantum((MagickRealType) ((l+
1585 (t>=threshold))*(MagickRealType) QuantumRange/levels.index)));
1589 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1591 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1596 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1597 #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
1599 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1600 if (proceed == MagickFalse)
1604 image_view=DestroyCacheView(image_view);
1606 map=DestroyThresholdMap(map);
1611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1615 % R a n d o m T h r e s h o l d I m a g e %
1619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1621 % RandomThresholdImage() changes the value of individual pixels based on the
1622 % intensity of each pixel compared to a random threshold. The result is a
1623 % low-contrast, two color image.
1625 % The format of the RandomThresholdImage method is:
1627 % MagickBooleanType RandomThresholdImageChannel(Image *image,
1628 % const char *thresholds,ExceptionInfo *exception)
1629 % MagickBooleanType RandomThresholdImageChannel(Image *image,
1630 % const ChannelType channel,const char *thresholds,
1631 % ExceptionInfo *exception)
1633 % A description of each parameter follows:
1635 % o image: the image.
1637 % o channel: the channel or channels to be thresholded.
1639 % o thresholds: a geometry string containing low,high thresholds. If the
1640 % string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1641 % is performed instead.
1643 % o exception: return any errors or warnings in this structure.
1647 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1648 const char *thresholds,ExceptionInfo *exception)
1653 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
1658 MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
1659 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1661 #define ThresholdImageTag "Threshold/Image"
1686 **restrict random_info;
1691 assert(image != (Image *) NULL);
1692 assert(image->signature == MagickSignature);
1693 if (image->debug != MagickFalse)
1694 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1695 assert(exception != (ExceptionInfo *) NULL);
1696 assert(exception->signature == MagickSignature);
1697 if (thresholds == (const char *) NULL)
1699 GetMagickPixelPacket(image,&threshold);
1701 max_threshold=(MagickRealType) QuantumRange;
1702 flags=ParseGeometry(thresholds,&geometry_info);
1703 min_threshold=geometry_info.rho;
1704 max_threshold=geometry_info.sigma;
1705 if ((flags & SigmaValue) == 0)
1706 max_threshold=min_threshold;
1707 if (strchr(thresholds,'%') != (char *) NULL)
1709 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1710 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1713 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
1714 (min_threshold <= 8))
1717 Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
1719 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
1723 Random threshold image.
1727 if (channel == CompositeChannels)
1729 if (AcquireImageColormap(image,2) == MagickFalse)
1730 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1732 random_info=AcquireRandomInfoThreadSet();
1733 image_view=AcquireCacheView(image);
1734 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1735 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1737 for (y=0; y < (ssize_t) image->rows; y++)
1740 id = GetOpenMPThreadId();
1745 register IndexPacket
1751 register PixelPacket
1754 if (status == MagickFalse)
1756 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1758 if (q == (PixelPacket *) NULL)
1763 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1764 for (x=0; x < (ssize_t) image->columns; x++)
1772 intensity=(MagickRealType) PixelIntensityToQuantum(q);
1773 if (intensity < min_threshold)
1774 threshold.index=min_threshold;
1775 else if (intensity > max_threshold)
1776 threshold.index=max_threshold;
1778 threshold.index=(MagickRealType)(QuantumRange*
1779 GetPseudoRandomValue(random_info[id]));
1780 index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
1781 SetIndexPixelComponent(indexes+x,index);
1782 SetRGBOPixelComponents(q,image->colormap+(ssize_t) index);
1785 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1786 if (sync == MagickFalse)
1788 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1793 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1794 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1796 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1798 if (proceed == MagickFalse)
1802 image_view=DestroyCacheView(image_view);
1803 random_info=DestroyRandomInfoThreadSet(random_info);
1806 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1808 InheritException(exception,&image->exception);
1809 return(MagickFalse);
1811 random_info=AcquireRandomInfoThreadSet();
1812 image_view=AcquireCacheView(image);
1813 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1814 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1816 for (y=0; y < (ssize_t) image->rows; y++)
1819 id = GetOpenMPThreadId();
1821 register IndexPacket
1824 register PixelPacket
1830 if (status == MagickFalse)
1832 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1833 if (q == (PixelPacket *) NULL)
1838 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1839 for (x=0; x < (ssize_t) image->columns; x++)
1841 if ((channel & RedChannel) != 0)
1843 if ((MagickRealType) GetRedPixelComponent(q) < min_threshold)
1844 threshold.red=min_threshold;
1846 if ((MagickRealType) GetRedPixelComponent(q) > max_threshold)
1847 threshold.red=max_threshold;
1849 threshold.red=(MagickRealType) (QuantumRange*
1850 GetPseudoRandomValue(random_info[id]));
1852 if ((channel & GreenChannel) != 0)
1854 if ((MagickRealType) GetGreenPixelComponent(q) < min_threshold)
1855 threshold.green=min_threshold;
1857 if ((MagickRealType) GetGreenPixelComponent(q) > max_threshold)
1858 threshold.green=max_threshold;
1860 threshold.green=(MagickRealType) (QuantumRange*
1861 GetPseudoRandomValue(random_info[id]));
1863 if ((channel & BlueChannel) != 0)
1865 if ((MagickRealType) GetBluePixelComponent(q) < min_threshold)
1866 threshold.blue=min_threshold;
1868 if ((MagickRealType) GetBluePixelComponent(q) > max_threshold)
1869 threshold.blue=max_threshold;
1871 threshold.blue=(MagickRealType) (QuantumRange*
1872 GetPseudoRandomValue(random_info[id]));
1874 if ((channel & OpacityChannel) != 0)
1876 if ((MagickRealType) GetOpacityPixelComponent(q) < min_threshold)
1877 threshold.opacity=min_threshold;
1879 if ((MagickRealType) GetOpacityPixelComponent(q) > max_threshold)
1880 threshold.opacity=max_threshold;
1882 threshold.opacity=(MagickRealType) (QuantumRange*
1883 GetPseudoRandomValue(random_info[id]));
1885 if (((channel & IndexChannel) != 0) &&
1886 (image->colorspace == CMYKColorspace))
1888 if ((MagickRealType) GetIndexPixelComponent(indexes+x) < min_threshold)
1889 threshold.index=min_threshold;
1891 if ((MagickRealType) GetIndexPixelComponent(indexes+x) > max_threshold)
1892 threshold.index=max_threshold;
1894 threshold.index=(MagickRealType) (QuantumRange*
1895 GetPseudoRandomValue(random_info[id]));
1897 if ((channel & RedChannel) != 0)
1898 SetRedPixelComponent(q,(MagickRealType) GetRedPixelComponent(q) <=
1899 threshold.red ? 0 : QuantumRange);
1900 if ((channel & GreenChannel) != 0)
1901 SetGreenPixelComponent(q,(MagickRealType) GetGreenPixelComponent(q) <=
1902 threshold.green ? 0 : QuantumRange);
1903 if ((channel & BlueChannel) != 0)
1904 SetBluePixelComponent(q,(MagickRealType) GetBluePixelComponent(q) <=
1905 threshold.blue ? 0 : QuantumRange);
1906 if ((channel & OpacityChannel) != 0)
1907 SetOpacityPixelComponent(q,(MagickRealType) GetOpacityPixelComponent(q)
1908 <= threshold.opacity ? 0 : QuantumRange);
1909 if (((channel & IndexChannel) != 0) &&
1910 (image->colorspace == CMYKColorspace))
1911 SetIndexPixelComponent(indexes+x,(MagickRealType)
1912 GetIndexPixelComponent(indexes+x) <= threshold.index ? 0 :
1916 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1918 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1923 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1924 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1926 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1928 if (proceed == MagickFalse)
1932 image_view=DestroyCacheView(image_view);
1933 random_info=DestroyRandomInfoThreadSet(random_info);
1938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1942 % W h i t e T h r e s h o l d I m a g e %
1946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1948 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1949 % the threshold into white while leaving all pixels at or below the threshold
1952 % The format of the WhiteThresholdImage method is:
1954 % MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
1955 % MagickBooleanType WhiteThresholdImageChannel(Image *image,
1956 % const ChannelType channel,const char *threshold,
1957 % ExceptionInfo *exception)
1959 % A description of each parameter follows:
1961 % o image: the image.
1963 % o channel: the channel or channels to be thresholded.
1965 % o threshold: Define the threshold value.
1967 % o exception: return any errors or warnings in this structure.
1970 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1971 const char *threshold)
1976 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
1981 MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
1982 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1984 #define ThresholdImageTag "Threshold/Image"
2007 assert(image != (Image *) NULL);
2008 assert(image->signature == MagickSignature);
2009 if (image->debug != MagickFalse)
2010 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2011 if (thresholds == (const char *) NULL)
2013 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2014 return(MagickFalse);
2015 flags=ParseGeometry(thresholds,&geometry_info);
2016 GetMagickPixelPacket(image,&threshold);
2017 threshold.red=geometry_info.rho;
2018 threshold.green=geometry_info.sigma;
2019 if ((flags & SigmaValue) == 0)
2020 threshold.green=threshold.red;
2021 threshold.blue=geometry_info.xi;
2022 if ((flags & XiValue) == 0)
2023 threshold.blue=threshold.red;
2024 threshold.opacity=geometry_info.psi;
2025 if ((flags & PsiValue) == 0)
2026 threshold.opacity=threshold.red;
2027 threshold.index=geometry_info.chi;
2028 if ((flags & ChiValue) == 0)
2029 threshold.index=threshold.red;
2030 if ((flags & PercentValue) != 0)
2032 threshold.red*=(QuantumRange/100.0);
2033 threshold.green*=(QuantumRange/100.0);
2034 threshold.blue*=(QuantumRange/100.0);
2035 threshold.opacity*=(QuantumRange/100.0);
2036 threshold.index*=(QuantumRange/100.0);
2039 White threshold image.
2043 image_view=AcquireCacheView(image);
2044 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2045 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2047 for (y=0; y < (ssize_t) image->rows; y++)
2049 register IndexPacket
2055 register PixelPacket
2058 if (status == MagickFalse)
2060 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2061 if (q == (PixelPacket *) NULL)
2066 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2067 for (x=0; x < (ssize_t) image->columns; x++)
2069 if (channel != DefaultChannels)
2071 if (PixelIntensity(q) > MagickPixelIntensity(&threshold))
2073 SetRedPixelComponent(q,QuantumRange);
2074 SetGreenPixelComponent(q,QuantumRange);
2075 SetBluePixelComponent(q,QuantumRange);
2076 if (image->colorspace == CMYKColorspace)
2077 SetIndexPixelComponent(indexes+x,QuantumRange);
2082 if (((channel & RedChannel) != 0) &&
2083 ((MagickRealType) GetRedPixelComponent(q) > threshold.red))
2084 SetRedPixelComponent(q,QuantumRange);
2085 if (((channel & GreenChannel) != 0) &&
2086 ((MagickRealType) GetGreenPixelComponent(q) > threshold.green))
2087 SetGreenPixelComponent(q,QuantumRange);
2088 if (((channel & BlueChannel) != 0) &&
2089 ((MagickRealType) GetBluePixelComponent(q) > threshold.blue))
2090 SetBluePixelComponent(q,QuantumRange);
2091 if (((channel & OpacityChannel) != 0) &&
2092 ((MagickRealType) GetOpacityPixelComponent(q) > threshold.opacity))
2093 SetOpacityPixelComponent(q,QuantumRange);
2094 if (((channel & IndexChannel) != 0) &&
2095 (image->colorspace == CMYKColorspace) &&
2096 ((MagickRealType) GetIndexPixelComponent(indexes+x)) >
2098 SetIndexPixelComponent(indexes+x,QuantumRange);
2102 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2104 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2109 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2110 #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
2112 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2114 if (proceed == MagickFalse)
2118 image_view=DestroyCacheView(image_view);