Skip to content

Commit

Permalink
Splitting bindings into multiple files.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DinoBektesevic committed Sep 19, 2023
1 parent 16f58e4 commit b1ac016
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 103 deletions.
20 changes: 10 additions & 10 deletions src/kbmod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
#)
16 changes: 8 additions & 8 deletions src/kbmod/image_base/LayeredImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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<float> rawSci(width * height);
std::random_device r;
Expand All @@ -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<float>& k = psf.getKernel();
int dim = psf.getDim();
float initialX = x - static_cast<float>(psf.getRadius());
float initialY = y - static_cast<float>(psf.getRadius());
const std::vector<float>& k = psf.get_kernel();
int dim = psf.get_dim();
float initialX = x - static_cast<float>(psf.get_radius());
float initialY = y - static_cast<float>(psf.get_radius());

int count = 0;
for (int i = 0; i < dim; ++i) {
Expand Down
16 changes: 8 additions & 8 deletions src/kbmod/image_base/PointSpreadFunc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ PointSpreadFunc::PointSpreadFunc(float stdev) {
kernel.push_back(current);
}
}
calcSum();
calc_sum();
}

// Copy constructor.
Expand Down Expand Up @@ -83,9 +83,9 @@ PointSpreadFunc& PointSpreadFunc::operator=(PointSpreadFunc&& other) {
}

#ifdef Py_PYTHON_H
PointSpreadFunc::PointSpreadFunc(pybind11::array_t<float> arr) { setArray(arr); }
PointSpreadFunc::PointSpreadFunc(pybind11::array_t<float> arr) { set_array(arr); }

void PointSpreadFunc::setArray(pybind11::array_t<float> arr) {
void PointSpreadFunc::set_array(pybind11::array_t<float> arr) {
pybind11::buffer_info info = arr.request();

if (info.ndim != 2)
Expand All @@ -106,24 +106,24 @@ void PointSpreadFunc::setArray(pybind11::array_t<float> arr) {
radius = dim / 2; // Rounds down
sum = 0.0;
kernel = std::vector<float>(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);
Expand Down
41 changes: 17 additions & 24 deletions src/kbmod/image_base/PointSpreadFunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,18 @@
#include <sstream>
#include <vector>
#include <stdexcept>
#ifdef Py_PYTHON_H
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#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<float> arr);
void setArray(pybind11::array_t<float> arr);
void set_array(pybind11::array_t<float> arr);
#endif
virtual ~PointSpreadFunc(){};

Expand All @@ -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<float>& 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<float>& 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<float> kernel;
float width;
float sum;
int dim;
int radius;
};

};
} /* namespace image_base */

#endif /* POINTSPREADFUNC_H_ */
10 changes: 5 additions & 5 deletions src/kbmod/image_base/RawImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<float> 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) {
Expand All @@ -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;
}
Expand All @@ -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
Expand Down
57 changes: 9 additions & 48 deletions src/kbmod/image_base/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,29 @@

#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;
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_<pf>(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<float>::format(),
2, {m.getDim(), m.getDim()},
{sizeof(float) * m.getDim(), sizeof(float)});
})
.def(py::init<float>())
.def(py::init<py::array_t<float>>())
.def(py::init<pf &>())
.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_<ri>(m, "raw_image", py::buffer_protocol())
.def_buffer([](ri &m) -> py::buffer_info {
Expand Down
79 changes: 79 additions & 0 deletions src/kbmod/image_base/psf_bindings.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#ifndef PSF_BINDINGS
#define PSF_BINDINGS

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>

#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_<psf>(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<float>::format(),
2, {m.get_dim(), m.get_dim()},
{sizeof(float) * m.get_dim(), sizeof(float)});
})
.def(py::init<float>())
.def(py::init<py::array_t<float>>())
.def(py::init<psf &>())
.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 */

0 comments on commit b1ac016

Please sign in to comment.