diff --git a/changelogs/diplib_next.md b/changelogs/diplib_next.md index 4e71a405..097fa591 100644 --- a/changelogs/diplib_next.md +++ b/changelogs/diplib_next.md @@ -38,6 +38,9 @@ date: 2020-00-00 The low-level functions `dip::SymmetricEigenDecomposition2()` and `dip::SymmetricEigenDecomposition3()` add the same argument, but as an enum rather than a string. +- `dip::CreateGauss()` has a new argument, `extent`, which defaults to `"full"`. When set to `"half"`, the output + is the first half (in 1D) or quadrant (in 2D) of the Gaussian kernel. + ### Changed functionality - All functions that compute a percentile (`dip::Percentile()`, `dip::PercentilePosition()`, diff --git a/dipimage/derivative.m b/dipimage/derivative.m index 04b18852..70ee61a3 100644 --- a/dipimage/derivative.m +++ b/dipimage/derivative.m @@ -36,7 +36,7 @@ % % DIPlib: % This function calls the DIPlib function dip::Derivative (which calls dip::GaussFIR, -% dip::GaussIIR, dip::GaussFT and dip::FiniteDifference) and dip::CreateGauss. +% dip::GaussIIR, dip::GaussFT and dip::FiniteDifference) and dip::CreateGauss. % (c)2017-2018, Cris Luengo. % Based on original DIPlib code: (c)1995-2014, Delft University of Technology. diff --git a/dipimage/gaussf.m b/dipimage/gaussf.m index a2141308..bd2ee9f1 100644 --- a/dipimage/gaussf.m +++ b/dipimage/gaussf.m @@ -32,7 +32,7 @@ % % DIPlib: % This function calls the DIPlib function dip::Derivative (which calls dip::GaussFIR, -% dip::GaussIIR and dip::GaussFT) and dip::CreateGauss. +% dip::GaussIIR and dip::GaussFT) and dip::CreateGauss. % (c)2017-2018, Cris Luengo. % Based on original DIPlib code: (c)1995-2014, Delft University of Technology. diff --git a/include/diplib/generation.h b/include/diplib/generation.h index b25658f0..28f21204 100644 --- a/include/diplib/generation.h +++ b/include/diplib/generation.h @@ -628,6 +628,19 @@ DIP_NODISCARD inline Image CreateDelta( UnsignedArray const& sizes, String const return out; } +// Create a half Gaussian, used by MakeGaussian in linear/gauss.cpp (where it is defined) +// Not documented because not a public function. We suggest you use `CreateGauss()` instead. +// Length will be given by truncation and sigma, but limited to meaningful values. +// +// !!! warning +// The second half of the gaussian will need to be scaled by -1.0 for odd derivative order (ex 1, 3, ...) +// Defined in src/linear/gauss.cpp +DIP_EXPORT std::vector< dfloat > MakeHalfGaussian( + dfloat sigma, + dip::uint derivativeOrder = 0, + dfloat truncation = 3.0, + DataType dt = DT_DFLOAT // if not DT_DFLOAT, assumed to be DT_SFLOAT +); // Create 1D Gaussian, used in linear/gauss.cpp (where it is defined) and in nonlinear/bilateral.cpp // Not documented because not a public function. We suggest you use `CreateGauss()` instead. @@ -654,6 +667,10 @@ DIP_EXPORT std::vector< dfloat > MakeGaussian( /// By setting `exponents` to a positive value for each dimension, the created kernel will be multiplied by /// the coordinates to the power of `exponents`. /// +/// `extent` defaults to `"full"`. Set it to `"half"` to generate only the first half (along each dimension) +/// of the kernel. +/// The second half of the gaussian will need to be scaled by -1.0 for odd derivative order (ex 1, 3, ...) +/// /// !!! warning /// Convolving an image with the result of this function is much less efficient than calling \ref Gauss. // Defined in src/linear/gauss.cpp @@ -662,16 +679,18 @@ DIP_EXPORT void CreateGauss( FloatArray const& sigmas, UnsignedArray derivativeOrder = { 0 }, dfloat truncation = 3.0, - UnsignedArray exponents = { 0 } + UnsignedArray exponents = { 0 }, + String const& extent = "full" ); DIP_NODISCARD inline Image CreateGauss( FloatArray const& sigmas, UnsignedArray derivativeOrder = { 0 }, dfloat truncation = 3.0, - UnsignedArray exponents = { 0 } + UnsignedArray exponents = { 0 }, + String const& extent = "full" ) { Image out; - CreateGauss( out, sigmas, std::move( derivativeOrder ), truncation, std::move( exponents )); + CreateGauss( out, sigmas, std::move( derivativeOrder ), truncation, std::move( exponents ), extent ); return out; } diff --git a/pydip/src/documentation_strings.h b/pydip/src/documentation_strings.h index b7897839..4540b50c 100644 --- a/pydip/src/documentation_strings.h +++ b/pydip/src/documentation_strings.h @@ -147,6 +147,7 @@ constexpr char const* dip·viewer·Manager·createWindow·WindowPtr· = "Create constexpr char const* dip·viewer·Manager·activeWindows = "Returns the number of managed windows."; constexpr char const* dip·viewer·Manager·destroyWindows = "Destroys all windows."; constexpr char const* dip·viewer·Manager·processEvents = "Processes event queue."; +constexpr char const* dip·viewer·Manager·screenSize·C = "Returns the size of the screen in pixels."; constexpr char const* dip·viewer·Manager·swapBuffers·Window·P = "Swap display buffers."; constexpr char const* dip·viewer·Manager·setWindowTitle·Window·P·char·CP = "Sets a Window's title."; constexpr char const* dip·viewer·Manager·refreshWindow·Window·P = "Refresh a Window's contents."; @@ -1970,7 +1971,7 @@ constexpr char const* dip·GaussianLineClip·Image·CL·Image·L·Image·Pixel· constexpr char const* dip·FillDelta·Image·L·String·CL = "Fills an image with a delta function."; constexpr char const* dip·CreateDelta·Image·L·UnsignedArray·CL·String·CL = "Creates a delta function image."; constexpr char const* dip·CreateDelta·UnsignedArray·CL·String·CL = "Overload for the function above, which takes image sizes instead of an image."; -constexpr char const* dip·CreateGauss·Image·L·FloatArray·CL·UnsignedArray··dfloat··UnsignedArray· = "Creates a Gaussian kernel."; +constexpr char const* dip·CreateGauss·Image·L·FloatArray·CL·UnsignedArray··dfloat··UnsignedArray··String·CL = "Creates a Gaussian kernel."; constexpr char const* dip·CreateGabor·Image·L·FloatArray·CL·FloatArray·CL·dfloat· = "Creates a Gabor kernel."; constexpr char const* dip·FTEllipsoid·Image·L·FloatArray··dfloat· = "Generates the Fourier transform of an ellipsoid."; constexpr char const* dip·FTEllipsoid·UnsignedArray·CL·FloatArray··dfloat· = "Overload for the function above, which takes image sizes instead of an image."; diff --git a/pydip/src/documentation_urls.py b/pydip/src/documentation_urls.py index 7f72d0ef..f6ee08d1 100644 --- a/pydip/src/documentation_urls.py +++ b/pydip/src/documentation_urls.py @@ -148,6 +148,7 @@ ('dip.viewer.Manager.activeWindows', 'dip-viewer-Manager.html#dip-viewer-Manager-activeWindows'), ('dip.viewer.Manager.destroyWindows', 'dip-viewer-Manager.html#dip-viewer-Manager-destroyWindows'), ('dip.viewer.Manager.processEvents', 'dip-viewer-Manager.html#dip-viewer-Manager-processEvents'), + ('dip.viewer.Manager.screenSize', 'dip-viewer-Manager.html#dip-viewer-Manager-screenSize-C'), ('dip.viewer.Manager.swapBuffers', 'dip-viewer-Manager.html#dip-viewer-Manager-swapBuffers-Window-P'), ('dip.viewer.Manager.setWindowTitle', 'dip-viewer-Manager.html#dip-viewer-Manager-setWindowTitle-Window-P-char-CP'), ('dip.viewer.Manager.refreshWindow', 'dip-viewer-Manager.html#dip-viewer-Manager-refreshWindow-Window-P'), @@ -2012,7 +2013,7 @@ ('dip.FillDelta', 'generation_test.html#dip-FillDelta-Image-L-String-CL'), ('dip.CreateDelta', 'generation_test.html#dip-CreateDelta-Image-L-UnsignedArray-CL-String-CL'), ('dip.CreateDelta', 'generation_test.html#dip-CreateDelta-UnsignedArray-CL-String-CL'), - ('dip.CreateGauss', 'generation_test.html#dip-CreateGauss-Image-L-FloatArray-CL-UnsignedArray--dfloat--UnsignedArray-'), + ('dip.CreateGauss', 'generation_test.html#dip-CreateGauss-Image-L-FloatArray-CL-UnsignedArray--dfloat--UnsignedArray--String-CL'), ('dip.CreateGabor', 'generation_test.html#dip-CreateGabor-Image-L-FloatArray-CL-FloatArray-CL-dfloat-'), ('dip.FTEllipsoid', 'generation_test.html#dip-FTEllipsoid-Image-L-FloatArray--dfloat-'), ('dip.FTEllipsoid', 'generation_test.html#dip-FTEllipsoid-UnsignedArray-CL-FloatArray--dfloat-'), diff --git a/pydip/src/generation.cpp b/pydip/src/generation.cpp index 6786963d..b0334cd5 100644 --- a/pydip/src/generation.cpp +++ b/pydip/src/generation.cpp @@ -105,10 +105,10 @@ void init_generation( py::module& m ) { "sizes"_a, "origin"_a = "", doc_strings::dip·CreateDelta·UnsignedArray·CL·String·CL ); m.def( "CreateDelta", py::overload_cast< dip::Image&, dip::UnsignedArray const&, dip::String const& >( &dip::CreateDelta ), py::kw_only(), "out"_a, "sizes"_a, "origin"_a = "", doc_strings::dip·CreateDelta·Image·L·UnsignedArray·CL·String·CL ); - m.def( "CreateGauss", py::overload_cast< dip::FloatArray const&, dip::UnsignedArray, dip::dfloat, dip::UnsignedArray >( &dip::CreateGauss ), - "sigmas"_a, "order"_a = dip::UnsignedArray{ 0 }, "truncation"_a = 3.0, "exponents"_a = dip::UnsignedArray{ 0 }, doc_strings::dip·CreateGauss·Image·L·FloatArray·CL·UnsignedArray··dfloat··UnsignedArray· ); - m.def( "CreateGauss", py::overload_cast< dip::Image&, dip::FloatArray const&, dip::UnsignedArray, dip::dfloat, dip::UnsignedArray >( &dip::CreateGauss ), - py::kw_only(), "out"_a, "sigmas"_a, "order"_a = dip::UnsignedArray{ 0 }, "truncation"_a = 3.0, "exponents"_a = dip::UnsignedArray{ 0 }, doc_strings::dip·CreateGauss·Image·L·FloatArray·CL·UnsignedArray··dfloat··UnsignedArray· ); + m.def( "CreateGauss", py::overload_cast< dip::FloatArray const&, dip::UnsignedArray, dip::dfloat, dip::UnsignedArray, dip::String const& >( &dip::CreateGauss ), + "sigmas"_a, "order"_a = dip::UnsignedArray{ 0 }, "truncation"_a = 3.0, "exponents"_a = dip::UnsignedArray{ 0 }, "extent"_a = "full", doc_strings::dip·CreateGauss·Image·L·FloatArray·CL·UnsignedArray··dfloat··UnsignedArray··String·CL ); + m.def( "CreateGauss", py::overload_cast< dip::Image&, dip::FloatArray const&, dip::UnsignedArray, dip::dfloat, dip::UnsignedArray, dip::String const& >( &dip::CreateGauss ), + py::kw_only(), "out"_a, "sigmas"_a, "order"_a = dip::UnsignedArray{ 0 }, "truncation"_a = 3.0, "exponents"_a = dip::UnsignedArray{ 0 }, "extent"_a = "full", doc_strings::dip·CreateGauss·Image·L·FloatArray·CL·UnsignedArray··dfloat··UnsignedArray··String·CL ); m.def( "CreateGabor", py::overload_cast< dip::FloatArray const&, dip::FloatArray const&, dip::dfloat >( &dip::CreateGabor ), "sigmas"_a, "frequencies"_a, "truncation"_a = 3.0, doc_strings::dip·CreateGabor·Image·L·FloatArray·CL·FloatArray·CL·dfloat· ); m.def( "CreateGabor", py::overload_cast< dip::Image&, dip::FloatArray const&, dip::FloatArray const&, dip::dfloat >( &dip::CreateGabor ), diff --git a/src/linear/gauss.cpp b/src/linear/gauss.cpp index c36c2f14..5500a902 100644 --- a/src/linear/gauss.cpp +++ b/src/linear/gauss.cpp @@ -50,8 +50,7 @@ inline dip::uint HalfGaussianSize( return clamp_cast< dip::uint >( std::ceil( truncation * sigma )); } -// Creates a half Gaussian kernel, with the x=0 at the right end (last element) of the output array. -std::vector< dfloat > MakeHalfGaussian( +std::vector< dfloat > MakeHalfGaussianInternal( dfloat sigma, dip::uint derivativeOrder, dfloat truncation, @@ -148,6 +147,23 @@ std::vector< dfloat > MakeHalfGaussian( } // namespace +// Creates a half Gaussian kernel, with the x=0 at the right end (last element) of the output array. +std::vector< dfloat > MakeHalfGaussian( + dfloat sigma, + dip::uint derivativeOrder, + dfloat truncation, + DataType dt +) { + // Handle sigma == 0.0 + if( sigma == 0.0 ) { + return { 1.0 }; + } + // Create half Gaussian + std::vector< dfloat > gaussian; + DIP_STACK_TRACE_THIS( gaussian = MakeHalfGaussianInternal( sigma, derivativeOrder, truncation, dt )); + return gaussian; +} + // Create 1D full Gaussian std::vector< dfloat > MakeGaussian( dfloat sigma, @@ -161,7 +177,7 @@ std::vector< dfloat > MakeGaussian( } // Create half Gaussian std::vector< dfloat > gaussian; - DIP_STACK_TRACE_THIS( gaussian = MakeHalfGaussian( sigma, derivativeOrder, truncation, dt )); + DIP_STACK_TRACE_THIS( gaussian = MakeHalfGaussianInternal( sigma, derivativeOrder, truncation, dt )); dip::uint halfFilterSize = gaussian.size() - 1; // Complete the Gaussian gaussian.resize( halfFilterSize * 2 + 1 ); @@ -177,22 +193,27 @@ void CreateGauss( FloatArray const& sigmas, UnsignedArray orders, dfloat truncation, - UnsignedArray exponents + UnsignedArray exponents, + String const& extent ) { // Verify dimensionality dip::uint nDims = sigmas.size(); DIP_STACK_TRACE_THIS( ArrayUseParameter( orders, nDims, dip::uint( 0 ))); DIP_STACK_TRACE_THIS( ArrayUseParameter( exponents, nDims, dip::uint( 0 ))); + bool full{}; + DIP_STACK_TRACE_THIS( full = BooleanFromString( extent, "full", "half" )); + // Create 1D gaussian for each dimension std::vector< std::vector< dfloat >> gaussians( nDims ); UnsignedArray outSizes( nDims ); UnsignedArray centers( nDims ); + for( dip::uint ii = 0; ii < nDims; ++ii ) { - DIP_STACK_TRACE_THIS( gaussians[ ii ] = MakeGaussian( sigmas[ ii ], orders[ ii ], truncation, DT_DFLOAT )); + DIP_STACK_TRACE_THIS( gaussians[ ii ] = full ? MakeGaussian( sigmas[ ii ], orders[ ii ], truncation, DT_DFLOAT ) : MakeHalfGaussian( sigmas[ ii ], orders[ ii ], truncation, DT_DFLOAT )); dip::uint gaussianLength = gaussians[ ii ].size(); outSizes[ ii ] = gaussianLength; - centers[ ii ] = ( gaussianLength - 1 ) / 2; + centers[ ii ] = full ? ( gaussianLength - 1 ) / 2 : gaussianLength - 1; } // Create output image