Compare commits
4 Commits
e69b29a259
...
554eff9b55
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
554eff9b55 | ||
|
|
42db99759f | ||
|
|
69920acd61 | ||
|
|
647d0caa1c |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -5,3 +5,10 @@ build_warnings*.log
|
||||
final_build.log
|
||||
cmake_configure.log
|
||||
compile_commands.json
|
||||
|
||||
dist/
|
||||
build_python/
|
||||
src/Python/uLib/*.so*
|
||||
src/Python/uLib/*.pyd
|
||||
src/Python/uLib/*.pyc
|
||||
src/Python/uLib/__pycache__
|
||||
|
||||
47
build_python.py
Normal file
47
build_python.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
def build(setup_kwargs):
|
||||
"""
|
||||
Build the C++ extension using CMake.
|
||||
This function is called by poetry-core during the build process.
|
||||
The binary is placed directly inside the uLib directory in src/Python.
|
||||
"""
|
||||
# Root of the whole project where this build_extension.py is located
|
||||
project_root = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# Where the extension should go
|
||||
package_dir = os.path.join(project_root, "src/Python/uLib")
|
||||
|
||||
# Ensure package directory exists
|
||||
os.makedirs(package_dir, exist_ok=True)
|
||||
|
||||
# Temporary build directory
|
||||
build_temp = os.path.join(project_root, "build_python")
|
||||
os.makedirs(build_temp, exist_ok=True)
|
||||
|
||||
print(f"--- Running CMake build in {build_temp} ---")
|
||||
print(f"Project root: {project_root}")
|
||||
print(f"Target binary dir: {package_dir}")
|
||||
|
||||
# CMake configuration
|
||||
cmake_args = [
|
||||
f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={package_dir}",
|
||||
f"-DPYTHON_EXECUTABLE={sys.executable}",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DUSE_CUDA=OFF",
|
||||
"-G", "Unix Makefiles",
|
||||
]
|
||||
|
||||
# Use micromamba to ensure Boost and VTK are found during the build
|
||||
subprocess.check_call(["cmake", project_root] + cmake_args, cwd=build_temp)
|
||||
subprocess.check_call(["cmake", "--build", ".", "--parallel", "--target", "uLib_python"], cwd=build_temp)
|
||||
|
||||
# Ensure the package is found by poetry during the wheel creation process.
|
||||
# Return setup_kwargs for poetry-core.
|
||||
return setup_kwargs
|
||||
|
||||
if __name__ == "__main__":
|
||||
build({})
|
||||
7
poetry.lock
generated
Normal file
7
poetry.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand.
|
||||
package = []
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.9"
|
||||
content-hash = "db9b4c08b159b17b239e26c67ead7c37b82d9f9eb06550245ae3134c095f98f7"
|
||||
15
pyproject.toml
Normal file
15
pyproject.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[tool.poetry]
|
||||
name = "uLib"
|
||||
version = "0.6.0"
|
||||
description = "CMT Cosmic Muon Tomography project uLib python bindings"
|
||||
authors = ["Andrea Rigoni Garola <andrea.rigoni@pd.infn.it>"]
|
||||
readme = "README.md"
|
||||
packages = [{ include = "uLib", from = "src/Python" }]
|
||||
build = "build_python.py"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=2.0.0", "pybind11>=2.6.0", "cmake>=3.12"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
@@ -1,7 +1,36 @@
|
||||
|
||||
set(HEADERS Archives.h Array.h Collection.h DataAllocator.h Debug.h Export.h Function.h Macros.h Mpl.h Object.h Options.h Serializable.h Signal.h Singleton.h SmartPointer.h StaticInterface.h StringReader.h Types.h Uuid.h Vector.h)
|
||||
set(HEADERS
|
||||
Archives.h
|
||||
Array.h
|
||||
Collection.h
|
||||
DataAllocator.h
|
||||
Debug.h
|
||||
Export.h
|
||||
Function.h
|
||||
Macros.h
|
||||
Mpl.h
|
||||
Object.h
|
||||
Options.h
|
||||
Serializable.h
|
||||
Signal.h
|
||||
Singleton.h
|
||||
SmartPointer.h
|
||||
StaticInterface.h
|
||||
StringReader.h
|
||||
Types.h
|
||||
Uuid.h
|
||||
Vector.h
|
||||
)
|
||||
|
||||
set(SOURCES Archives.cpp Debug.cpp Object.cpp Options.cpp Serializable.cpp Signal.cpp Uuid.cpp)
|
||||
set(SOURCES
|
||||
Archives.cpp
|
||||
Debug.cpp
|
||||
Object.cpp
|
||||
Options.cpp
|
||||
Serializable.cpp
|
||||
Signal.cpp
|
||||
Uuid.cpp
|
||||
)
|
||||
|
||||
set(LIBRARIES Boost::program_options Boost::serialization)
|
||||
|
||||
|
||||
@@ -117,6 +117,10 @@ bool operator!=(const MetaAllocator<T> &, const MetaAllocator<U> &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Vector Implemetation ... wraps std::vector
|
||||
template <typename T> class Vector : public std::vector<T, MetaAllocator<T>> {
|
||||
typedef std::vector<T, MetaAllocator<T>> BaseClass;
|
||||
@@ -136,6 +140,7 @@ public:
|
||||
Vector(unsigned int size) : BaseClass(size) {}
|
||||
Vector(unsigned int size, T &value) : BaseClass(size, value) {}
|
||||
Vector() : BaseClass(0) {}
|
||||
Vector(std::initializer_list<T> init) : BaseClass(init) {}
|
||||
|
||||
inline VectorCommaInit operator<<(T scalar) {
|
||||
return VectorCommaInit(this, scalar);
|
||||
@@ -274,6 +279,11 @@ public:
|
||||
this->MoveToRAM();
|
||||
return BaseClass::insert(pos, std::move(x));
|
||||
}
|
||||
template <typename InputIt>
|
||||
iterator insert(const_iterator pos, InputIt first, InputIt last) {
|
||||
this->MoveToRAM();
|
||||
return BaseClass::insert(pos, first, last);
|
||||
}
|
||||
iterator erase(const_iterator pos) {
|
||||
this->MoveToRAM();
|
||||
return BaseClass::erase(pos);
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#include <Core/Vector.h>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
uLib::Vector<int> v;
|
||||
v.push_back(1);
|
||||
v.push_back(2);
|
||||
v.push_back(3);
|
||||
|
||||
std::cout << "RAM Vector elements: ";
|
||||
for (int i = 0; i < v.size(); ++i) {
|
||||
std::cout << v[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
#ifdef USE_CUDA
|
||||
std::cout << "Moving to VRAM..." << std::endl;
|
||||
v.MoveToVRAM();
|
||||
int *vram_ptr = v.GetVRAMData();
|
||||
if (vram_ptr) {
|
||||
std::cout << "Successfully got VRAM pointer!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to get VRAM pointer!" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Moving back to RAM..." << std::endl;
|
||||
v.MoveToRAM();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
#ifndef ULIB_DENSEMATRIX_H
|
||||
#define ULIB_DENSEMATRIX_H
|
||||
|
||||
// #include <Eigen/src/Core/Matrix.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
@@ -114,6 +115,21 @@ typedef unsigned long Scalarul;
|
||||
typedef float Scalarf;
|
||||
typedef double Scalard;
|
||||
|
||||
typedef Eigen::Matrix<int, 1, 1> Vector1i;
|
||||
typedef Eigen::Vector2i Vector2i;
|
||||
typedef Eigen::Vector3i Vector3i;
|
||||
typedef Eigen::Vector4i Vector4i;
|
||||
|
||||
typedef Eigen::Matrix<float, 1, 1> Vector1f;
|
||||
typedef Eigen::Vector2f Vector2f;
|
||||
typedef Eigen::Vector3f Vector3f;
|
||||
typedef Eigen::Vector4f Vector4f;
|
||||
|
||||
typedef Eigen::Matrix<double, 1, 1> Vector1d;
|
||||
typedef Eigen::Vector2d Vector2d;
|
||||
typedef Eigen::Vector3d Vector3d;
|
||||
typedef Eigen::Vector4d Vector4d;
|
||||
|
||||
typedef Eigen::Matrix<int, 1, 1> Matrix1i;
|
||||
typedef Eigen::Matrix2i Matrix2i;
|
||||
typedef Eigen::Matrix3i Matrix3i;
|
||||
@@ -124,15 +140,15 @@ typedef Eigen::Matrix2f Matrix2f;
|
||||
typedef Eigen::Matrix3f Matrix3f;
|
||||
typedef Eigen::Matrix4f Matrix4f;
|
||||
|
||||
typedef Eigen::Matrix<int, 1, 1> Vector1i;
|
||||
typedef Eigen::Vector2i Vector2i;
|
||||
typedef Eigen::Vector3i Vector3i;
|
||||
typedef Eigen::Vector4i Vector4i;
|
||||
typedef Eigen::Matrix<double, 1, 1> Matrix1d;
|
||||
typedef Eigen::Matrix2d Matrix2d;
|
||||
typedef Eigen::Matrix3d Matrix3d;
|
||||
typedef Eigen::Matrix4d Matrix4d;
|
||||
|
||||
typedef Eigen::MatrixXi MatrixXi;
|
||||
typedef Eigen::MatrixXf MatrixXf;
|
||||
typedef Eigen::MatrixXd MatrixXd;
|
||||
|
||||
typedef Eigen::Matrix<float, 1, 1> Vector1f;
|
||||
typedef Eigen::Vector2f Vector2f;
|
||||
typedef Eigen::Vector3f Vector3f;
|
||||
typedef Eigen::Vector4f Vector4f;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vector String interaction ///////////////////////////////////////////////////
|
||||
@@ -175,7 +191,7 @@ std::string VectorxT_ToString(const Eigen::Matrix<T, size, 1> &vec) {
|
||||
// }
|
||||
|
||||
template <typename T, int size>
|
||||
void operator>>(std::string &str, Eigen::Matrix<T, size, 1> &vec) {
|
||||
void operator >> (std::string &str, Eigen::Matrix<T, size, 1> &vec) {
|
||||
VectorxT_StringTo(vec, str);
|
||||
}
|
||||
|
||||
@@ -188,6 +204,9 @@ public:
|
||||
typedef Eigen::Matrix<Scalarf, 4, 1> BaseClass;
|
||||
|
||||
_HPoint3f() : BaseClass(0, 0, 0, p) {}
|
||||
_HPoint3f(int rows, int cols) : BaseClass() {
|
||||
this->operator()(3) = p;
|
||||
}
|
||||
_HPoint3f(float x, float y, float z) : BaseClass(x, y, z, p) {}
|
||||
_HPoint3f(Vector3f &in) : BaseClass(in.homogeneous()) {
|
||||
this->operator()(3) = p;
|
||||
|
||||
@@ -70,8 +70,8 @@ public:
|
||||
|
||||
int ImportFromVti(const char *file, bool density_type = 0);
|
||||
|
||||
protected:
|
||||
virtual ~VoxImage() {}
|
||||
protected:
|
||||
VoxImage(const Vector3i &size) : BaseClass(size) {}
|
||||
};
|
||||
|
||||
@@ -90,7 +90,8 @@ struct Voxel {
|
||||
} // namespace Interface
|
||||
|
||||
struct Voxel {
|
||||
Scalarf Value;
|
||||
Scalarf Value = 0.0f;
|
||||
Scalari Count = 0;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
namespace uLib {
|
||||
|
||||
#ifdef USE_CUDA
|
||||
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||
template <typename VoxelT>
|
||||
__global__ void ABTrimFilterKernel(const VoxelT *in, VoxelT *out,
|
||||
const VoxelT *kernel, int vox_size,
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
mBtrim = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_CUDA
|
||||
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||
void Run() {
|
||||
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
||||
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
||||
@@ -206,7 +206,7 @@ public:
|
||||
mBtrim = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_CUDA
|
||||
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||
void Run() {
|
||||
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
||||
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
namespace uLib {
|
||||
|
||||
#ifdef USE_CUDA
|
||||
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||
template <typename VoxelT>
|
||||
__global__ void LinearFilterKernel(const VoxelT *in, VoxelT *out,
|
||||
const VoxelT *kernel, int vox_size,
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
typedef VoxImageFilter<VoxelT, VoxFilterAlgorithmLinear<VoxelT>> BaseClass;
|
||||
VoxFilterAlgorithmLinear(const Vector3i &size) : BaseClass(size) {}
|
||||
|
||||
#ifdef USE_CUDA
|
||||
#if defined(USE_CUDA) && defined(__CUDACC__)
|
||||
void Run() {
|
||||
if (this->m_Image->Data().GetDevice() == MemoryDevice::VRAM ||
|
||||
this->m_KernelData.Data().GetDevice() == MemoryDevice::VRAM) {
|
||||
|
||||
@@ -4,10 +4,11 @@ set(SOURCES
|
||||
module.cpp
|
||||
core_bindings.cpp
|
||||
math_bindings.cpp
|
||||
math_filters_bindings.cpp
|
||||
)
|
||||
|
||||
# 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
|
||||
target_link_libraries(uLib_python PRIVATE
|
||||
@@ -22,6 +23,14 @@ target_include_directories(uLib_python PRIVATE
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Install uLib_python within the uLib install target
|
||||
install(TARGETS uLib_python
|
||||
EXPORT "${PROJECT_NAME}Targets"
|
||||
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
|
||||
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib
|
||||
ARCHIVE DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib
|
||||
)
|
||||
|
||||
# --- Python Tests ---------------------------------------------------------- #
|
||||
|
||||
if(BUILD_TESTING)
|
||||
@@ -30,15 +39,20 @@ if(BUILD_TESTING)
|
||||
add_test(NAME pybind_general
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/pybind_test.py)
|
||||
set_tests_properties(pybind_general PROPERTIES
|
||||
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>")
|
||||
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>:${PROJECT_SOURCE_DIR}/src/Python")
|
||||
|
||||
add_test(NAME pybind_core
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/core_pybind_test.py)
|
||||
set_tests_properties(pybind_core PROPERTIES
|
||||
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>")
|
||||
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_python>:${PROJECT_SOURCE_DIR}/src/Python")
|
||||
|
||||
add_test(NAME pybind_math
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/testing/math_pybind_test.py)
|
||||
set_tests_properties(pybind_math PROPERTIES
|
||||
ENVIRONMENT "PYTHONPATH=$<TARGET_FILE_DIR:uLib_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()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/eigen.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
#include <pybind11/numpy.h>
|
||||
|
||||
#include "Math/Dense.h"
|
||||
#include "Math/Transform.h"
|
||||
@@ -13,13 +16,224 @@
|
||||
#include "Math/TriangleMesh.h"
|
||||
#include "Math/VoxRaytracer.h"
|
||||
#include "Math/Accumulator.h"
|
||||
#include "Math/VoxImage.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace uLib;
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalari>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalarui>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalarl>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalarul>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalarf>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Scalard>);
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector3f>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector3i>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector4f>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector4i>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector3d>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Vector4d>);
|
||||
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<Voxel>);
|
||||
PYBIND11_MAKE_OPAQUE(uLib::Vector<VoxRaytracer::RayData::Element>);
|
||||
|
||||
template <typename MatrixType>
|
||||
void bind_eigen_type(py::module_ &m, const char *name) {
|
||||
using Scalar = typename MatrixType::Scalar;
|
||||
constexpr bool is_vector = MatrixType::IsVectorAtCompileTime;
|
||||
|
||||
// Default constructor (zeros)
|
||||
m.def(name, []() -> MatrixType {
|
||||
if constexpr (MatrixType::RowsAtCompileTime == Eigen::Dynamic || MatrixType::ColsAtCompileTime == Eigen::Dynamic) {
|
||||
return MatrixType(); // Empty dynamic matrix
|
||||
} else {
|
||||
return MatrixType::Zero(); // Zero static matrix
|
||||
}
|
||||
});
|
||||
|
||||
// Specialized constructor for dynamic matrices
|
||||
if constexpr (MatrixType::RowsAtCompileTime == Eigen::Dynamic || MatrixType::ColsAtCompileTime == Eigen::Dynamic) {
|
||||
m.def(name, [](int rows, int cols) -> MatrixType {
|
||||
MatrixType mat;
|
||||
mat.setZero(rows, cols);
|
||||
return mat;
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize from list
|
||||
m.def(name, [](py::list l) -> MatrixType {
|
||||
MatrixType mat;
|
||||
if constexpr (is_vector) {
|
||||
mat.setZero(l.size());
|
||||
for (size_t i = 0; i < l.size(); ++i) {
|
||||
mat(i) = l[i].cast<Scalar>();
|
||||
}
|
||||
} else {
|
||||
int rows = MatrixType::RowsAtCompileTime == Eigen::Dynamic ? (int)std::sqrt(l.size()) : MatrixType::RowsAtCompileTime;
|
||||
int cols = MatrixType::ColsAtCompileTime == Eigen::Dynamic ? (int)std::sqrt(l.size()) : MatrixType::ColsAtCompileTime;
|
||||
mat.setZero(rows, cols);
|
||||
for (size_t i = 0; i < (size_t)l.size(); ++i) {
|
||||
mat(i / cols, i % cols) = l[i].cast<Scalar>();
|
||||
}
|
||||
}
|
||||
return mat;
|
||||
});
|
||||
|
||||
// Initialize from py::array
|
||||
m.def(name, [](py::array_t<Scalar, py::array::c_style | py::array::forcecast> arr) -> MatrixType {
|
||||
auto buf = arr.request();
|
||||
MatrixType mat;
|
||||
if constexpr (is_vector) {
|
||||
mat.setZero(buf.size);
|
||||
Scalar* ptr = static_cast<Scalar*>(buf.ptr);
|
||||
for (ssize_t i = 0; i < buf.size; ++i) mat(i) = ptr[i];
|
||||
} else {
|
||||
int rows = buf.shape.size() > 0 ? (int)buf.shape[0] : 1;
|
||||
int cols = buf.shape.size() > 1 ? (int)buf.shape[1] : 1;
|
||||
mat.setZero(rows, cols);
|
||||
Scalar* ptr = static_cast<Scalar*>(buf.ptr);
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
for (int j = 0; j < cols; ++j) {
|
||||
mat(i, j) = ptr[i * cols + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return mat;
|
||||
});
|
||||
}
|
||||
|
||||
void init_math(py::module_ &m) {
|
||||
|
||||
// Math/Transform.h
|
||||
// 1. Basic Eigen Types (Vectors and Matrices)
|
||||
bind_eigen_type<Vector1f>(m, "Vector1f");
|
||||
bind_eigen_type<Vector2f>(m, "Vector2f");
|
||||
bind_eigen_type<Vector3f>(m, "Vector3f");
|
||||
bind_eigen_type<Vector4f>(m, "Vector4f");
|
||||
bind_eigen_type<Vector1i>(m, "Vector1i");
|
||||
bind_eigen_type<Vector2i>(m, "Vector2i");
|
||||
bind_eigen_type<Vector3i>(m, "Vector3i");
|
||||
bind_eigen_type<Vector4i>(m, "Vector4i");
|
||||
bind_eigen_type<Vector1d>(m, "Vector1d");
|
||||
bind_eigen_type<Vector2d>(m, "Vector2d");
|
||||
bind_eigen_type<Vector3d>(m, "Vector3d");
|
||||
bind_eigen_type<Vector4d>(m, "Vector4d");
|
||||
|
||||
bind_eigen_type<Matrix2f>(m, "Matrix2f");
|
||||
bind_eigen_type<Matrix3f>(m, "Matrix3f");
|
||||
bind_eigen_type<Matrix4f>(m, "Matrix4f");
|
||||
bind_eigen_type<Matrix2i>(m, "Matrix2i");
|
||||
bind_eigen_type<Matrix3i>(m, "Matrix3i");
|
||||
bind_eigen_type<Matrix4i>(m, "Matrix4i");
|
||||
bind_eigen_type<Matrix2d>(m, "Matrix2d");
|
||||
bind_eigen_type<Matrix3d>(m, "Matrix3d");
|
||||
bind_eigen_type<Matrix4d>(m, "Matrix4d");
|
||||
|
||||
bind_eigen_type<MatrixXi>(m, "MatrixXi");
|
||||
bind_eigen_type<MatrixXf>(m, "MatrixXf");
|
||||
bind_eigen_type<MatrixXd>(m, "MatrixXd");
|
||||
|
||||
// 2. Homogeneous types
|
||||
py::class_<HPoint3f>(m, "HPoint3f")
|
||||
.def(py::init<>())
|
||||
.def(py::init<float, float, float>())
|
||||
.def(py::init<Vector3f &>());
|
||||
py::class_<HVector3f>(m, "HVector3f")
|
||||
.def(py::init<>())
|
||||
.def(py::init<float, float, float>())
|
||||
.def(py::init<Vector3f &>());
|
||||
py::class_<HLine3f>(m, "HLine3f")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("origin", &HLine3f::origin)
|
||||
.def_readwrite("direction", &HLine3f::direction);
|
||||
py::class_<HError3f>(m, "HError3f")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("position_error", &HError3f::position_error)
|
||||
.def_readwrite("direction_error", &HError3f::direction_error);
|
||||
|
||||
// 3. Dynamic Vectors (uLib::Vector)
|
||||
py::bind_vector<uLib::Vector<Scalari>>(m, "Vector_i")
|
||||
.def("MoveToVRAM", &uLib::Vector<Scalari>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Scalari>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Scalarui>>(m, "Vector_ui")
|
||||
.def("MoveToVRAM", &uLib::Vector<Scalarui>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Scalarui>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Scalarl>>(m, "Vector_l")
|
||||
.def("MoveToVRAM", &uLib::Vector<Scalarl>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Scalarl>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Scalarul>>(m, "Vector_ul")
|
||||
.def("MoveToVRAM", &uLib::Vector<Scalarul>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Scalarul>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Scalarf>>(m, "Vector_f")
|
||||
.def("MoveToVRAM", &uLib::Vector<Scalarf>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Scalarf>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Scalard>>(m, "Vector_d")
|
||||
.def("MoveToVRAM", &uLib::Vector<Scalard>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Scalard>::MoveToRAM);
|
||||
|
||||
py::bind_vector<uLib::Vector<Vector3f>>(m, "Vector_Vector3f")
|
||||
.def("MoveToVRAM", &uLib::Vector<Vector3f>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Vector3f>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Vector3i>>(m, "Vector_Vector3i")
|
||||
.def("MoveToVRAM", &uLib::Vector<Vector3i>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Vector3i>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Vector4f>>(m, "Vector_Vector4f")
|
||||
.def("MoveToVRAM", &uLib::Vector<Vector4f>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Vector4f>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Vector4i>>(m, "Vector_Vector4i")
|
||||
.def("MoveToVRAM", &uLib::Vector<Vector4i>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Vector4i>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Vector3d>>(m, "Vector_Vector3d")
|
||||
.def("MoveToVRAM", &uLib::Vector<Vector3d>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Vector3d>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Vector4d>>(m, "Vector_Vector4d")
|
||||
.def("MoveToVRAM", &uLib::Vector<Vector4d>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Vector4d>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<Voxel>>(m, "Vector_Voxel")
|
||||
.def("MoveToVRAM", &uLib::Vector<Voxel>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Voxel>::MoveToRAM);
|
||||
py::bind_vector<uLib::Vector<VoxRaytracer::RayData::Element>>(m, "Vector_VoxRaytracerRayDataElement")
|
||||
.def("MoveToVRAM", &uLib::Vector<VoxRaytracer::RayData::Element>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<VoxRaytracer::RayData::Element>::MoveToRAM);
|
||||
|
||||
// 4. Accumulators
|
||||
py::class_<Accumulator_Mean<float>>(m, "Accumulator_Mean_f")
|
||||
.def(py::init<>())
|
||||
.def("AddPass", &Accumulator_Mean<float>::AddPass)
|
||||
.def("__call__", py::overload_cast<const float>(&Accumulator_Mean<float>::operator()))
|
||||
.def("__call__", py::overload_cast<>(&Accumulator_Mean<float>::operator(), py::const_));
|
||||
|
||||
py::class_<Accumulator_Mean<double>>(m, "Accumulator_Mean_d")
|
||||
.def(py::init<>())
|
||||
.def("AddPass", &Accumulator_Mean<double>::AddPass)
|
||||
.def("__call__", py::overload_cast<const double>(&Accumulator_Mean<double>::operator()))
|
||||
.def("__call__", py::overload_cast<>(&Accumulator_Mean<double>::operator(), py::const_));
|
||||
|
||||
py::class_<Accumulator_ABTrim<float>>(m, "Accumulator_ABTrim_f")
|
||||
.def(py::init<>())
|
||||
.def("SetABTrim", &Accumulator_ABTrim<float>::SetABTrim)
|
||||
.def("__iadd__", [](Accumulator_ABTrim<float> &self, float val) { self += val; return &self; })
|
||||
.def("__call__", &Accumulator_ABTrim<float>::operator());
|
||||
|
||||
py::class_<Accumulator_ABTrim<double>>(m, "Accumulator_ABTrim_d")
|
||||
.def(py::init<>())
|
||||
.def("SetABTrim", &Accumulator_ABTrim<double>::SetABTrim)
|
||||
.def("__iadd__", [](Accumulator_ABTrim<double> &self, double val) { self += val; return &self; })
|
||||
.def("__call__", &Accumulator_ABTrim<double>::operator());
|
||||
|
||||
py::class_<Accumulator_ABClip<float>>(m, "Accumulator_ABClip_f")
|
||||
.def(py::init<>())
|
||||
.def("SetABTrim", &Accumulator_ABClip<float>::SetABTrim)
|
||||
.def("__iadd__", [](Accumulator_ABClip<float> &self, float val) { self += val; return &self; })
|
||||
.def("__call__", &Accumulator_ABClip<float>::operator());
|
||||
|
||||
py::class_<Accumulator_ABClip<double>>(m, "Accumulator_ABClip_d")
|
||||
.def(py::init<>())
|
||||
.def("SetABTrim", &Accumulator_ABClip<double>::SetABTrim)
|
||||
.def("__iadd__", [](Accumulator_ABClip<double> &self, double val) { self += val; return &self; })
|
||||
.def("__call__", &Accumulator_ABClip<double>::operator());
|
||||
|
||||
// 5. Core Math Structures
|
||||
py::class_<AffineTransform>(m, "AffineTransform")
|
||||
.def(py::init<>())
|
||||
.def("GetWorldMatrix", &AffineTransform::GetWorldMatrix)
|
||||
@@ -34,13 +248,11 @@ void init_math(py::module_ &m) {
|
||||
.def("EulerYZYRotate", &AffineTransform::EulerYZYRotate)
|
||||
.def("FlipAxes", &AffineTransform::FlipAxes);
|
||||
|
||||
// Math/Geometry.h
|
||||
py::class_<Geometry, AffineTransform>(m, "Geometry")
|
||||
.def(py::init<>())
|
||||
.def("GetWorldPoint", py::overload_cast<const Vector4f &>(&Geometry::GetWorldPoint, py::const_))
|
||||
.def("GetLocalPoint", py::overload_cast<const Vector4f &>(&Geometry::GetLocalPoint, py::const_));
|
||||
|
||||
// Math/ContainerBox.h
|
||||
py::class_<ContainerBox, AffineTransform>(m, "ContainerBox")
|
||||
.def(py::init<>())
|
||||
.def("SetOrigin", &ContainerBox::SetOrigin)
|
||||
@@ -51,7 +263,6 @@ void init_math(py::module_ &m) {
|
||||
.def("GetWorldPoint", py::overload_cast<const Vector4f &>(&ContainerBox::GetWorldPoint, py::const_))
|
||||
.def("GetLocalPoint", py::overload_cast<const Vector4f &>(&ContainerBox::GetLocalPoint, py::const_));
|
||||
|
||||
// Math/StructuredData.h
|
||||
py::enum_<StructuredData::_Order>(m, "StructuredDataOrder")
|
||||
.value("CustomOrder", StructuredData::CustomOrder)
|
||||
.value("XYZ", StructuredData::XYZ)
|
||||
@@ -74,7 +285,6 @@ void init_math(py::module_ &m) {
|
||||
.def("Map", &StructuredData::Map)
|
||||
.def("UnMap", &StructuredData::UnMap);
|
||||
|
||||
// Math/StructuredGrid.h
|
||||
py::class_<StructuredGrid, ContainerBox, StructuredData>(m, "StructuredGrid")
|
||||
.def(py::init<const Vector3i &>())
|
||||
.def("SetSpacing", &StructuredGrid::SetSpacing)
|
||||
@@ -84,7 +294,6 @@ void init_math(py::module_ &m) {
|
||||
return self.Find(HPoint3f(pt));
|
||||
});
|
||||
|
||||
// Math/Structured2DGrid.h
|
||||
py::class_<Structured2DGrid>(m, "Structured2DGrid")
|
||||
.def(py::init<>())
|
||||
.def("SetDims", &Structured2DGrid::SetDims)
|
||||
@@ -100,7 +309,6 @@ void init_math(py::module_ &m) {
|
||||
.def("UnitToPhysicsSpace", &Structured2DGrid::UnitToPhysicsSpace)
|
||||
.def("SetDebug", &Structured2DGrid::SetDebug);
|
||||
|
||||
// Math/Structured4DGrid.h
|
||||
py::class_<Structured4DGrid>(m, "Structured4DGrid")
|
||||
.def(py::init<>())
|
||||
.def("SetDims", &Structured4DGrid::SetDims)
|
||||
@@ -116,7 +324,37 @@ void init_math(py::module_ &m) {
|
||||
.def("UnitToPhysicsSpace", &Structured4DGrid::UnitToPhysicsSpace)
|
||||
.def("SetDebug", &Structured4DGrid::SetDebug);
|
||||
|
||||
// Math/TriangleMesh.h
|
||||
// 6. High-level Structures
|
||||
py::class_<Voxel>(m, "Voxel")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("Value", &Voxel::Value)
|
||||
.def_readwrite("Count", &Voxel::Count);
|
||||
|
||||
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 int>(&Abstract::VoxImage::GetValue, py::const_))
|
||||
.def("SetValue", py::overload_cast<const Vector3i &, float>(&Abstract::VoxImage::SetValue))
|
||||
.def("SetValue", py::overload_cast<const int, float>(&Abstract::VoxImage::SetValue))
|
||||
.def("ExportToVtk", &Abstract::VoxImage::ExportToVtk)
|
||||
.def("ExportToVti", &Abstract::VoxImage::ExportToVti)
|
||||
.def("ImportFromVtk", &Abstract::VoxImage::ImportFromVtk)
|
||||
.def("ImportFromVti", &Abstract::VoxImage::ImportFromVti);
|
||||
|
||||
py::class_<VoxImage<Voxel>, Abstract::VoxImage>(m, "VoxImage")
|
||||
.def(py::init<>())
|
||||
.def(py::init<const Vector3i &>())
|
||||
.def("Data", &VoxImage<Voxel>::Data, py::return_value_policy::reference_internal)
|
||||
.def("InitVoxels", &VoxImage<Voxel>::InitVoxels)
|
||||
.def("Abs", &VoxImage<Voxel>::Abs)
|
||||
.def("clipImage", py::overload_cast<const Vector3i, const Vector3i>(&VoxImage<Voxel>::clipImage, py::const_))
|
||||
.def("clipImage", py::overload_cast<const HPoint3f, const HPoint3f>(&VoxImage<Voxel>::clipImage, py::const_))
|
||||
.def("clipImage", py::overload_cast<const float>(&VoxImage<Voxel>::clipImage, py::const_))
|
||||
.def("maskImage", py::overload_cast<const HPoint3f, const HPoint3f, float>(&VoxImage<Voxel>::maskImage, py::const_))
|
||||
.def("maskImage", py::overload_cast<const float, float, float>(&VoxImage<Voxel>::maskImage, py::const_), py::arg("threshold"), py::arg("belowValue") = 0, py::arg("aboveValue") = 0)
|
||||
.def("fixVoxels", py::overload_cast<const float, float>(&VoxImage<Voxel>::fixVoxels, py::const_))
|
||||
.def("__getitem__", py::overload_cast<unsigned int>(&VoxImage<Voxel>::operator[]))
|
||||
.def("__getitem__", py::overload_cast<const Vector3i &>(&VoxImage<Voxel>::operator[]));
|
||||
|
||||
py::class_<TriangleMesh>(m, "TriangleMesh")
|
||||
.def(py::init<>())
|
||||
.def("AddPoint", &TriangleMesh::AddPoint)
|
||||
@@ -124,7 +362,6 @@ void init_math(py::module_ &m) {
|
||||
.def("Points", &TriangleMesh::Points, py::return_value_policy::reference_internal)
|
||||
.def("Triangles", &TriangleMesh::Triangles, py::return_value_policy::reference_internal);
|
||||
|
||||
// Math/VoxRaytracer.h
|
||||
py::class_<VoxRaytracer::RayData::Element>(m, "VoxRaytracerRayDataElement")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("vox_id", &VoxRaytracer::RayData::Element::vox_id)
|
||||
@@ -133,6 +370,7 @@ void init_math(py::module_ &m) {
|
||||
py::class_<VoxRaytracer::RayData>(m, "VoxRaytracerRayData")
|
||||
.def(py::init<>())
|
||||
.def("AppendRay", &VoxRaytracer::RayData::AppendRay)
|
||||
.def("Data", py::overload_cast<>(&VoxRaytracer::RayData::Data), py::return_value_policy::reference_internal)
|
||||
.def("Count", &VoxRaytracer::RayData::Count)
|
||||
.def("TotalLength", &VoxRaytracer::RayData::TotalLength)
|
||||
.def("SetCount", &VoxRaytracer::RayData::SetCount)
|
||||
@@ -140,13 +378,8 @@ void init_math(py::module_ &m) {
|
||||
|
||||
py::class_<VoxRaytracer>(m, "VoxRaytracer")
|
||||
.def(py::init<StructuredGrid &>(), py::keep_alive<1, 2>())
|
||||
.def("GetImage", &VoxRaytracer::GetImage, py::return_value_policy::reference_internal);
|
||||
|
||||
// Math/Accumulator.h
|
||||
py::class_<Accumulator_Mean<float>>(m, "Accumulator_Mean_f")
|
||||
.def(py::init<>())
|
||||
.def("AddPass", &Accumulator_Mean<float>::AddPass)
|
||||
.def("__call__", py::overload_cast<const float>(&Accumulator_Mean<float>::operator()))
|
||||
.def("__call__", py::overload_cast<>(&Accumulator_Mean<float>::operator(), py::const_));
|
||||
.def("GetImage", &VoxRaytracer::GetImage, py::return_value_policy::reference_internal)
|
||||
.def("TraceLine", &VoxRaytracer::TraceLine)
|
||||
.def("TraceBetweenPoints", &VoxRaytracer::TraceBetweenPoints);
|
||||
|
||||
}
|
||||
|
||||
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_math(py::module_ &m);
|
||||
void init_math_filters(py::module_ &m);
|
||||
|
||||
PYBIND11_MODULE(uLib_python, m) {
|
||||
m.doc() = "Python bindings for uLib Core and Math libraries";
|
||||
@@ -15,4 +16,5 @@ PYBIND11_MODULE(uLib_python, m) {
|
||||
// Math submodule
|
||||
py::module_ math = m.def_submodule("Math", "Math library bindings");
|
||||
init_math(math);
|
||||
init_math_filters(math);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ import os
|
||||
import unittest
|
||||
import time
|
||||
|
||||
import uLib_python
|
||||
import uLib
|
||||
|
||||
class TestCoreOptions(unittest.TestCase):
|
||||
def test_options(self):
|
||||
opt = uLib_python.Core.Options("Test Options")
|
||||
opt = uLib.Core.Options("Test Options")
|
||||
|
||||
# Test basic config file parsing
|
||||
with open("test_configuration.ini", "w") as f:
|
||||
@@ -18,12 +18,12 @@ class TestCoreOptions(unittest.TestCase):
|
||||
|
||||
class TestCoreObject(unittest.TestCase):
|
||||
def test_object(self):
|
||||
obj = uLib_python.Core.Object()
|
||||
obj = uLib.Core.Object()
|
||||
self.assertIsNotNone(obj)
|
||||
|
||||
class TestCoreTimer(unittest.TestCase):
|
||||
def test_timer(self):
|
||||
timer = uLib_python.Core.Timer()
|
||||
timer = uLib.Core.Timer()
|
||||
timer.Start()
|
||||
time.sleep(0.1)
|
||||
val = timer.StopWatch()
|
||||
|
||||
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()
|
||||
@@ -3,16 +3,77 @@ import os
|
||||
import unittest
|
||||
import numpy as np
|
||||
|
||||
import uLib_python
|
||||
import uLib
|
||||
|
||||
def vector4f0(v, target):
|
||||
diff = np.array(v) - np.array(target)
|
||||
diff[3] = 0 # ignoring w
|
||||
return np.all(np.abs(diff) < 0.001)
|
||||
|
||||
|
||||
class TestMathMatrix(unittest.TestCase):
|
||||
|
||||
def test_matrix(self):
|
||||
def check_1234(m2f):
|
||||
self.assertEqual(m2f[0, 0], 1)
|
||||
self.assertEqual(m2f[0, 1], 2)
|
||||
self.assertEqual(m2f[1, 0], 3)
|
||||
self.assertEqual(m2f[1, 1], 4)
|
||||
|
||||
m2f = uLib.Math.Matrix2f()
|
||||
m2f[0, 0] = 1
|
||||
m2f[0, 1] = 2
|
||||
m2f[1, 0] = 3
|
||||
m2f[1, 1] = 4
|
||||
check_1234(m2f)
|
||||
|
||||
m2f = uLib.Math.Matrix2f([1, 2, 3, 4])
|
||||
check_1234(m2f)
|
||||
|
||||
# m2f = uLib.Math.Matrix2f([[1, 2], [3, 4]])
|
||||
# check_1234(m2f)
|
||||
|
||||
m2f = uLib.Math.Matrix2f(np.array([[1, 2], [3, 4]]))
|
||||
check_1234(m2f)
|
||||
|
||||
def test_vector2(self):
|
||||
v2f = uLib.Math.Vector2f()
|
||||
v2f[0] = 1
|
||||
v2f[1] = 2
|
||||
self.assertEqual(v2f[0], 1)
|
||||
self.assertEqual(v2f[1], 2)
|
||||
|
||||
v2f = uLib.Math.Vector2f([1, 2])
|
||||
self.assertEqual(v2f[0], 1)
|
||||
self.assertEqual(v2f[1], 2)
|
||||
|
||||
v2f = uLib.Math.Vector2f(np.array([1, 2]))
|
||||
self.assertEqual(v2f[0], 1)
|
||||
self.assertEqual(v2f[1], 2)
|
||||
|
||||
def test_vector3(self):
|
||||
v3f = uLib.Math.Vector3f()
|
||||
v3f[0] = 1
|
||||
v3f[1] = 2
|
||||
v3f[2] = 3
|
||||
self.assertEqual(v3f[0], 1)
|
||||
self.assertEqual(v3f[1], 2)
|
||||
self.assertEqual(v3f[2], 3)
|
||||
|
||||
v3f = uLib.Math.Vector3f([1, 2, 3])
|
||||
self.assertEqual(v3f[0], 1)
|
||||
self.assertEqual(v3f[1], 2)
|
||||
self.assertEqual(v3f[2], 3)
|
||||
|
||||
v3f = uLib.Math.Vector3f(np.array([1, 2, 3]))
|
||||
self.assertEqual(v3f[0], 1)
|
||||
self.assertEqual(v3f[1], 2)
|
||||
self.assertEqual(v3f[2], 3)
|
||||
|
||||
|
||||
class TestMathGeometry(unittest.TestCase):
|
||||
def test_geometry(self):
|
||||
Geo = uLib_python.Math.Geometry()
|
||||
Geo = uLib.Math.Geometry()
|
||||
|
||||
Geo.SetPosition([1, 1, 1])
|
||||
|
||||
@@ -27,7 +88,7 @@ class TestMathGeometry(unittest.TestCase):
|
||||
|
||||
class TestMathContainerBox(unittest.TestCase):
|
||||
def test_container_box_local(self):
|
||||
Cnt = uLib_python.Math.ContainerBox()
|
||||
Cnt = uLib.Math.ContainerBox()
|
||||
Cnt.SetOrigin([-1, -1, -1])
|
||||
Cnt.SetSize([2, 2, 2])
|
||||
|
||||
@@ -35,7 +96,7 @@ class TestMathContainerBox(unittest.TestCase):
|
||||
self.assertTrue(np.allclose(size, [2, 2, 2]))
|
||||
|
||||
def test_container_box_global(self):
|
||||
Box = uLib_python.Math.ContainerBox()
|
||||
Box = uLib.Math.ContainerBox()
|
||||
Box.SetPosition([1, 1, 1])
|
||||
Box.SetSize([2, 2, 2])
|
||||
|
||||
@@ -45,7 +106,7 @@ class TestMathContainerBox(unittest.TestCase):
|
||||
|
||||
class TestMathStructuredGrid(unittest.TestCase):
|
||||
def test_structured_grid(self):
|
||||
grid = uLib_python.Math.StructuredGrid([10, 10, 10])
|
||||
grid = uLib.Math.StructuredGrid([10, 10, 10])
|
||||
grid.SetSpacing([1, 1, 1])
|
||||
|
||||
spacing = grid.GetSpacing()
|
||||
@@ -53,10 +114,76 @@ class TestMathStructuredGrid(unittest.TestCase):
|
||||
|
||||
class TestMathAccumulator(unittest.TestCase):
|
||||
def test_accumulator_mean(self):
|
||||
acc = uLib_python.Math.Accumulator_Mean_f()
|
||||
acc = uLib.Math.Accumulator_Mean_f()
|
||||
acc(10.0)
|
||||
acc(20.0)
|
||||
self.assertAlmostEqual(acc(), 15.0)
|
||||
|
||||
class TestMathNewTypes(unittest.TestCase):
|
||||
def test_eigen_vectors(self):
|
||||
v1f = uLib.Math.Vector1f()
|
||||
v3d = uLib.Math.Vector3d()
|
||||
m4f = uLib.Math.Matrix4f()
|
||||
self.assertIsNotNone(v1f)
|
||||
self.assertIsNotNone(v3d)
|
||||
self.assertIsNotNone(m4f)
|
||||
|
||||
def test_ulib_vectors(self):
|
||||
vi = uLib.Math.Vector_i()
|
||||
vi.append(1)
|
||||
vi.append(2)
|
||||
self.assertEqual(len(vi), 2)
|
||||
self.assertEqual(vi[0], 1)
|
||||
self.assertEqual(vi[1], 2)
|
||||
|
||||
vf = uLib.Math.Vector_f()
|
||||
vf.append(1.5)
|
||||
self.assertAlmostEqual(vf[0], 1.5)
|
||||
|
||||
def test_homogeneous(self):
|
||||
p = uLib.Math.HPoint3f(1.0, 2.0, 3.0)
|
||||
v = uLib.Math.HVector3f(0.0, 1.0, 0.0)
|
||||
self.assertIsNotNone(p)
|
||||
self.assertIsNotNone(v)
|
||||
|
||||
def test_vox_image(self):
|
||||
img = uLib.Math.VoxImage([2, 2, 2])
|
||||
self.assertEqual(img.GetDims()[0], 2)
|
||||
img.SetValue([0, 0, 0], 10.5)
|
||||
# Note: GetValue returns float, and there might be internal scaling (1.E-6 observed in code)
|
||||
# Actually in VoxImage.h: GetValue(id) returns At(id).Value
|
||||
# SetValue(id, value) sets At(id).Value = value
|
||||
self.assertAlmostEqual(img.GetValue([0, 0, 0]), 10.5)
|
||||
|
||||
class TestMathVoxRaytracer(unittest.TestCase):
|
||||
def test_raytracer(self):
|
||||
grid = uLib.Math.StructuredGrid([10, 10, 10])
|
||||
grid.SetSpacing([1, 1, 1])
|
||||
grid.SetOrigin([0, 0, 0])
|
||||
|
||||
rt = uLib.Math.VoxRaytracer(grid)
|
||||
self.assertIsNotNone(rt)
|
||||
|
||||
# Test TraceBetweenPoints
|
||||
p1 = np.array([0.5, 0.5, -1.0, 1.0], dtype=np.float32)
|
||||
p2 = np.array([0.5, 0.5, 11.0, 1.0], dtype=np.float32)
|
||||
data = rt.TraceBetweenPoints(p1, p2)
|
||||
|
||||
self.assertGreater(data.Count(), 0)
|
||||
self.assertAlmostEqual(data.TotalLength(), 10.0)
|
||||
|
||||
# Check elements
|
||||
elements = data.Data()
|
||||
for i in range(data.Count()):
|
||||
self.assertGreaterEqual(elements[i].vox_id, 0)
|
||||
self.assertGreater(elements[i].L, 0)
|
||||
|
||||
def test_ray_data(self):
|
||||
data = uLib.Math.VoxRaytracerRayData()
|
||||
data.SetCount(10)
|
||||
data.SetTotalLength(5.5)
|
||||
self.assertEqual(data.Count(), 10)
|
||||
self.assertAlmostEqual(data.TotalLength(), 5.5)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
import uLib_python
|
||||
import uLib
|
||||
|
||||
def test_core():
|
||||
print("Testing Core module...")
|
||||
obj = uLib_python.Core.Object()
|
||||
obj = uLib.Core.Object()
|
||||
print("Core Object created:", obj)
|
||||
|
||||
timer = uLib_python.Core.Timer()
|
||||
timer = uLib.Core.Timer()
|
||||
timer.Start()
|
||||
print("Core Timer started")
|
||||
|
||||
options = uLib_python.Core.Options("Test Options")
|
||||
options = uLib.Core.Options("Test Options")
|
||||
print("Core Options created:", options)
|
||||
|
||||
def test_math():
|
||||
print("Testing Math module...")
|
||||
|
||||
# Test AffineTransform
|
||||
transform = uLib_python.Math.AffineTransform()
|
||||
transform = uLib.Math.AffineTransform()
|
||||
print("AffineTransform created")
|
||||
|
||||
# Test Geometry
|
||||
geom = uLib_python.Math.Geometry()
|
||||
geom = uLib.Math.Geometry()
|
||||
print("Geometry created")
|
||||
|
||||
# Test StructuredData
|
||||
data = uLib_python.Math.StructuredData([10, 10, 10])
|
||||
data = uLib.Math.StructuredData([10, 10, 10])
|
||||
print("StructuredData created with dims:", data.GetDims())
|
||||
|
||||
# Test Structured2DGrid
|
||||
grid2d = uLib_python.Math.Structured2DGrid()
|
||||
grid2d = uLib.Math.Structured2DGrid()
|
||||
grid2d.SetDims([100, 100])
|
||||
print("Structured2DGrid created with dims:", grid2d.GetDims())
|
||||
|
||||
# Test TriangleMesh
|
||||
mesh = uLib_python.Math.TriangleMesh()
|
||||
mesh = uLib.Math.TriangleMesh()
|
||||
print("TriangleMesh created")
|
||||
|
||||
print("All tests passed successfully!")
|
||||
|
||||
7
src/Python/uLib/__init__.py
Normal file
7
src/Python/uLib/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
try:
|
||||
from .uLib_python import Core, Math
|
||||
except ImportError:
|
||||
# Handle cases where the binary extension is not yet built
|
||||
pass
|
||||
|
||||
__all__ = ["Core", "Math"]
|
||||
Reference in New Issue
Block a user