From f122e4c4970e3b4818f72b0a89f86d74027360eb Mon Sep 17 00:00:00 2001 From: Philippe Blanc
Date: Fri, 29 Aug 2025 12:14:57 +0200 Subject: [PATCH 1/4] expose internally MakeHalfGaussian and update CreateGauss to only generate half of a Gaussian kernel --- include/diplib/generation.h | 26 ++++++++++++++++++++++---- src/linear/gauss.cpp | 32 +++++++++++++++++++++++++------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/include/diplib/generation.h b/include/diplib/generation.h index b25658f0..cf61e568 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,24 +667,29 @@ 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`. /// +/// Can be used to generate only the first half of the gaussian and its derivative. +/// 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. +/// Convolving an image with the result of this function is much less efficient than calling \ref Gauss. // Defined in src/linear/gauss.cpp DIP_EXPORT void CreateGauss( Image& out, FloatArray const& sigmas, UnsignedArray derivativeOrder = { 0 }, dfloat truncation = 3.0, - UnsignedArray exponents = { 0 } + UnsignedArray exponents = { 0 }, + bool full = true ); DIP_NODISCARD inline Image CreateGauss( FloatArray const& sigmas, UnsignedArray derivativeOrder = { 0 }, dfloat truncation = 3.0, - UnsignedArray exponents = { 0 } + UnsignedArray exponents = { 0 }, + bool full = true ) { Image out; - CreateGauss( out, sigmas, std::move( derivativeOrder ), truncation, std::move( exponents )); + CreateGauss( out, sigmas, std::move( derivativeOrder ), truncation, std::move( exponents ), full); return out; } diff --git a/src/linear/gauss.cpp b/src/linear/gauss.cpp index c36c2f14..e56929b9 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 > MakeHalfGaussian_( 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 = MakeHalfGaussian_( 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 = MakeHalfGaussian_( sigma, derivativeOrder, truncation, dt )); dip::uint halfFilterSize = gaussian.size() - 1; // Complete the Gaussian gaussian.resize( halfFilterSize * 2 + 1 ); @@ -177,7 +193,8 @@ void CreateGauss( FloatArray const& sigmas, UnsignedArray orders, dfloat truncation, - UnsignedArray exponents + UnsignedArray exponents, + bool full ) { // Verify dimensionality dip::uint nDims = sigmas.size(); @@ -188,13 +205,14 @@ void CreateGauss( 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 out.ReForge( outSizes, 1, DT_DFLOAT ); ImageIterator< dfloat > itOut( out ); From 49146e2ff29138619d3b53017a5a1cb27168f04f Mon Sep 17 00:00:00 2001 From: Philippe Blanc
Date: Fri, 29 Aug 2025 20:05:33 +0200
Subject: [PATCH 2/4] use a better name for MakeHalfGaussian_, use a string
instead of a bool in CreateGauss, udate pydip signature for CreateGauss
---
include/diplib/generation.h | 6 +++---
pydip/src/generation.cpp | 4 ++--
src/linear/gauss.cpp | 13 ++++++++-----
3 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/include/diplib/generation.h b/include/diplib/generation.h
index cf61e568..a86634d8 100644
--- a/include/diplib/generation.h
+++ b/include/diplib/generation.h
@@ -679,17 +679,17 @@ DIP_EXPORT void CreateGauss(
UnsignedArray derivativeOrder = { 0 },
dfloat truncation = 3.0,
UnsignedArray exponents = { 0 },
- bool full = true
+ String const& extent = "full"
);
DIP_NODISCARD inline Image CreateGauss(
FloatArray const& sigmas,
UnsignedArray derivativeOrder = { 0 },
dfloat truncation = 3.0,
UnsignedArray exponents = { 0 },
- bool full = true
+ String const& extent = "full"
) {
Image out;
- CreateGauss( out, sigmas, std::move( derivativeOrder ), truncation, std::move( exponents ), full);
+ CreateGauss( out, sigmas, std::move( derivativeOrder ), truncation, std::move( exponents ), extent );
return out;
}
diff --git a/pydip/src/generation.cpp b/pydip/src/generation.cpp
index 6786963d..60320fe6 100644
--- a/pydip/src/generation.cpp
+++ b/pydip/src/generation.cpp
@@ -105,9 +105,9 @@ 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 ),
+ 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 }, 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 ),
+ 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 }, doc_strings::dip·CreateGauss·Image·L·FloatArray·CL·UnsignedArray··dfloat··UnsignedArray· );
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· );
diff --git a/src/linear/gauss.cpp b/src/linear/gauss.cpp
index e56929b9..5500a902 100644
--- a/src/linear/gauss.cpp
+++ b/src/linear/gauss.cpp
@@ -50,7 +50,7 @@ inline dip::uint HalfGaussianSize(
return clamp_cast< dip::uint >( std::ceil( truncation * sigma ));
}
-std::vector< dfloat > MakeHalfGaussian_(
+std::vector< dfloat > MakeHalfGaussianInternal(
dfloat sigma,
dip::uint derivativeOrder,
dfloat truncation,
@@ -160,7 +160,7 @@ std::vector< dfloat > MakeHalfGaussian(
}
// 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 ));
return gaussian;
}
@@ -177,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 );
@@ -194,13 +194,16 @@ void CreateGauss(
UnsignedArray orders,
dfloat truncation,
UnsignedArray exponents,
- bool full
+ 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 );
@@ -212,7 +215,7 @@ void CreateGauss(
outSizes[ ii ] = gaussianLength;
centers[ ii ] = full ? ( gaussianLength - 1 ) / 2 : gaussianLength - 1;
}
-
+
// Create output image
out.ReForge( outSizes, 1, DT_DFLOAT );
ImageIterator< dfloat > itOut( out );
From 0397d8df7a417690274ac5505398d4b83f9088da Mon Sep 17 00:00:00 2001
From: Cris Luengo
Date: Sat, 30 Aug 2025 18:34:40 +0200
Subject: [PATCH 4/4] update m.def missed the correct function signature for
extent, still unsure about doc_string
---
pydip/src/generation.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pydip/src/generation.cpp b/pydip/src/generation.cpp
index 60320fe6..d4169f5e 100644
--- a/pydip/src/generation.cpp
+++ b/pydip/src/generation.cpp
@@ -106,9 +106,9 @@ void init_generation( py::module& m ) {
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::String const& >( &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· );
+ "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· );
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 }, doc_strings::dip·CreateGauss·Image·L·FloatArray·CL·UnsignedArray··dfloat··UnsignedArray· );
+ 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· );
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 ),