From b1ac016c1d1cf915c6a067e1c35e0e8935f50d58 Mon Sep 17 00:00:00 2001 From: DinoBektesevic Date: Tue, 19 Sep 2023 13:36:32 -0700 Subject: [PATCH] Splitting bindings into multiple files. Proposal on how to split Pybind bindings into multiple files for readability. Implemented on the example of a PSF class. The code currently doesn't work because we can not have multiple entry point calls to the macro PYBIND11_MODULE in a single codebase. Or at least I don't think we can and nothing I tried worked. We certainly can not extend a once-declared module by repeatedly calling PYBIND11_MODULE, but seemingly we can not define a new one either. --- src/kbmod/__init__.py | 20 +++--- src/kbmod/image_base/LayeredImage.cpp | 16 ++--- src/kbmod/image_base/PointSpreadFunc.cpp | 16 ++--- src/kbmod/image_base/PointSpreadFunc.h | 41 +++++------- src/kbmod/image_base/RawImage.cpp | 10 +-- src/kbmod/image_base/bindings.cpp | 57 +++-------------- src/kbmod/image_base/psf_bindings.cpp | 79 ++++++++++++++++++++++++ 7 files changed, 136 insertions(+), 103 deletions(-) create mode 100644 src/kbmod/image_base/psf_bindings.cpp diff --git a/src/kbmod/__init__.py b/src/kbmod/__init__.py index c7c2df3e..8975ec40 100644 --- a/src/kbmod/__init__.py +++ b/src/kbmod/__init__.py @@ -8,13 +8,13 @@ # lazy import analysis to arrest # loading large libraries in them # import the filters subdirectory -from . import ( - analysis, - analysis_utils, - file_utils, - filters, - image_info, - jointfit_functions, - result_list, - run_search, -) +#from . import ( +# analysis, +# analysis_utils, +# file_utils, +# filters, +# image_info, +# jointfit_functions, +# result_list, +# run_search, +#) diff --git a/src/kbmod/image_base/LayeredImage.cpp b/src/kbmod/image_base/LayeredImage.cpp index bef5253f..a58c32de 100644 --- a/src/kbmod/image_base/LayeredImage.cpp +++ b/src/kbmod/image_base/LayeredImage.cpp @@ -10,7 +10,7 @@ namespace image_base { LayeredImage::LayeredImage(std::string path, const PointSpreadFunc& psf) : psf(psf), psfSQ(psf) { - psfSQ.squarePSF(); + psfSQ.square_psf(); int fBegin = path.find_last_of("/"); int fEnd = path.find_last_of(".fits") - 4; @@ -46,7 +46,7 @@ LayeredImage::LayeredImage(const RawImage& sci, const RawImage& var, const RawIm throw std::runtime_error("Science and Mask layers are not the same size."); // Set the remaining variables. - psfSQ.squarePSF(); + psfSQ.square_psf(); // Copy the image layers. science = sci; @@ -64,7 +64,7 @@ LayeredImage::LayeredImage(std::string name, int w, int h, float noiseStDev, flo fileName = name; width = w; height = h; - psfSQ.squarePSF(); + psfSQ.square_psf(); std::vector rawSci(width * height); std::random_device r; @@ -85,14 +85,14 @@ LayeredImage::LayeredImage(std::string name, int w, int h, float noiseStDev, flo void LayeredImage::setPSF(const PointSpreadFunc& new_psf) { psf = new_psf; psfSQ = new_psf; - psfSQ.squarePSF(); + psfSQ.square_psf(); } void LayeredImage::addObject(float x, float y, float flux) { - const std::vector& k = psf.getKernel(); - int dim = psf.getDim(); - float initialX = x - static_cast(psf.getRadius()); - float initialY = y - static_cast(psf.getRadius()); + const std::vector& k = psf.get_kernel(); + int dim = psf.get_dim(); + float initialX = x - static_cast(psf.get_radius()); + float initialY = y - static_cast(psf.get_radius()); int count = 0; for (int i = 0; i < dim; ++i) { diff --git a/src/kbmod/image_base/PointSpreadFunc.cpp b/src/kbmod/image_base/PointSpreadFunc.cpp index e004473b..a9249610 100644 --- a/src/kbmod/image_base/PointSpreadFunc.cpp +++ b/src/kbmod/image_base/PointSpreadFunc.cpp @@ -40,7 +40,7 @@ PointSpreadFunc::PointSpreadFunc(float stdev) { kernel.push_back(current); } } - calcSum(); + calc_sum(); } // Copy constructor. @@ -83,9 +83,9 @@ PointSpreadFunc& PointSpreadFunc::operator=(PointSpreadFunc&& other) { } #ifdef Py_PYTHON_H -PointSpreadFunc::PointSpreadFunc(pybind11::array_t arr) { setArray(arr); } +PointSpreadFunc::PointSpreadFunc(pybind11::array_t arr) { set_array(arr); } -void PointSpreadFunc::setArray(pybind11::array_t arr) { +void PointSpreadFunc::set_array(pybind11::array_t arr) { pybind11::buffer_info info = arr.request(); if (info.ndim != 2) @@ -106,24 +106,24 @@ void PointSpreadFunc::setArray(pybind11::array_t arr) { radius = dim / 2; // Rounds down sum = 0.0; kernel = std::vector(pix, pix + dim * dim); - calcSum(); + calc_sum(); width = 0.0; } #endif -void PointSpreadFunc::calcSum() { +void PointSpreadFunc::calc_sum() { sum = 0.0; for (auto& i : kernel) sum += i; } -void PointSpreadFunc::squarePSF() { +void PointSpreadFunc::square_psf() { for (float& i : kernel) { i = i * i; } - calcSum(); + calc_sum(); } -std::string PointSpreadFunc::printPSF() { +std::string PointSpreadFunc::print() { std::stringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(3); diff --git a/src/kbmod/image_base/PointSpreadFunc.h b/src/kbmod/image_base/PointSpreadFunc.h index d1dec713..f9e47647 100644 --- a/src/kbmod/image_base/PointSpreadFunc.h +++ b/src/kbmod/image_base/PointSpreadFunc.h @@ -15,24 +15,18 @@ #include #include #include -#ifdef Py_PYTHON_H -#include -#include -#include -#endif - #include "common.h" -namespace image_base { -class PointSpreadFunc { -public: +namespace image_base { + class PointSpreadFunc { + public: PointSpreadFunc(float stdev); PointSpreadFunc(const PointSpreadFunc& other); // Copy constructor PointSpreadFunc(PointSpreadFunc&& other); // Move constructor #ifdef Py_PYTHON_H PointSpreadFunc(pybind11::array_t arr); - void setArray(pybind11::array_t arr); + void set_array(pybind11::array_t arr); #endif virtual ~PointSpreadFunc(){}; @@ -41,28 +35,27 @@ class PointSpreadFunc { PointSpreadFunc& operator=(PointSpreadFunc&& other); // Move assignment // Getter functions (inlined) - float getStdev() const { return width; } - float getSum() const { return sum; } - float getValue(int x, int y) const { return kernel[y * dim + x]; } - int getDim() const { return dim; } - int getRadius() const { return radius; } - int getSize() const { return kernel.size(); } - const std::vector& getKernel() const { return kernel; }; - float* kernelData() { return kernel.data(); } + float get_stdev() const { return width; } + float get_sum() const { return sum; } + float get_value(int x, int y) const { return kernel[y * dim + x]; } + int get_dim() const { return dim; } + int get_radius() const { return radius; } + int get_size() const { return kernel.size(); } + const std::vector& get_kernel() const { return kernel; }; + float* data() { return kernel.data(); } // Computation functions. - void calcSum(); - void squarePSF(); - std::string printPSF(); + void calc_sum(); + void square_psf(); + std::string print(); -private: + private: std::vector kernel; float width; float sum; int dim; int radius; -}; - + }; } /* namespace image_base */ #endif /* POINTSPREADFUNC_H_ */ diff --git a/src/kbmod/image_base/RawImage.cpp b/src/kbmod/image_base/RawImage.cpp index 597b83bd..55acfca0 100644 --- a/src/kbmod/image_base/RawImage.cpp +++ b/src/kbmod/image_base/RawImage.cpp @@ -232,8 +232,8 @@ RawImage RawImage::createStamp(float x, float y, int radius, bool interpolate, b void RawImage::convolve_cpu(const PointSpreadFunc& psf) { std::vector result(width * height, 0.0); - const int psfRad = psf.getRadius(); - const float psfTotal = psf.getSum(); + const int psfRad = psf.get_radius(); + const float psfTotal = psf.get_sum(); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { @@ -250,7 +250,7 @@ void RawImage::convolve_cpu(const PointSpreadFunc& psf) { if ((x + i >= 0) && (x + i < width) && (y + j >= 0) && (y + j < height)) { float currentPixel = pixels[(y + j) * width + (x + i)]; if (currentPixel != NO_DATA) { - float currentPSF = psf.getValue(i + psfRad, j + psfRad); + float currentPSF = psf.get_value(i + psfRad, j + psfRad); psfPortion += currentPSF; sum += currentPixel * currentPSF; } @@ -270,8 +270,8 @@ void RawImage::convolve_cpu(const PointSpreadFunc& psf) { void RawImage::convolve(PointSpreadFunc psf) { #ifdef HAVE_CUDA - deviceConvolve(pixels.data(), pixels.data(), getWidth(), getHeight(), psf.kernelData(), - psf.getSize(), psf.getDim(), psf.getRadius(), psf.getSum()); + deviceConvolve(pixels.data(), pixels.data(), getWidth(), getHeight(), psf.data(), + psf.get_size(), psf.get_dim(), psf.get_radius(), psf.get_sum()); #else convolve_cpu(psf); #endif diff --git a/src/kbmod/image_base/bindings.cpp b/src/kbmod/image_base/bindings.cpp index c0e68723..595a0418 100644 --- a/src/kbmod/image_base/bindings.cpp +++ b/src/kbmod/image_base/bindings.cpp @@ -4,12 +4,17 @@ #include "common.h" #include "PointSpreadFunc.cpp" +#include "psf_bindings.cpp" #include "RawImage.cpp" #include "LayeredImage.cpp" #include "ImageStack.cpp" namespace py = pybind11; +// this has to be here because all the other bindings +// f.e. for LayeredImage(PSF) - but in practice this goes +// away into individual bindings alongisde with the import +// at the top. using pf = image_base::PointSpreadFunc; using ri = image_base::RawImage; using li = image_base::LayeredImage; @@ -17,55 +22,11 @@ using is = image_base::ImageStack; using std::to_string; -PYBIND11_MODULE(search, m) { - m.attr("KB_NO_DATA") = pybind11::float_(NO_DATA); - m.attr("HAS_GPU") = pybind11::bool_(HAVE_GPU); - py::class_(m, "psf", py::buffer_protocol(), R"pbdoc( - Point Spread Function. +PYBIND11_MODULE(image_base, m) { + m.attr("KB_NO_DATA") = pybind11::float_(NO_DATA); + m.attr("HAS_GPU") = pybind11::bool_(HAVE_GPU); - Parameters - ---------- - arg : `float`, `numpy.array` or `psf` - Given value represents one of: - - * standard deviation of a Gaussian PSF (`float`) - * kernel representing the PSF (array-like) - * another `psf` object. - - - Notes - ----- - When instantiated with another `psf` object, returns its copy. - - When instantiated with an array-like object, that array must be - a square matrix and have an odd number of dimensions - )pbdoc") - .def_buffer([](pf &m) -> py::buffer_info { - return py::buffer_info(m.kernelData(), sizeof(float), py::format_descriptor::format(), - 2, {m.getDim(), m.getDim()}, - {sizeof(float) * m.getDim(), sizeof(float)}); - }) - .def(py::init()) - .def(py::init>()) - .def(py::init()) - .def("set_array", &pf::setArray, R"pbdoc( - Sets the PSF kernel. - - Parameters - ---------- - arr : `numpy.array` - Numpy array representing the PSF. - )pbdoc") - .def("get_stdev", &pf::getStdev, "Returns the PSF's standard deviation.") - .def("get_sum", &pf::getSum, "Returns the sum of PSFs kernel elements.") - .def("get_dim", &pf::getDim, "Returns the PSF kernel dimensions.") - .def("get_radius", &pf::getRadius, "Returns the radius of the PSF") - .def("get_size", &pf::getSize, "Returns the number of elements in the PSFs kernel.") - .def("get_kernel", &pf::getKernel, "Returns the PSF kernel.") - .def("get_value", &pf::getValue, "Returns the PSF kernel value at a specific point.") - .def("square_psf", &pf::squarePSF, - "Squares, raises to the power of two, the elements of the PSF kernel.") - .def("print_psf", &pf::printPSF, "Pretty-prints the PSF."); + image_base_bindings::psf_bindings_factory(m); py::class_(m, "raw_image", py::buffer_protocol()) .def_buffer([](ri &m) -> py::buffer_info { diff --git a/src/kbmod/image_base/psf_bindings.cpp b/src/kbmod/image_base/psf_bindings.cpp new file mode 100644 index 00000000..15c59a98 --- /dev/null +++ b/src/kbmod/image_base/psf_bindings.cpp @@ -0,0 +1,79 @@ +#ifndef PSF_BINDINGS +#define PSF_BINDINGS + +#include +#include +#include + +#include "PointSpreadFunc.h" + + +namespace py = pybind11; + + +namespace image_base_bindings { + + static const auto DOC_PSF = R"doc( + Point Spread Function. + + Parameters + ---------- + stdev : `float`, optional + Standard deviation of the Gaussian PSF. + psf : `PointSpreadFunc`, optional + Another PSF object. + arr : `numpy.array`, optional + A realization of the PSF. + + Notes + ----- + When instantiated with another `psf` object, returns its copy. + When instantiated with an array-like object, that array must be + a square matrix and have an odd number of dimensions. Only one + of the arguments is required. + )doc"; + + + static const auto DOC_PSF_set_array = R"doc( + Set the kernel values of a realized PSF. + + Parameters + ---------- + arr : `numpy.array` + A realization of the PSF. + + Notes + ----- + Given realization of a PSF has to be an odd-dimensional square + matrix. + )doc"; + + static void psf_bindings_factory(py::module &m) { + + using psf = image_base::PointSpreadFunc; + + py::class_(m, "PSF", py::buffer_protocol(), DOC_PSF) + .def_buffer([](psf &m) -> py::buffer_info { + return py::buffer_info(m.data(), sizeof(float), py::format_descriptor::format(), + 2, {m.get_dim(), m.get_dim()}, + {sizeof(float) * m.get_dim(), sizeof(float)}); + }) + .def(py::init()) + .def(py::init>()) + .def(py::init()) + .def("set_array", &psf::set_array, DOC_PSF_set_array) + .def("get_stdev", &psf::get_stdev, "Returns the PSF's standard deviation.") + .def("get_sum", &psf::get_sum, "Returns the sum of PSFs kernel elements.") + .def("get_dim", &psf::get_dim, "Returns the PSF kernel dimensions.") + .def("get_radius", &psf::get_radius, "Returns the radius of the PSF") + .def("get_size", &psf::get_size, "Returns the number of elements in the PSFs kernel.") + .def("get_kernel", &psf::get_kernel, "Returns the PSF kernel.") + .def("get_value", &psf::get_value, "Returns the PSF kernel value at a specific point.") + .def("square_psf", &psf::square_psf, + "Squares, raises to the power of two, the elements of the PSF kernel.") + .def("print_psf", &psf::print, "Pretty-prints the PSF."); + } + +} /* namesapce image_base_bindings */ + +#endif /* PSF_BINDINGS */