aare/src/python/cpp/processing.hpp
Bechir Braham 68dcfca74e
Feature/reactivate python bindings (#74)
major changes:
- add python bindings for all c++ features except network_io
- changes to cross compile on windows,linux and macos
- fix bugs with cluster_finder
- use Dtype in Frame instead of bitdepth
- remove boost::program_options and replace with our implementation 
- add Transforms class that applies a sequence of functions (c++ or
python functions) on a Frame.
- remove frame reorder and flip from SubFile.cpp. use Transforms instead
- Test clusterFinder and Pedestal results in comparison with
slsDetectorCalibration

---------

Co-authored-by: Bechir <bechir.brahem420@gmail.com>
Co-authored-by: Erik Fröjdh <erik.frojdh@gmail.com>
2024-07-04 11:51:48 +02:00

160 lines
7.7 KiB
C++

#include <cstdint>
#include <filesystem>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <string>
#include "aare/core/Frame.hpp"
#include "aare/core/NDView.hpp"
#include "aare/core/defs.hpp"
#include "aare/file_io/ClusterFileV2.hpp"
#include "aare/file_io/File.hpp"
#include "aare/processing/ClusterFinder.hpp"
#include "aare/processing/Pedestal.hpp"
namespace py = pybind11;
template <typename T, typename SUM_TYPE> void define_pedestal_push_bindings(py::class_<Pedestal<SUM_TYPE>> &p) {
p.def("push", [](Pedestal<SUM_TYPE> &pedestal, py::array_t<T> &np_array) {
py::buffer_info info = np_array.request();
if (info.format != py::format_descriptor<T>::format())
throw std::runtime_error(
"Incompatible format: different formats! (Are you sure the arrays are of the same type?)");
if (info.ndim != 2)
throw std::runtime_error("Incompatible dimension: expected a 2D array!");
std::array<int64_t, 2> arr_shape;
std::move(info.shape.begin(), info.shape.end(), arr_shape.begin());
NDView<T, 2> a(static_cast<T *>(info.ptr), arr_shape);
pedestal.push(a);
});
p.def("push", [](Pedestal<SUM_TYPE> &pedestal, const int row, const int col, const T val) {
pedestal.push(row, col, val);
});
}
template <typename SUM_TYPE> void define_pedestal_bindings(py::module &m) {
auto p = py::class_<Pedestal<SUM_TYPE>>(m, "Pedestal");
// TODO: add DType to Frame so that we can define def_buffer()
// and we can know what type of values are stored in the frame
p.def(py::init<int, int, int>())
.def(py::init<int, int>())
.def("set_freeze", &Pedestal<SUM_TYPE>::set_freeze)
.def("mean", py::overload_cast<>(&Pedestal<SUM_TYPE>::mean))
.def("mean", [](Pedestal<SUM_TYPE> &pedestal, const uint32_t row, const uint32_t col) { return pedestal.mean(row, col); })
.def("variance", py::overload_cast<>(&Pedestal<SUM_TYPE>::variance))
.def("variance",
[](Pedestal<SUM_TYPE> &pedestal, const uint32_t row, const uint32_t col) { return pedestal.variance(row, col); })
.def("standard_deviation", py::overload_cast<>(&Pedestal<SUM_TYPE>::standard_deviation))
.def("standard_deviation", [](Pedestal<SUM_TYPE> &pedestal, const int row,
const int col) { return pedestal.standard_deviation(row, col); })
.def("clear", py::overload_cast<>(&Pedestal<SUM_TYPE>::clear))
.def("clear", py::overload_cast<const uint32_t, const uint32_t>(&Pedestal<SUM_TYPE>::clear))
.def_property_readonly("rows", &Pedestal<SUM_TYPE>::rows)
.def_property_readonly("cols", &Pedestal<SUM_TYPE>::cols)
.def_property_readonly("n_samples", &Pedestal<SUM_TYPE>::n_samples)
.def_property_readonly("index", &Pedestal<SUM_TYPE>::index)
.def_property_readonly("sum", &Pedestal<SUM_TYPE>::get_sum)
.def_property_readonly("sum2", &Pedestal<SUM_TYPE>::get_sum2);
p.def("push", [](Pedestal<SUM_TYPE> &pedestal, Frame &f) {
if (f.bitdepth() == 8) {
pedestal.template push<uint8_t>(f);
} else if (f.bitdepth() == 16) {
pedestal.template push<uint16_t>(f);
} else if (f.bitdepth() == 32) {
pedestal.template push<uint32_t>(f);
} else if (f.bitdepth() == 64) {
pedestal.template push<uint64_t>(f);
} else {
throw std::runtime_error("Unsupported bitdepth");
}
});
define_pedestal_push_bindings<uint8_t>(p);
define_pedestal_push_bindings<uint16_t>(p);
define_pedestal_push_bindings<uint32_t>(p);
define_pedestal_push_bindings<uint64_t>(p);
define_pedestal_push_bindings<int8_t>(p);
define_pedestal_push_bindings<int16_t>(p);
define_pedestal_push_bindings<int32_t>(p);
define_pedestal_push_bindings<int64_t>(p);
define_pedestal_push_bindings<float>(p);
define_pedestal_push_bindings<double>(p);
}
template <typename VIEW_TYPE, typename PEDESTAL_TYPE = double>
void define_cluster_finder_template_bindings(py::class_<ClusterFinder> &cf) {
cf.def("find_clusters_without_threshold",
py::overload_cast<NDView<VIEW_TYPE, 2>, Pedestal<PEDESTAL_TYPE> &, bool>(
&ClusterFinder::find_clusters_without_threshold<VIEW_TYPE, PEDESTAL_TYPE>));
cf.def("find_clusters_with_threshold", py::overload_cast<NDView<VIEW_TYPE, 2>, Pedestal<PEDESTAL_TYPE> &>(
&ClusterFinder::find_clusters_with_threshold<VIEW_TYPE, PEDESTAL_TYPE>));
cf.def("find_clusters_without_threshold", [](ClusterFinder &self, py::array_t<VIEW_TYPE> &np_array,
Pedestal<PEDESTAL_TYPE> &pedestal, bool late_update) {
py::buffer_info info = np_array.request();
if (info.format != Dtype(typeid(VIEW_TYPE)).numpy_descr())
throw std::runtime_error(
"Incompatible format: different formats! (Are you sure the arrays are of the same type?)");
if (info.ndim != 2)
throw std::runtime_error("Incompatible dimension: expected a 2D array!");
std::array<int64_t, 2> arr_shape;
std::copy(info.shape.begin(), info.shape.end(), arr_shape.begin());
NDView<VIEW_TYPE, 2> a(static_cast<VIEW_TYPE *>(info.ptr), arr_shape);
return self.find_clusters_without_threshold(a, pedestal, late_update);
});
cf.def("find_clusters_with_threshold",
[](ClusterFinder &self, py::array_t<VIEW_TYPE> &np_array, Pedestal<PEDESTAL_TYPE> &pedestal) {
py::buffer_info info = np_array.request();
if (info.format != py::format_descriptor<VIEW_TYPE>::format())
throw std::runtime_error(
"Incompatible format: different formats! (Are you sure the arrays are of the same type?)");
if (info.ndim != 2)
throw std::runtime_error("Incompatible dimension: expected a 2D array!");
std::array<int64_t, 2> arr_shape;
std::copy(info.shape.begin(), info.shape.end(), arr_shape.begin());
NDView<VIEW_TYPE, 2> a(static_cast<VIEW_TYPE *>(info.ptr), arr_shape);
return self.find_clusters_with_threshold(a, pedestal);
});
}
void define_cluster_finder_bindings(py::module &m) {
py::class_<ClusterFinder> cf(m, "ClusterFinder");
cf.def(py::init<int, int, double, double>());
define_cluster_finder_template_bindings<uint8_t>(cf);
define_cluster_finder_template_bindings<uint16_t>(cf);
define_cluster_finder_template_bindings<uint32_t>(cf);
define_cluster_finder_template_bindings<uint64_t>(cf);
define_cluster_finder_template_bindings<int8_t>(cf);
define_cluster_finder_template_bindings<int16_t>(cf);
define_cluster_finder_template_bindings<int32_t>(cf);
define_cluster_finder_template_bindings<int64_t>(cf);
define_cluster_finder_template_bindings<float>(cf);
define_cluster_finder_template_bindings<double>(cf);
}
void define_processing_bindings(py::module &m) {
define_pedestal_bindings<double>(m);
py::class_<Cluster>(m, "Cluster",py::buffer_protocol())
.def(py::init<int, int, Dtype>())
.def("size", &Cluster::size)
.def("begin", &Cluster::begin)
.def("end", &Cluster::end)
.def_readwrite("x", &Cluster::x)
.def_readwrite("y", &Cluster::y)
.def_buffer([](Cluster &c) -> py::buffer_info {
return py::buffer_info(c.data(), c.dt.bytes(), c.dt.format_descr(), 1, {c.size()}, {c.dt.bytes()});
})
.def("__repr__", [](const Cluster &a) {
return "<Cluster: x: " + std::to_string(a.x) + ", y: " + std::to_string(a.y) + ">";
});
define_cluster_finder_bindings(m);
}