Skip to content

Commit 61efa75

Browse files
authored
Merge pull request #440 from clEsperanto/hessian_gaussian_derivative
Ridge detector and some other functions
2 parents 9c77627 + ecd7cb3 commit 61efa75

25 files changed

+792
-157
lines changed

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
cmake_minimum_required(VERSION 3.20)
22

3-
project(CLIc VERSION 0.17.1)
3+
project(CLIc VERSION 0.18.0)
44

5-
set(kernel_version_tag "3.2.0" CACHE STRING "clEsperanto kernel version tag") # "3.1.1"
5+
set(kernel_version_tag "3.3.0" CACHE STRING "clEsperanto kernel version tag")
66
set(eigen_lib_version_tag "3.4.0" CACHE STRING "Eigen library version tag")
77

88
# if not set, set the default build type to Release

clic/include/execution.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,29 @@ execute_separable(const Device::Pointer & device,
5151
const std::array<float, 3> & sigma,
5252
const std::array<int, 3> & radius) -> void;
5353

54+
/**
55+
* @brief Execution function for separable kernel operation (with with extra parameters)
56+
*
57+
* This is currently only used for Gaussian derivative kernels.
58+
* to be fused / generalized with the previous function in future
59+
*
60+
* @param device Device pointer
61+
* @param kernel Kernel function name and code
62+
* @param src Source array
63+
* @param dst Destination array
64+
* @param sigma Sigma value for the kernel
65+
* @param radius Radius value for the kernel
66+
* @param orders Orders for the kernel operation
67+
*/
68+
auto
69+
execute_separable(const Device::Pointer & device,
70+
const KernelInfo & kernel,
71+
const Array::Pointer & src,
72+
const Array::Pointer & dst,
73+
const std::array<float, 3> & sigma,
74+
const std::array<int, 3> & radius,
75+
const std::array<int, 3> & orders) -> void;
76+
5477
/**
5578
* @brief Execute a kernel using native framework
5679
* @param device Device pointer

clic/include/tier1.hpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,36 @@ gaussian_blur_func(const Device::Pointer & device,
745745
float sigma_y,
746746
float sigma_z) -> Array::Pointer;
747747

748+
/**
749+
* @name gaussian_derivative
750+
* @brief Convolve the image with a gaussian derivate. The filter kernel can have nonisotropic sigma and order.
751+
* The implementation is done separable. In case a sigma equals zero, the direction is not filtered.
752+
* If all orders are zero, the filtering is equivalent to a Gaussian blur.
753+
*
754+
* @param device Device to perform the operation on. [const Device::Pointer &]
755+
* @param src Input image to process. [const Array::Pointer &]
756+
* @param dst Output result image. [Array::Pointer ( = None )]
757+
* @param sigma_x Sigma value along the x axis. [float ( = 0 )]
758+
* @param sigma_y Sigma value along the y axis. [float ( = 0 )]
759+
* @param sigma_z Sigma value along the z axis. [float ( = 0 )]
760+
* @param order_x Order of derivation along the x axis. [int ( = 0 )]
761+
* @param order_y Order of derivation along the y axis. [int ( = 0 )]
762+
* @param order_z Order of derivation along the z axis. [int ( = 0 )]
763+
* @return Array::Pointer
764+
*
765+
* @note 'filter', 'in assistant'
766+
*/
767+
auto
768+
gaussian_derivative_func(const Device::Pointer & device,
769+
const Array::Pointer & src,
770+
Array::Pointer dst,
771+
float sigma_x,
772+
float sigma_y,
773+
float sigma_z,
774+
int order_x,
775+
int order_y,
776+
int order_z) -> Array::Pointer;
777+
748778

749779
/**
750780
* @name generate_distance_matrix
@@ -906,10 +936,14 @@ greater_or_equal_constant_func(const Device::Pointer & device,
906936
/**
907937
* @name hessian_eigenvalues
908938
* @brief Computes the eigenvalues of the hessian matrix of a 2d or 3d image. Hessian matrix or 2D images: [Ixx, Ixy]
909-
* [Ixy, Iyy] Hessian matrix for 3D images: [Ixx, Ixy, Ixz] [Ixy, Iyy, Iyz] [Ixz, Iyz, Izz] Ixx denotes the second
939+
* [Ixy, Iyy] Hessian matrix for 3D images: [Ixx, Ixy, Ixz] [Ixy, Iyy, Iyz] [Ixz, Iyz, Izz], Ixx denotes the second
910940
* derivative in x. Ixx and Iyy are calculated by convolving the image with the 1d kernel [1 2 1]. Ixy is calculated by
911-
* a convolution with the 2d kernel: [ 0.25 0 0.25] [ 0 0 0] [0.25 0 0.25] Note: This is the only clesperanto function
912-
* that returns multiple images. This API might be subject to change in the future. Consider using
941+
* a convolution with the 2d kernel: [ 0.25 0 0.25] [ 0 0 0] [0.25 0 0.25]
942+
*
943+
* The function return the list of eigenvalues as images, by decreasing order. The first image is the largest
944+
* eigenvalue,
945+
*
946+
* Note: This function returns multiple images. This API might be subject to change in the future. Consider using
913947
* small_hessian_eigenvalue() and/or large_hessian_eigenvalue() instead which return only one image.
914948
*
915949
* @param device Device to perform the operation on. [const Device::Pointer &]

clic/include/tier2.hpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,9 @@ top_hat_func(const Device::Pointer & device,
11051105

11061106
/**
11071107
* @name extended_depth_of_focus_variance_projection
1108-
* @brief Extended depth of focus projection maximizing local pixel intensity variance.
1108+
* @brief Depth projection using the local variance maxima to determine the best focus plane.
1109+
* The radius parameter control the local variance calculation, and the sigma apply a
1110+
* gaussian blur for smoothness of the projection.
11091111
*
11101112
* @param device Device to perform the operation on. [const Device::Pointer &]
11111113
* @param src Input image to process. [const Array::Pointer &]
@@ -1125,6 +1127,51 @@ extended_depth_of_focus_variance_projection_func(const Device::Pointer & device,
11251127
float radius_y,
11261128
float sigma) -> Array::Pointer;
11271129

1130+
/**
1131+
* @name extended_depth_of_focus_sobel_projection
1132+
* @brief Depth projection using the local sobel gradient magnitude maxima to determine the best focus plane.
1133+
* The sigma apply a gaussian blur for smoothness of the projection.
1134+
*
1135+
* @param device Device to perform the operation on. [const Device::Pointer &]
1136+
* @param src Input image to process. [const Array::Pointer &]
1137+
* @param dst Output result image. [Array::Pointer ( = None )]
1138+
* @param sigma Sigma for Gaussian blur. [float ( = 5 )]
1139+
* @return Array::Pointer
1140+
*
1141+
* @note 'projection'
1142+
*/
1143+
auto
1144+
extended_depth_of_focus_sobel_projection_func(const Device::Pointer & device,
1145+
const Array::Pointer & src,
1146+
Array::Pointer dst,
1147+
float sigma) -> Array::Pointer;
1148+
1149+
/**
1150+
* @name hessian_gaussian_eigenvalues
1151+
* @brief Determines the Hessian matrix eigenvalues using the gaussian derivative method and returns the small,
1152+
* middle and large eigenvalue images.
1153+
*
1154+
* The function return the list of eigenvalues as images, by decreasing order. The first image is the largest
1155+
* eigenvalue,
1156+
*
1157+
* @param device Device to perform the operation on. [const Device::Pointer &]
1158+
* @param src Input image to process. [const Array::Pointer &]
1159+
* @param small_eigenvalue Output result image for the small eigenvalue. [Array::Pointer ( = None )]
1160+
* @param middle_eigenvalue Output result image for the middle eigenvalue. [Array::Pointer ( = None )]
1161+
* @param large_eigenvalue Output result image for the large eigenvalue. [Array::Pointer ( = None )]
1162+
* @param sigma Sigma of the Gaussian kernel. [float ( = 1 )]
1163+
* @return std::vector<Array::Pointer>
1164+
*
1165+
*/
1166+
auto
1167+
hessian_gaussian_eigenvalues_func(const Device::Pointer & device,
1168+
const Array::Pointer & src,
1169+
Array::Pointer small_eigenvalue,
1170+
Array::Pointer middle_eigenvalue,
1171+
Array::Pointer large_eigenvalue,
1172+
float sigma) -> std::vector<Array::Pointer>;
1173+
1174+
11281175
} // namespace cle::tier2
11291176

11301177
#endif // __INCLUDE_TIER2_HPP

clic/include/tier3.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,30 @@ statistics_of_background_and_labelled_pixels_func(const Device::Pointer & device
419419
Array::Pointer intensity,
420420
Array::Pointer label) -> StatisticsMap;
421421

422+
/**
423+
* @name sato_filter
424+
* @brief Applies the multi-scale ridge detection Sato filter.
425+
* This filter was first introduced by Sato et al. in 1998 (https://doi.org/10.1016/S1361-8415(98)80009-1)
426+
*
427+
* @param device Device to perform the operation on. [const Device::Pointer &]
428+
* @param src Input image to process. [const Array::Pointer &]
429+
* @param dst Output result image. [Array::Pointer ( = None )]
430+
* @param sigma_minimum Minimum sigma value for the filter. [float ( = 1 )]
431+
* @param sigma_maximum Maximum sigma value for the filter. [float ( = 3 )]
432+
* @param sigma_step Step size for the sigma values. [float ( = 1 )]
433+
* @return Array::Pointer
434+
*
435+
* @note 'filter', 'in assistant'
436+
* @see https://doi.org/10.1016/S1361-8415(98)80009-1
437+
*/
438+
auto
439+
sato_filter_func(const Device::Pointer & device,
440+
const Array::Pointer & src,
441+
Array::Pointer dst,
442+
float sigma_minimum,
443+
float sigma_maximum,
444+
float sigma_step) -> Array::Pointer;
445+
422446
} // namespace cle::tier3
423447

424448
#endif // __INCLUDE_TIER3_HPP

clic/include/tier4.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,20 @@ standard_deviation_intensity_map_func(const Device::Pointer & device,
409409
Array::Pointer dst) -> Array::Pointer;
410410

411411

412+
/**
413+
* @name percentile
414+
* @brief Computes the percentile value of an image.
415+
*
416+
* @param device Device to perform the operation on. [const Device::Pointer &]
417+
* @param src Input image. [const Array::Pointer &]
418+
* @param percentile Percentile to compute. [float ( = 50.0 )]
419+
* @return float
420+
*
421+
*/
422+
auto
423+
percentile_func(const Device::Pointer & device, const Array::Pointer & src, const float percentile) -> float;
424+
425+
412426
} // namespace cle::tier4
413427

414428
#endif // __INCLUDE_TIER4_HPP

clic/src/execution.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,42 @@ execute(const Device::Pointer & device,
277277
}
278278

279279

280+
auto
281+
execute_separable(const Device::Pointer & device,
282+
const KernelInfo & kernel,
283+
const Array::Pointer & src,
284+
const Array::Pointer & dst,
285+
const std::array<float, 3> & sigma,
286+
const std::array<int, 3> & radius,
287+
const std::array<int, 3> & orders) -> void
288+
{
289+
Array::check_ptr(src, "Error: 'src' is null. Please ensure the provided parameters before calling this function.");
290+
Array::check_ptr(dst, "Error: 'dst' is null. Please ensure the provided parameters before calling this function.");
291+
292+
const RangeArray global_range = { dst->width(), dst->height(), dst->depth() };
293+
294+
auto tmp1 = Array::create(dst);
295+
auto tmp2 = Array::create(dst);
296+
297+
auto execute_if_needed = [&](int dim, int idx, auto & input, auto & output) {
298+
if (dim > 1 && sigma[idx] > 0)
299+
{
300+
const ParameterList parameters = { { "src", input }, { "dst", output }, { "dim", idx },
301+
{ "N", radius[idx] }, { "s", sigma[idx] }, { "order", orders[idx] } };
302+
execute(device, kernel, parameters, global_range);
303+
}
304+
else
305+
{
306+
input->copyTo(output);
307+
}
308+
};
309+
310+
execute_if_needed(dst->width(), 0, src, tmp1);
311+
execute_if_needed(dst->height(), 1, tmp1, tmp2);
312+
execute_if_needed(dst->depth(), 2, tmp2, dst);
313+
}
314+
315+
280316
auto
281317
execute_separable(const Device::Pointer & device,
282318
const KernelInfo & kernel,

clic/src/tier1/gaussian_blur.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "utils.hpp"
55

66
#include "cle_gaussian_blur_separable.h"
7+
#include "cle_gaussian_derivative_separable.h"
78

89
namespace cle::tier1
910
{
@@ -29,11 +30,42 @@ gaussian_blur_func(const Device::Pointer & device,
2930
temp,
3031
dst,
3132
{ sigma_x, sigma_y, sigma_z },
32-
{ sigma2kernelsize(sigma_x), sigma2kernelsize(sigma_y), sigma2kernelsize(sigma_z) });
33+
{ sigma2kernelsize(sigma_x), sigma2kernelsize(sigma_y), sigma2kernelsize(sigma_z) },
34+
{ 0, 0, 0 });
3335
return dst;
3436
}
3537

36-
// generate_angle_matrix_func
37-
// generate_binary_overlap_matrix_func
38+
auto
39+
gaussian_derivative_func(const Device::Pointer & device,
40+
const Array::Pointer & src,
41+
Array::Pointer dst,
42+
float sigma_x,
43+
float sigma_y,
44+
float sigma_z,
45+
int order_x,
46+
int order_y,
47+
int order_z) -> Array::Pointer
48+
{
49+
tier0::create_like(src, dst, dType::FLOAT);
50+
Array::Pointer temp = src;
51+
if (temp->dtype() != dType::FLOAT)
52+
{
53+
temp = Array::create(dst);
54+
tier1::copy_func(device, src, temp);
55+
}
56+
57+
constexpr int truncate = 8;
58+
std::array<float, 3> sigmas = { std::max({ sigma_x, 0.0f }),
59+
std::max({ sigma_y, 0.0f }),
60+
std::max({ sigma_z, 0.0f }) };
61+
std::array<int, 3> radii = { static_cast<int>(truncate * sigmas[0] + 0.5f),
62+
static_cast<int>(truncate * sigmas[1] + 0.5f),
63+
static_cast<int>(truncate * sigmas[2] + 0.5f) };
64+
std::array<int, 3> orders = { std::min(order_x, 2), std::min(order_y, 2), std::min(order_z, 2) };
65+
66+
const KernelInfo kernel = { "gaussian_derivative_separable", kernel::gaussian_derivative_separable };
67+
execute_separable(device, kernel, temp, dst, sigmas, radii, orders);
68+
return dst;
69+
}
3870

3971
} // namespace cle::tier1

clic/src/tier1/hessian_eigenvalues.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ hessian_eigenvalues_func(const Device::Pointer & device,
3636
execute(device, kernel, params, range);
3737
if (src->depth() == 1)
3838
{
39-
return { small_eigenvalue, large_eigenvalue };
39+
return { large_eigenvalue, small_eigenvalue };
4040
}
41-
return { small_eigenvalue, middle_eigenvalue, large_eigenvalue };
41+
return { large_eigenvalue, middle_eigenvalue, small_eigenvalue };
4242
}
4343

4444
// inferior_superior(const Device::Pointer & device, const Array::Pointer & src, Array::Pointer dst) -> Array::Pointer

clic/src/tier1/maximum_filter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ maximum_filter_func(const Device::Pointer & device,
3939
src,
4040
dst,
4141
{ static_cast<float>(radius_x), static_cast<float>(radius_y), static_cast<float>(radius_z) },
42-
{ r_x, r_y, r_z });
42+
{ r_x, r_y, r_z },
43+
{ 0, 0, 0 });
4344
}
4445
return dst;
4546
}

0 commit comments

Comments
 (0)