andrea-dev #1
@@ -90,7 +90,8 @@ struct Voxel {
|
|||||||
} // namespace Interface
|
} // namespace Interface
|
||||||
|
|
||||||
struct Voxel {
|
struct Voxel {
|
||||||
Scalarf Value;
|
Scalarf Value = 0.0f;
|
||||||
|
Scalari Count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
namespace uLib {
|
namespace uLib {
|
||||||
|
|
||||||
#ifdef USE_CUDA
|
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||||
template <typename VoxelT>
|
template <typename VoxelT>
|
||||||
__global__ void ABTrimFilterKernel(const VoxelT *in, VoxelT *out,
|
__global__ void ABTrimFilterKernel(const VoxelT *in, VoxelT *out,
|
||||||
const VoxelT *kernel, int vox_size,
|
const VoxelT *kernel, int vox_size,
|
||||||
@@ -108,7 +108,7 @@ public:
|
|||||||
mBtrim = 0;
|
mBtrim = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_CUDA
|
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||||
void Run() {
|
void Run() {
|
||||||
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
||||||
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
||||||
@@ -206,7 +206,7 @@ public:
|
|||||||
mBtrim = 0;
|
mBtrim = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_CUDA
|
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||||
void Run() {
|
void Run() {
|
||||||
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
||||||
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
namespace uLib {
|
namespace uLib {
|
||||||
|
|
||||||
#ifdef USE_CUDA
|
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||||
template <typename VoxelT>
|
template <typename VoxelT>
|
||||||
__global__ void LinearFilterKernel(const VoxelT *in, VoxelT *out,
|
__global__ void LinearFilterKernel(const VoxelT *in, VoxelT *out,
|
||||||
const VoxelT *kernel, int vox_size,
|
const VoxelT *kernel, int vox_size,
|
||||||
@@ -66,7 +66,7 @@ public:
|
|||||||
typedef VoxImageFilter<VoxelT, VoxFilterAlgorithmLinear<VoxelT>> BaseClass;
|
typedef VoxImageFilter<VoxelT, VoxFilterAlgorithmLinear<VoxelT>> BaseClass;
|
||||||
VoxFilterAlgorithmLinear(const Vector3i &size) : BaseClass(size) {}
|
VoxFilterAlgorithmLinear(const Vector3i &size) : BaseClass(size) {}
|
||||||
|
|
||||||
#ifdef USE_CUDA
|
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||||
void Run() {
|
void Run() {
|
||||||
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
||||||
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ set(SOURCES
|
|||||||
module.cpp
|
module.cpp
|
||||||
core_bindings.cpp
|
core_bindings.cpp
|
||||||
math_bindings.cpp
|
math_bindings.cpp
|
||||||
|
math_filters_bindings.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use pybind11 to add the python module
|
# Use pybind11 to add the python module
|
||||||
pybind11_add_module(uLib_python module.cpp core_bindings.cpp math_bindings.cpp)
|
pybind11_add_module(uLib_python module.cpp core_bindings.cpp math_bindings.cpp math_filters_bindings.cpp)
|
||||||
|
|
||||||
# Link against our C++ libraries
|
# Link against our C++ libraries
|
||||||
target_link_libraries(uLib_python PRIVATE
|
target_link_libraries(uLib_python PRIVATE
|
||||||
@@ -49,4 +50,9 @@ if(BUILD_TESTING)
|
|||||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/math_pybind_test.py)
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/math_pybind_test.py)
|
||||||
set_tests_properties(pybind_math PROPERTIES
|
set_tests_properties(pybind_math PROPERTIES
|
||||||
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>:${PROJECT_SOURCE_DIR}/src/Python")
|
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>:${PROJECT_SOURCE_DIR}/src/Python")
|
||||||
|
|
||||||
|
add_test(NAME pybind_math_filters
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/math_filters_test.py)
|
||||||
|
set_tests_properties(pybind_math_filters PROPERTIES
|
||||||
|
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>:${PROJECT_SOURCE_DIR}/src/Python")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -327,7 +327,8 @@ void init_math(py::module_ &m) {
|
|||||||
// 6. High-level Structures
|
// 6. High-level Structures
|
||||||
py::class_<Voxel>(m, "Voxel")
|
py::class_<Voxel>(m, "Voxel")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def_readwrite("Value", &Voxel::Value);
|
.def_readwrite("Value", &Voxel::Value)
|
||||||
|
.def_readwrite("Count", &Voxel::Count);
|
||||||
|
|
||||||
py::class_<Abstract::VoxImage, StructuredGrid>(m, "AbstractVoxImage")
|
py::class_<Abstract::VoxImage, StructuredGrid>(m, "AbstractVoxImage")
|
||||||
.def("GetValue", py::overload_cast<const Vector3i &>(&Abstract::VoxImage::GetValue, py::const_))
|
.def("GetValue", py::overload_cast<const Vector3i &>(&Abstract::VoxImage::GetValue, py::const_))
|
||||||
|
|||||||
100
src/Python/math_filters_bindings.cpp
Normal file
100
src/Python/math_filters_bindings.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/eigen.h>
|
||||||
|
#include <pybind11/stl.h>
|
||||||
|
|
||||||
|
#include "Math/VoxImage.h"
|
||||||
|
#include "Math/VoxImageFilter.h"
|
||||||
|
#include "Math/VoxImageFilterLinear.hpp"
|
||||||
|
#include "Math/VoxImageFilterABTrim.hpp"
|
||||||
|
#include "Math/VoxImageFilterBilateral.hpp"
|
||||||
|
#include "Math/VoxImageFilterThreshold.hpp"
|
||||||
|
#include "Math/VoxImageFilterMedian.hpp"
|
||||||
|
#include "Math/VoxImageFilter2ndStat.hpp"
|
||||||
|
#include "Math/VoxImageFilterCustom.hpp"
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
using namespace uLib;
|
||||||
|
|
||||||
|
template <typename Algorithm>
|
||||||
|
void bind_common_filter(py::class_<Algorithm, Abstract::VoxImageFilter> &cls) {
|
||||||
|
cls.def(py::init<const Vector3i &>())
|
||||||
|
.def("Run", &Algorithm::Run)
|
||||||
|
.def("SetKernelNumericXZY", &Algorithm::SetKernelNumericXZY)
|
||||||
|
.def("GetImage", &Algorithm::GetImage, py::return_value_policy::reference_internal)
|
||||||
|
.def("SetImage", &Algorithm::SetImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_math_filters(py::module_ &m) {
|
||||||
|
|
||||||
|
// Abstract::VoxImageFilter
|
||||||
|
py::class_<Abstract::VoxImageFilter, std::unique_ptr<Abstract::VoxImageFilter, py::nodelete>>(m, "AbstractVoxImageFilter")
|
||||||
|
.def("Run", &Abstract::VoxImageFilter::Run)
|
||||||
|
.def("SetImage", &Abstract::VoxImageFilter::SetImage);
|
||||||
|
|
||||||
|
// Helper macro to define standard bindings for a filter
|
||||||
|
#define BIND_FILTER(ClassName) \
|
||||||
|
{ \
|
||||||
|
auto cls = py::class_<ClassName<Voxel>, Abstract::VoxImageFilter>(m, #ClassName); \
|
||||||
|
bind_common_filter(cls); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithmLinear
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithmLinear<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithmLinear");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithmAbtrim
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithmAbtrim<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithmAbtrim");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
cls.def("SetABTrim", &VoxFilterAlgorithmAbtrim<Voxel>::SetABTrim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithmSPR
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithmSPR<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithmSPR");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
cls.def("SetABTrim", &VoxFilterAlgorithmSPR<Voxel>::SetABTrim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithmBilateral
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithmBilateral<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithmBilateral");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
cls.def("SetIntensitySigma", &VoxFilterAlgorithmBilateral<Voxel>::SetIntensitySigma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithmBilateralTrim
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithmBilateralTrim<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithmBilateralTrim");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
cls.def("SetIntensitySigma", &VoxFilterAlgorithmBilateralTrim<Voxel>::SetIntensitySigma);
|
||||||
|
cls.def("SetABTrim", &VoxFilterAlgorithmBilateralTrim<Voxel>::SetABTrim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithmThreshold
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithmThreshold<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithmThreshold");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
cls.def("SetThreshold", &VoxFilterAlgorithmThreshold<Voxel>::SetThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithmMedian
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithmMedian<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithmMedian");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithm2ndStat
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithm2ndStat<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithm2ndStat");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoxFilterAlgorithmCustom (Omit CustomEvaluate since it uses static function ptrs)
|
||||||
|
{
|
||||||
|
auto cls = py::class_<VoxFilterAlgorithmCustom<Voxel>, Abstract::VoxImageFilter>(m, "VoxFilterAlgorithmCustom");
|
||||||
|
bind_common_filter(cls);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace py = pybind11;
|
|||||||
|
|
||||||
void init_core(py::module_ &m);
|
void init_core(py::module_ &m);
|
||||||
void init_math(py::module_ &m);
|
void init_math(py::module_ &m);
|
||||||
|
void init_math_filters(py::module_ &m);
|
||||||
|
|
||||||
PYBIND11_MODULE(uLib_python, m) {
|
PYBIND11_MODULE(uLib_python, m) {
|
||||||
m.doc() = "Python bindings for uLib Core and Math libraries";
|
m.doc() = "Python bindings for uLib Core and Math libraries";
|
||||||
@@ -15,4 +16,5 @@ PYBIND11_MODULE(uLib_python, m) {
|
|||||||
// Math submodule
|
// Math submodule
|
||||||
py::module_ math = m.def_submodule("Math", "Math library bindings");
|
py::module_ math = m.def_submodule("Math", "Math library bindings");
|
||||||
init_math(math);
|
init_math(math);
|
||||||
|
init_math_filters(math);
|
||||||
}
|
}
|
||||||
|
|||||||
151
src/Python/testing/math_filters_test.py
Normal file
151
src/Python/testing/math_filters_test.py
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import unittest
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Ensure PYTHONPATH is correct if run from root
|
||||||
|
sys.path.append(os.path.join(os.getcwd(), 'src', 'Python'))
|
||||||
|
|
||||||
|
import uLib
|
||||||
|
|
||||||
|
class TestMathFilters(unittest.TestCase):
|
||||||
|
def test_filter_creation(self):
|
||||||
|
# 1. Linear Filter
|
||||||
|
dims = [10, 10, 10]
|
||||||
|
v_dims = uLib.Math.Vector3i(dims)
|
||||||
|
|
||||||
|
linear_filter = uLib.Math.VoxFilterAlgorithmLinear(v_dims)
|
||||||
|
self.assertIsNotNone(linear_filter)
|
||||||
|
|
||||||
|
# 2. ABTrim Filter
|
||||||
|
abtrim_filter = uLib.Math.VoxFilterAlgorithmAbtrim(v_dims)
|
||||||
|
self.assertIsNotNone(abtrim_filter)
|
||||||
|
abtrim_filter.SetABTrim(1, 1)
|
||||||
|
|
||||||
|
# 3. Bilateral Filter
|
||||||
|
bilat_filter = uLib.Math.VoxFilterAlgorithmBilateral(v_dims)
|
||||||
|
self.assertIsNotNone(bilat_filter)
|
||||||
|
bilat_filter.SetIntensitySigma(0.5)
|
||||||
|
|
||||||
|
# 4. Threshold Filter
|
||||||
|
threshold_filter = uLib.Math.VoxFilterAlgorithmThreshold(v_dims)
|
||||||
|
self.assertIsNotNone(threshold_filter)
|
||||||
|
threshold_filter.SetThreshold(0.5)
|
||||||
|
|
||||||
|
# 5. Median Filter
|
||||||
|
median_filter = uLib.Math.VoxFilterAlgorithmMedian(v_dims)
|
||||||
|
self.assertIsNotNone(median_filter)
|
||||||
|
|
||||||
|
def test_filter_run(self):
|
||||||
|
# Create image
|
||||||
|
dims = [10, 10, 10]
|
||||||
|
vox_img = uLib.Math.VoxImage(dims)
|
||||||
|
for i in range(10*10*10):
|
||||||
|
vox_img.SetValue(i, 1.0)
|
||||||
|
|
||||||
|
# Linear filter
|
||||||
|
linear_filter = uLib.Math.VoxFilterAlgorithmLinear([3, 3, 3])
|
||||||
|
linear_filter.SetImage(vox_img)
|
||||||
|
|
||||||
|
# Set kernel (simple 3x3x3 all ones)
|
||||||
|
# Weights are usually normalized in linear filter logic?
|
||||||
|
# Let's just test it runs.
|
||||||
|
linear_filter.SetKernelNumericXZY([1.0] * 27)
|
||||||
|
|
||||||
|
# Run filter
|
||||||
|
linear_filter.Run()
|
||||||
|
|
||||||
|
# Value should be 1.0 (mean of all 1.0 is 1.0)
|
||||||
|
self.assertAlmostEqual(vox_img.GetValue(0), 1.0)
|
||||||
|
|
||||||
|
def test_filter_run_abtrim(self):
|
||||||
|
# Create image
|
||||||
|
dims = [10, 10, 10]
|
||||||
|
vox_img = uLib.Math.VoxImage(dims)
|
||||||
|
for i in range(10*10*10):
|
||||||
|
vox_img.SetValue(i, 1.0)
|
||||||
|
|
||||||
|
# ABTrim filter
|
||||||
|
abtrim_filter = uLib.Math.VoxFilterAlgorithmAbtrim([3, 3, 3])
|
||||||
|
abtrim_filter.SetImage(vox_img)
|
||||||
|
|
||||||
|
# Set kernel (simple 3x3x3 all ones)
|
||||||
|
# Weights are usually normalized in linear filter logic?
|
||||||
|
# Let's just test it runs.
|
||||||
|
abtrim_filter.SetKernelNumericXZY([1.0] * 27)
|
||||||
|
|
||||||
|
# Run filter
|
||||||
|
abtrim_filter.Run()
|
||||||
|
|
||||||
|
# Value should be 1.0 (mean of all 1.0 is 1.0)
|
||||||
|
self.assertAlmostEqual(vox_img.GetValue(0), 1.0)
|
||||||
|
|
||||||
|
def test_filter_run_bilateral(self):
|
||||||
|
# Create image
|
||||||
|
dims = [10, 10, 10]
|
||||||
|
vox_img = uLib.Math.VoxImage(dims)
|
||||||
|
for i in range(10*10*10):
|
||||||
|
vox_img.SetValue(i, 1.0)
|
||||||
|
|
||||||
|
# Bilateral filter
|
||||||
|
bilat_filter = uLib.Math.VoxFilterAlgorithmBilateral([3, 3, 3])
|
||||||
|
bilat_filter.SetImage(vox_img)
|
||||||
|
|
||||||
|
# Set kernel (simple 3x3x3 all ones)
|
||||||
|
# Weights are usually normalized in linear filter logic?
|
||||||
|
# Let's just test it runs.
|
||||||
|
bilat_filter.SetKernelNumericXZY([1.0] * 27)
|
||||||
|
|
||||||
|
# Run filter
|
||||||
|
bilat_filter.Run()
|
||||||
|
|
||||||
|
# Value should be 1.0 (mean of all 1.0 is 1.0)
|
||||||
|
self.assertAlmostEqual(vox_img.GetValue(0), 1.0)
|
||||||
|
|
||||||
|
def test_filter_run_threshold(self):
|
||||||
|
# Create image
|
||||||
|
dims = [10, 10, 10]
|
||||||
|
vox_img = uLib.Math.VoxImage(dims)
|
||||||
|
for i in range(10*10*10):
|
||||||
|
vox_img.SetValue(i, 1.0)
|
||||||
|
|
||||||
|
# Threshold filter
|
||||||
|
threshold_filter = uLib.Math.VoxFilterAlgorithmThreshold([3, 3, 3])
|
||||||
|
threshold_filter.SetImage(vox_img)
|
||||||
|
|
||||||
|
# Set kernel (simple 3x3x3 all ones)
|
||||||
|
# Weights are usually normalized in linear filter logic?
|
||||||
|
# Let's just test it runs.
|
||||||
|
threshold_filter.SetKernelNumericXZY([1.0] * 27)
|
||||||
|
|
||||||
|
# Run filter
|
||||||
|
threshold_filter.Run()
|
||||||
|
|
||||||
|
# Value should be 1.0 (mean of all 1.0 is 1.0)
|
||||||
|
self.assertAlmostEqual(vox_img.GetValue(0), 1.0)
|
||||||
|
|
||||||
|
def test_filter_run_median(self):
|
||||||
|
# Create image
|
||||||
|
dims = [10, 10, 10]
|
||||||
|
vox_img = uLib.Math.VoxImage(dims)
|
||||||
|
for i in range(10*10*10):
|
||||||
|
vox_img.SetValue(i, 1.0)
|
||||||
|
|
||||||
|
# Median filter
|
||||||
|
median_filter = uLib.Math.VoxFilterAlgorithmMedian([3, 3, 3])
|
||||||
|
median_filter.SetImage(vox_img)
|
||||||
|
|
||||||
|
# Set kernel (simple 3x3x3 all ones)
|
||||||
|
# Weights are usually normalized in linear filter logic?
|
||||||
|
# Let's just test it runs.
|
||||||
|
median_filter.SetKernelNumericXZY([1.0] * 27)
|
||||||
|
|
||||||
|
# Run filter
|
||||||
|
median_filter.Run()
|
||||||
|
|
||||||
|
# Value should be 1.0 (mean of all 1.0 is 1.0)
|
||||||
|
self.assertAlmostEqual(vox_img.GetValue(0), 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user