From b149ea6d636cabf4412d4e5139c8e14ea7c15728 Mon Sep 17 00:00:00 2001 From: Cristy Date: Sun, 5 Aug 2018 16:17:42 -0400 Subject: [PATCH] New -range threshold option that combines hard and soft thresholding --- ChangeLog | 1 + MagickCore/option.c | 3 + MagickCore/threshold.c | 144 +++++++++++++++++++++++++++++++++++ MagickCore/threshold.h | 2 + MagickWand/convert.c | 13 ++++ MagickWand/mogrify.c | 39 +++++++++- MagickWand/operation.c | 24 ++++++ utilities/convert.1.in | 2 + utilities/magick-script.1.in | 2 + utilities/magick.1.in | 2 + utilities/mogrify.1.in | 2 + 11 files changed, 233 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index ffdcb68bf..d22d636c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ * XBM coder leaves the hex image data uninitialized if hex value of the pixel is negative. * More improvements to SVG text handling. + * New -range threshold option that combines hard and soft thresholding. 2018-07-23 7.0.8-8 Cristy * Release ImageMagick version 7.0.8-8, GIT revision 14583:300fdbcfd:20180723. diff --git a/MagickCore/option.c b/MagickCore/option.c index e0560b585..c3c5c7fb9 100644 --- a/MagickCore/option.c +++ b/MagickCore/option.c @@ -348,6 +348,7 @@ static const OptionInfo { " radial-blur", 0, UndefinedOptionFlag, MagickFalse }, { " raise", 0, UndefinedOptionFlag, MagickFalse }, { " random-threshold", 0, UndefinedOptionFlag, MagickFalse }, + { " range-threshold", 0, UndefinedOptionFlag, MagickFalse }, { " resample", 0, UndefinedOptionFlag, MagickFalse }, { " resize", 0, UndefinedOptionFlag, MagickFalse }, { " roll", 0, UndefinedOptionFlag, MagickFalse }, @@ -927,6 +928,8 @@ static const OptionInfo { "+raise", 1L, SimpleOperatorFlag, MagickFalse }, { "+random-threshold", 1L, DeprecateOptionFlag, MagickTrue }, { "-random-threshold", 1L, SimpleOperatorFlag, MagickFalse }, + { "+range-threshold", 1L, DeprecateOptionFlag, MagickTrue }, + { "-range-threshold", 1L, SimpleOperatorFlag, MagickFalse }, { "-read", 1L, NoImageOperatorFlag | NeverInterpretArgsFlag, MagickFalse }, { "+read-mask", 0L, SimpleOperatorFlag | NeverInterpretArgsFlag, MagickFalse }, { "-read-mask", 1L, SimpleOperatorFlag | NeverInterpretArgsFlag, MagickFalse }, diff --git a/MagickCore/threshold.c b/MagickCore/threshold.c index 8271798d8..0c87b5826 100644 --- a/MagickCore/threshold.c +++ b/MagickCore/threshold.c @@ -2123,6 +2123,150 @@ MagickExport MagickBooleanType RandomThresholdImage(Image *image, % % % % % % +% R a n g e T h r e s h o l d I m a g e % +% % +% % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% RangeThresholdImage() applies soft and hard thresholding. +% +% The format of the RangeThresholdImage method is: +% +% MagickBooleanType RangeThresholdImage(Image *image, +% const double low_soft,const double high_soft,const double low_hard, +% const double high_hard,ExceptionInfo *exception) +% +% A description of each parameter follows: +% +% o image: the image. +% +% o low_soft: Define the minimum threshold value. +% +% o high_soft: Define the maximum threshold value. +% +% o low_hard: Define the minimum threshold value. +% +% o high_soft: Define the maximum threshold value. +% +% o exception: return any errors or warnings in this structure. +% +*/ +MagickExport MagickBooleanType RangeThresholdImage(Image *image, + const double low_soft,const double high_soft,const double low_hard, + const double high_hard,ExceptionInfo *exception) +{ +#define ThresholdImageTag "Threshold/Image" + + CacheView + *image_view; + + MagickBooleanType + status; + + MagickOffsetType + progress; + + ssize_t + y; + + assert(image != (Image *) NULL); + assert(image->signature == MagickCoreSignature); + if (image->debug != MagickFalse) + (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); + if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) + return(MagickFalse); + if (IsGrayColorspace(image->colorspace) != MagickFalse) + (void) TransformImageColorspace(image,sRGBColorspace,exception); + /* + Range threshold image. + */ + status=MagickTrue; + progress=0; + image_view=AcquireAuthenticCacheView(image,exception); +#if defined(MAGICKCORE_OPENMP_SUPPORT) + #pragma omp parallel for schedule(static) shared(progress,status) \ + magick_number_threads(image,image,image->rows,1) +#endif + for (y=0; y < (ssize_t) image->rows; y++) + { + register ssize_t + x; + + register Quantum + *magick_restrict q; + + if (status == MagickFalse) + continue; + q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); + if (q == (Quantum *) NULL) + { + status=MagickFalse; + continue; + } + for (x=0; x < (ssize_t) image->columns; x++) + { + double + pixel; + + register ssize_t + i; + + pixel=GetPixelIntensity(image,q); + for (i=0; i < (ssize_t) GetPixelChannels(image); i++) + { + PixelChannel channel = GetPixelChannelChannel(image,i); + PixelTrait traits = GetPixelChannelTraits(image,channel); + if ((traits & UpdatePixelTrait) == 0) + continue; + if (image->channel_mask != DefaultChannels) + pixel=(double) q[i]; + if (pixel < low_soft) + q[i]=0; + else + if ((pixel >= low_soft) && (pixel < high_soft)) + q[i]=ClampToQuantum(PerceptibleReciprocal(high_soft-low_soft)* + (pixel-low_soft)); + else + if ((pixel >= high_soft) && (pixel <= low_hard)) + q[i]=QuantumRange; + else + if ((pixel > low_hard) && (pixel <= high_hard)) + q[i]=ClampToQuantum(PerceptibleReciprocal(high_hard-low_hard)* + (high_hard-pixel)); + else + if (pixel > high_hard) + q[i]=0; + else + q[i]=0; + } + q+=GetPixelChannels(image); + } + if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) + status=MagickFalse; + if (image->progress_monitor != (MagickProgressMonitor) NULL) + { + MagickBooleanType + proceed; + +#if defined(MAGICKCORE_OPENMP_SUPPORT) + #pragma omp critical (MagickCore_RangeThresholdImage) +#endif + proceed=SetImageProgress(image,ThresholdImageTag,progress++, + image->rows); + if (proceed == MagickFalse) + status=MagickFalse; + } + } + image_view=DestroyCacheView(image_view); + return(status); +} + +/* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % % W h i t e T h r e s h o l d I m a g e % % % % % diff --git a/MagickCore/threshold.h b/MagickCore/threshold.h index 3b049126e..8b54c8c1a 100644 --- a/MagickCore/threshold.h +++ b/MagickCore/threshold.h @@ -50,6 +50,8 @@ extern MagickExport MagickBooleanType OrderedDitherImage(Image *,const char *,ExceptionInfo *), PerceptibleImage(Image *,const double,ExceptionInfo *), RandomThresholdImage(Image *,const double,const double,ExceptionInfo *), + RangeThresholdImage(Image *,const double,const double,const double, + const double,ExceptionInfo *), WhiteThresholdImage(Image *,const char *,ExceptionInfo *); #if defined(__cplusplus) || defined(c_plusplus) diff --git a/MagickWand/convert.c b/MagickWand/convert.c index 78527a717..efd6b27e4 100644 --- a/MagickWand/convert.c +++ b/MagickWand/convert.c @@ -275,6 +275,8 @@ static MagickBooleanType ConvertUsage(void) "-raise value lighten/darken image edges to create a 3-D effect", "-random-threshold low,high", " random threshold the image", + "-range-threshold values", + " combine hard and soft thresholding", "-region geometry apply options to a portion of the image", "-render render vector graphics", "-resample geometry change the resolution of an image", @@ -2532,6 +2534,17 @@ WandExport MagickBooleanType ConvertImageCommand(ImageInfo *image_info, ThrowConvertInvalidArgumentException(option,argv[i]); break; } + if (LocaleCompare("range-threshold",option+1) == 0) + { + if (*option == '+') + break; + i++; + if (i == (ssize_t) argc) + ThrowConvertException(OptionError,"MissingArgument",option); + if (IsGeometry(argv[i]) == MagickFalse) + ThrowConvertInvalidArgumentException(option,argv[i]); + break; + } if (LocaleCompare("read-mask",option+1) == 0) { if (*option == '+') diff --git a/MagickWand/mogrify.c b/MagickWand/mogrify.c index e54d53668..7e3546e2f 100644 --- a/MagickWand/mogrify.c +++ b/MagickWand/mogrify.c @@ -2523,7 +2523,7 @@ WandExport MagickBooleanType MogrifyImage(ImageInfo *image_info,const int argc, if (LocaleCompare("random-threshold",option+1) == 0) { /* - Threshold image. + Random threshold image. */ double min_threshold, @@ -2546,6 +2546,30 @@ WandExport MagickBooleanType MogrifyImage(ImageInfo *image_info,const int argc, exception); break; } + if (LocaleCompare("range-threshold",option+1) == 0) + { + /* + Range threshold image. + */ + (void) SyncImageSettings(mogrify_info,*image,exception); + flags=ParseGeometry(argv[i+1],&geometry_info); + if ((flags & SigmaValue) == 0) + geometry_info.sigma=geometry_info.rho; + if ((flags & XiValue) == 0) + geometry_info.xi=geometry_info.sigma; + if ((flags & PsiValue) == 0) + geometry_info.psi=geometry_info.xi; + if (strchr(argv[i+1],'%') != (char *) NULL) + { + geometry_info.rho*=(double) (0.01*QuantumRange); + geometry_info.sigma*=(double) (0.01*QuantumRange); + geometry_info.xi*=(double) (0.01*QuantumRange); + geometry_info.psi*=(double) (0.01*QuantumRange); + } + (void) RandomThresholdImage(*image,geometry_info.rho, + geometry_info.sigma,geometry_info.xi,geometry_info.psi,exception); + break; + } if (LocaleCompare("read-mask",option+1) == 0) { Image @@ -3540,6 +3564,8 @@ static MagickBooleanType MogrifyUsage(void) "-raise value lighten/darken image edges to create a 3-D effect", "-random-threshold low,high", " random threshold the image", + "-range-threshold values", + " combine hard and soft thresholding", "-region geometry apply options to a portion of the image", "-render render vector graphics", "-repage geometry size and location of an image canvas", @@ -5768,6 +5794,17 @@ WandExport MagickBooleanType MogrifyImageCommand(ImageInfo *image_info, ThrowMogrifyInvalidArgumentException(option,argv[i]); break; } + if (LocaleCompare("range-threshold",option+1) == 0) + { + if (*option == '+') + break; + i++; + if (i == (ssize_t) argc) + ThrowMogrifyException(OptionError,"MissingArgument",option); + if (IsGeometry(argv[i]) == MagickFalse) + ThrowMogrifyInvalidArgumentException(option,argv[i]); + break; + } if (LocaleCompare("read-mask",option+1) == 0) { if (*option == '+') diff --git a/MagickWand/operation.c b/MagickWand/operation.c index 365c118df..c083b832f 100644 --- a/MagickWand/operation.c +++ b/MagickWand/operation.c @@ -3052,6 +3052,30 @@ static MagickBooleanType CLISimpleOperatorImage(MagickCLI *cli_wand, _exception); break; } + if (LocaleCompare("random-threshold",option+1) == 0) + { + /* + Range threshold image. + */ + (void) SyncImageSettings(mogrify_info,*image,exception); + flags=ParseGeometry(argv[i+1],&geometry_info); + if ((flags & SigmaValue) == 0) + geometry_info.sigma=geometry_info.rho; + if ((flags & XiValue) == 0) + geometry_info.xi=geometry_info.sigma; + if ((flags & PsiValue) == 0) + geometry_info.psi=geometry_info.xi; + if (strchr(argv[i+1],'%') != (char *) NULL) + { + geometry_info.rho*=(double) (0.01*QuantumRange); + geometry_info.sigma*=(double) (0.01*QuantumRange); + geometry_info.xi*=(double) (0.01*QuantumRange); + geometry_info.psi*=(double) (0.01*QuantumRange); + } + (void) RandomThresholdImage(*image,geometry_info.rho, + geometry_info.sigma,geometry_info.xi,geometry_info.psi,exception); + break; + } if (LocaleCompare("read-mask",option+1) == 0) { /* Note: arguments do not have percent escapes expanded */ diff --git a/utilities/convert.1.in b/utilities/convert.1.in index b2b1835c0..c57e2ca94 100644 --- a/utilities/convert.1.in +++ b/utilities/convert.1.in @@ -221,6 +221,8 @@ Image Operators: \-raise value lighten/darken image edges to create a 3-D effect \-random-threshold low,high random threshold the image + \-range-threshold values + combine hard and soft thresholding \-region geometry apply options to a portion of the image \-render render vector graphics \-resample geometry change the resolution of an image diff --git a/utilities/magick-script.1.in b/utilities/magick-script.1.in index c0a9db139..9540c8855 100644 --- a/utilities/magick-script.1.in +++ b/utilities/magick-script.1.in @@ -184,6 +184,8 @@ Image Operators: \-raise value lighten/darken image edges to create a 3-D effect \-random-threshold low,high random threshold the image + \-range-threshold values + combine hard and soft thresholding \-region geometry apply options to a portion of the image \-render render vector graphics \-resample geometry change the resolution of an image diff --git a/utilities/magick.1.in b/utilities/magick.1.in index 74bb31ff9..8108c618c 100644 --- a/utilities/magick.1.in +++ b/utilities/magick.1.in @@ -181,6 +181,8 @@ Image Operators: \-raise value lighten/darken image edges to create a 3-D effect \-random-threshold low,high random threshold the image + \-range-threshold values + combine hard and soft thresholding \-region geometry apply options to a portion of the image \-render render vector graphics \-resample geometry change the resolution of an image diff --git a/utilities/mogrify.1.in b/utilities/mogrify.1.in index f215d122a..70beeab6e 100644 --- a/utilities/mogrify.1.in +++ b/utilities/mogrify.1.in @@ -218,6 +218,8 @@ Image Operators: \-raise value lighten/darken image edges to create a 3-D effect \-random-threshold low,high random threshold the image + \-range-threshold values + combine hard and soft thresholding \-region geometry apply options to a portion of the image \-render render vector graphics \-resample geometry change the resolution of an image -- 2.40.0