mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-09-03 01:00:43 +02:00
Merge branch 'main' into dev/cluster-vector
This commit is contained in:
@@ -39,7 +39,9 @@ set(PYTHON_FILES
|
||||
aare/ClusterFinder.py
|
||||
aare/ClusterVector.py
|
||||
aare/Cluster.py
|
||||
aare/FastPedestal.py
|
||||
aare/calibration.py
|
||||
aare/factory.py
|
||||
aare/experimental.py
|
||||
aare/RawFile.py
|
||||
aare/transform.py
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from . import _aare
|
||||
import numpy as np
|
||||
from .ClusterFinder import _type_to_char
|
||||
from .factory import _type_to_char
|
||||
|
||||
|
||||
def Cluster(x : int, y : int, data, cluster_size=(3,3), dtype = np.int32):
|
||||
@@ -21,4 +21,4 @@ def Cluster(x : int, y : int, data, cluster_size=(3,3), dtype = np.int32):
|
||||
except AttributeError:
|
||||
raise ValueError(f"Unsupported combination of type and cluster size: {dtype}/{cluster_size} when requesting {class_name}")
|
||||
|
||||
return cls(x, y, data)
|
||||
return cls(x, y, data)
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
from . import _aare
|
||||
import numpy as np
|
||||
from .factory import _type_to_char
|
||||
|
||||
_supported_cluster_sizes = [(2,2), (3,3), (5,5), (7,7), (9,9),]
|
||||
|
||||
def _type_to_char(dtype):
|
||||
if dtype == np.int32:
|
||||
return 'i'
|
||||
elif dtype == np.float32:
|
||||
return 'f'
|
||||
elif dtype == np.float64:
|
||||
return 'd'
|
||||
elif dtype == np.int16:
|
||||
return 'i16'
|
||||
else:
|
||||
raise ValueError(f"Unsupported dtype: {dtype}. Only np.int32, np.float32, and np.float64 are supported.")
|
||||
|
||||
def _get_class(name, cluster_size, dtype):
|
||||
"""
|
||||
Helper function to get the class based on the name, cluster size, and dtype.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .factory import _get_typed_class
|
||||
|
||||
|
||||
def _get_fast_pedestal_class(dtype):
|
||||
return _get_typed_class("FastPedestal", dtype)
|
||||
|
||||
|
||||
def FastPedestal(rows, cols, n_samples=1000, dtype=np.float64):
|
||||
"""Create an empty per-pixel running pedestal.
|
||||
|
||||
This factory hides the dtype suffix used by the templated C++ bindings.
|
||||
Call ``add_init_frame()`` exactly ``n_samples`` times before using the
|
||||
statistics or calling ``push_ema()``. Subsequent frames have weight
|
||||
``1 / n_samples`` in the running mean and population variance.
|
||||
|
||||
Args:
|
||||
rows: Number of image rows.
|
||||
cols: Number of image columns.
|
||||
n_samples: Initialization frame count and steady-state update-weight
|
||||
denominator.
|
||||
dtype: Output dtype for the mean, variance, and standard deviation.
|
||||
Supported values are ``np.float64``, ``np.float32``, and
|
||||
``np.int16``.
|
||||
"""
|
||||
cls = _get_fast_pedestal_class(dtype)
|
||||
return cls(rows, cols, n_samples)
|
||||
|
||||
|
||||
def from_file(filename, n_samples=1000, skip_first=0, dtype=np.float64):
|
||||
"""Create a FastPedestal from frames in a file.
|
||||
|
||||
After ignoring ``skip_first`` frames, the next ``n_samples`` frames
|
||||
initialize the pedestal. Every remaining frame is then applied as a
|
||||
steady-state update. Input frames are read as uint16 data.
|
||||
|
||||
Args:
|
||||
filename: Input image file.
|
||||
n_samples: Number of frames used for initialization.
|
||||
skip_first: Number of leading frames to ignore.
|
||||
dtype: Output dtype for the mean, variance, and standard deviation.
|
||||
|
||||
Raises:
|
||||
RuntimeError: If fewer than ``n_samples`` frames remain after
|
||||
``skip_first`` or ``n_samples`` is zero.
|
||||
"""
|
||||
cls = _get_fast_pedestal_class(dtype)
|
||||
return cls.from_file(
|
||||
filename, n_samples=n_samples, skip_first=skip_first
|
||||
)
|
||||
|
||||
|
||||
FastPedestal.from_file = from_file
|
||||
@@ -5,6 +5,16 @@ from . import _aare
|
||||
from . import transform
|
||||
from . import experimental
|
||||
|
||||
from ._aare import (
|
||||
FastPedestal_d,
|
||||
FastPedestal_f,
|
||||
FastPedestal_i16,
|
||||
Pedestal_d,
|
||||
Pedestal_f,
|
||||
Pedestal_i16,
|
||||
ClusterFinder_Cluster3x3i,
|
||||
VarClusterFinder,
|
||||
)
|
||||
from ._aare import (
|
||||
File,
|
||||
JungfrauDataFile,
|
||||
@@ -20,6 +30,7 @@ from ._aare import corner
|
||||
# from ._aare import ClusterFinderMT, ClusterCollector, ClusterFileSink, ClusterVector_i
|
||||
|
||||
from ._version import __version__
|
||||
from .FastPedestal import FastPedestal
|
||||
from .ClusterFinder import ClusterFinder, ClusterCollector, ClusterFinderMT, ClusterFileSink, ClusterFile
|
||||
from .ClusterVector import ClusterVector
|
||||
from .Cluster import Cluster
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import numpy as np
|
||||
|
||||
from . import _aare
|
||||
|
||||
|
||||
_TYPE_TO_CHAR = {
|
||||
np.dtype(np.int32): "i",
|
||||
np.dtype(np.float32): "f",
|
||||
np.dtype(np.float64): "d",
|
||||
np.dtype(np.int16): "i16",
|
||||
}
|
||||
|
||||
|
||||
def _type_to_char(dtype):
|
||||
"""Return the suffix used by bindings instantiated for ``dtype``."""
|
||||
try:
|
||||
return _TYPE_TO_CHAR[np.dtype(dtype)]
|
||||
except (KeyError, TypeError):
|
||||
supported = ", ".join(str(dtype) for dtype in _TYPE_TO_CHAR)
|
||||
raise ValueError(
|
||||
f"Unsupported dtype: {dtype}. Supported dtypes are {supported}."
|
||||
) from None
|
||||
|
||||
|
||||
def _get_typed_class(name, dtype):
|
||||
"""Return a bound class named ``<name>_<dtype suffix>``."""
|
||||
class_name = f"{name}_{_type_to_char(dtype)}"
|
||||
try:
|
||||
return getattr(_aare, class_name)
|
||||
except AttributeError:
|
||||
raise ValueError(
|
||||
f"Unsupported dtype for {name}: {dtype} "
|
||||
f"(binding {class_name} is not available)."
|
||||
) from None
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
using namespace aare;
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/Pedestal.hpp"
|
||||
|
||||
#include "module_config.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -15,7 +17,6 @@
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
using namespace aare;
|
||||
|
||||
@@ -30,7 +31,7 @@ void define_ClusterCollector(py::module &m, const std::string &typestr) {
|
||||
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
|
||||
|
||||
py::class_<ClusterCollector<ClusterType>>(m, class_name.c_str())
|
||||
.def(py::init<ClusterFinderMT<ClusterType, uint16_t, double> *>())
|
||||
.def(py::init<ClusterFinderMT<ClusterType, uint16_t, pd_type> *>())
|
||||
.def("stop", &ClusterCollector<ClusterType>::stop)
|
||||
.def(
|
||||
"steal_clusters",
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
using namespace aare;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
@@ -29,6 +27,8 @@ void define_ClusterFileSink(py::module &m, const std::string &typestr) {
|
||||
|
||||
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
|
||||
|
||||
// TODO! adapt to set pedestal type (needs templating of ClusterFileSink)
|
||||
// or maybe access through base class?
|
||||
py::class_<ClusterFileSink<ClusterType>>(m, class_name.c_str())
|
||||
.def(py::init<ClusterFinderMT<ClusterType, uint16_t, double> *,
|
||||
const std::filesystem::path &>())
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/Pedestal.hpp"
|
||||
|
||||
#include "module_config.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -15,7 +17,6 @@
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
using namespace aare;
|
||||
|
||||
@@ -48,6 +49,8 @@ void define_ClusterFinder(py::module &m, const std::string &typestr) {
|
||||
})
|
||||
.def("clear_pedestal",
|
||||
&ClusterFinder<ClusterType, uint16_t, pd_type>::clear_pedestal)
|
||||
.def("update_threshold",
|
||||
&ClusterFinder<ClusterType, uint16_t, pd_type>::update_threshold)
|
||||
.def_property_readonly(
|
||||
"pedestal",
|
||||
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self) {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/Pedestal.hpp"
|
||||
|
||||
#include "module_config.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -15,7 +17,6 @@
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
using namespace aare;
|
||||
|
||||
@@ -31,9 +32,10 @@ void define_ClusterFinderMT(py::module &m, const std::string &typestr) {
|
||||
|
||||
py::class_<ClusterFinderMT<ClusterType, uint16_t, pd_type>>(
|
||||
m, class_name.c_str())
|
||||
.def(py::init<Shape<2>, pd_type, size_t, size_t>(),
|
||||
.def(py::init<Shape<2>, pd_type, size_t, size_t, size_t>(),
|
||||
py::arg("image_size"), py::arg("n_sigma") = 5.0,
|
||||
py::arg("capacity") = 2048, py::arg("n_threads") = 3)
|
||||
py::arg("capacity") = 2048, py::arg("n_threads") = 3,
|
||||
py::arg("queue_depth") = 16)
|
||||
.def("push_pedestal_frame",
|
||||
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
|
||||
py::array_t<uint16_t> frame) {
|
||||
@@ -46,7 +48,6 @@ void define_ClusterFinderMT(py::module &m, const std::string &typestr) {
|
||||
py::array_t<uint16_t> frame, uint64_t frame_number) {
|
||||
auto view = make_view_2d(frame);
|
||||
self.find_clusters(view, frame_number);
|
||||
return;
|
||||
},
|
||||
py::arg(), py::arg("frame_number") = 0)
|
||||
.def_property_readonly(
|
||||
@@ -56,6 +57,8 @@ void define_ClusterFinderMT(py::module &m, const std::string &typestr) {
|
||||
})
|
||||
.def("clear_pedestal",
|
||||
&ClusterFinderMT<ClusterType, uint16_t, pd_type>::clear_pedestal)
|
||||
.def("update_threshold",
|
||||
&ClusterFinderMT<ClusterType, uint16_t, pd_type>::update_threshold)
|
||||
.def("sync", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::sync)
|
||||
.def("stop", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::stop)
|
||||
.def("start", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::start)
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
using namespace aare;
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#include "aare/FastPedestal.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <pybind11/numpy.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
template <typename SUM_TYPE>
|
||||
void define_fast_pedestal_bindings(py::module &m, const std::string &name) {
|
||||
|
||||
py::class_<FastPedestal<SUM_TYPE>>(
|
||||
m, name.c_str(),
|
||||
"Maintain a per-pixel running mean, population variance, and "
|
||||
"standard deviation.",
|
||||
py::buffer_protocol())
|
||||
.def(py::init<uint32_t, uint32_t, uint32_t>(), py::arg("rows"),
|
||||
py::arg("cols"), py::arg("n_samples"),
|
||||
"Construct an empty pedestal. It becomes ready after n_samples "
|
||||
"calls to add_init_frame().")
|
||||
.def(py::init<uint32_t, uint32_t>(), py::arg("rows"), py::arg("cols"),
|
||||
"Construct an empty pedestal with n_samples=1000.")
|
||||
|
||||
.def(
|
||||
"mean",
|
||||
[](FastPedestal<SUM_TYPE> &self) {
|
||||
auto mean = new NDArray<SUM_TYPE, 2>{};
|
||||
*mean = self.mean();
|
||||
return return_image_data(mean);
|
||||
},
|
||||
"Return a copy of the cached mean. The pedestal must be ready.")
|
||||
.def(
|
||||
"var",
|
||||
[](FastPedestal<SUM_TYPE> &self) {
|
||||
auto variance = new NDArray<SUM_TYPE, 2>{};
|
||||
*variance = self.variance();
|
||||
return return_image_data(variance);
|
||||
},
|
||||
"Return the population variance, normalized by n_samples, as a "
|
||||
"NumPy array. The pedestal must be ready.")
|
||||
.def(
|
||||
"std",
|
||||
[](FastPedestal<SUM_TYPE> &self) {
|
||||
auto standard_deviation = new NDArray<SUM_TYPE, 2>{};
|
||||
*standard_deviation = self.std();
|
||||
return return_image_data(standard_deviation);
|
||||
},
|
||||
"Return the population standard deviation as a NumPy array. The "
|
||||
"pedestal must be ready.")
|
||||
.def(
|
||||
"view",
|
||||
[](py::object self_py) {
|
||||
return py::module_::import("numpy").attr("asarray")(self_py);
|
||||
},
|
||||
"Return a non-owning, non-writable NumPy view of the cached mean. "
|
||||
"The pedestal must be ready.")
|
||||
|
||||
// We need to buffer protocol to allow for numpy operations using the
|
||||
// pedestal mean
|
||||
.def_buffer([](FastPedestal<SUM_TYPE> &self) {
|
||||
auto mean = self.view();
|
||||
return py::buffer_info(
|
||||
const_cast<SUM_TYPE *>(mean.data()), sizeof(SUM_TYPE),
|
||||
py::format_descriptor<SUM_TYPE>::format(), 2,
|
||||
{static_cast<py::ssize_t>(mean.shape(0)),
|
||||
static_cast<py::ssize_t>(mean.shape(1))},
|
||||
{static_cast<py::ssize_t>(mean.strides()[0] * sizeof(SUM_TYPE)),
|
||||
static_cast<py::ssize_t>(mean.strides()[1] *
|
||||
sizeof(SUM_TYPE))},
|
||||
true);
|
||||
})
|
||||
// Subtracting a FastPedestal from a NumPy array
|
||||
.def(
|
||||
"__array_ufunc__",
|
||||
[](py::object self, py::object ufunc, const std::string &method,
|
||||
py::args inputs, py::kwargs kwargs) -> py::object {
|
||||
if (method != "__call__" || inputs.size() != 2 ||
|
||||
inputs[1].ptr() != self.ptr() ||
|
||||
py::cast<std::string>(ufunc.attr("__name__")) !=
|
||||
"subtract") {
|
||||
return py::reinterpret_borrow<py::object>(
|
||||
Py_NotImplemented);
|
||||
}
|
||||
|
||||
auto mean =
|
||||
py::module_::import("builtins").attr("memoryview")(self);
|
||||
return ufunc(inputs[0], mean, **kwargs);
|
||||
},
|
||||
"Support subtracting a FastPedestal from a NumPy array.")
|
||||
.def("clear", py::overload_cast<>(&FastPedestal<SUM_TYPE>::clear),
|
||||
"Reset all statistics and initialization state to zero.")
|
||||
.def_property_readonly("rows", &FastPedestal<SUM_TYPE>::rows,
|
||||
"Number of image rows.")
|
||||
.def_property_readonly("cols", &FastPedestal<SUM_TYPE>::cols,
|
||||
"Number of image columns.")
|
||||
.def_property_readonly("cur_samples",
|
||||
&FastPedestal<SUM_TYPE>::cur_samples,
|
||||
"Number of initialization frames accumulated. "
|
||||
"Steady-state pushes do not change it.")
|
||||
.def_property_readonly(
|
||||
"ready", &FastPedestal<SUM_TYPE>::ready,
|
||||
"Whether n_samples initialization frames have been accumulated.")
|
||||
.def_property_readonly(
|
||||
"n_samples", &FastPedestal<SUM_TYPE>::n_samples,
|
||||
"Initialization frame count and steady-state update-weight "
|
||||
"denominator.")
|
||||
.def(
|
||||
"clone",
|
||||
[](FastPedestal<SUM_TYPE> &pedestal) {
|
||||
return FastPedestal<SUM_TYPE>(pedestal);
|
||||
},
|
||||
"Return an independent copy of the pedestal and its state.")
|
||||
.def(
|
||||
"push_ema",
|
||||
[](FastPedestal<SUM_TYPE> &pedestal,
|
||||
py::array_t<uint16_t, py::array::c_style> &frame) {
|
||||
pedestal.push_ema(make_view_2d(frame));
|
||||
},
|
||||
py::arg("frame").noconvert(),
|
||||
"Update exponential moving average. The pedstal must "
|
||||
"already be ready for this update.")
|
||||
.def(
|
||||
"add_init_frame",
|
||||
[](FastPedestal<SUM_TYPE> &pedestal,
|
||||
py::array_t<uint16_t, py::array::c_style> &frame) {
|
||||
pedestal.add_init_frame(make_view_2d(frame));
|
||||
},
|
||||
py::arg("frame").noconvert(),
|
||||
"Accumulate one uint16 initialization frame. Call exactly "
|
||||
"n_samples times to make the pedestal ready.")
|
||||
.def_static(
|
||||
"from_file",
|
||||
[](const std::filesystem::path &filename, uint32_t n_samples,
|
||||
uint32_t skip_first) {
|
||||
return FastPedestal<SUM_TYPE>::template from_file<uint16_t>(
|
||||
filename, n_samples, skip_first);
|
||||
},
|
||||
py::arg("filename"), py::arg("n_samples") = 1000,
|
||||
py::arg("skip_first") = 0,
|
||||
"Create a pedestal from a uint16 file. Skip skip_first frames, use "
|
||||
"the next n_samples for initialization, then apply every remaining "
|
||||
"frame as a steady-state update.");
|
||||
}
|
||||
@@ -153,9 +153,13 @@ void define_pedestal_tracking_pixel_histogram_bindings(py::module &m) {
|
||||
Args:
|
||||
file_path: Path to the file to fill from
|
||||
max_frames: Maximum number of frames to fill from the file (default: -1)
|
||||
reader_threads: Number of parallel file reader workers (default: 2)
|
||||
reader_chunk_size: Frames claimed by each reader worker per batch (default: 4)
|
||||
)",
|
||||
py::call_guard<py::gil_scoped_release>(), py::arg("fname"),
|
||||
py::arg("max_frames") = -1, py::arg("verbose") = false)
|
||||
py::arg("max_frames") = -1, py::arg("verbose") = false,
|
||||
py::arg("reader_threads") = std::size_t{2},
|
||||
py::arg("reader_chunk_size") = std::size_t{4})
|
||||
.def("process_pedestal_file",
|
||||
&PedestalTrackingPixelHistogram::process_pedestal_file,
|
||||
R"(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Files with bindings to the different classes
|
||||
|
||||
#include "module_config.hpp"
|
||||
|
||||
// New style file naming
|
||||
#include "bind_Cluster.hpp"
|
||||
#include "bind_ClusterCollector.hpp"
|
||||
@@ -11,6 +13,7 @@
|
||||
#include "bind_ClusterVector.hpp"
|
||||
#include "bind_Defs.hpp"
|
||||
#include "bind_Eta.hpp"
|
||||
#include "bind_FastPedestal.hpp"
|
||||
#include "bind_Interpolator.hpp"
|
||||
#include "bind_MultiThreadedFileReader.hpp"
|
||||
#include "bind_PedestalTrackingPixelHistogram.hpp"
|
||||
@@ -30,6 +33,7 @@
|
||||
#include "var_cluster.hpp"
|
||||
|
||||
// Pybind stuff
|
||||
#include <cstdint>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
@@ -75,6 +79,10 @@ PYBIND11_MODULE(_aare, m) {
|
||||
define_pedestal_tracking_pixel_histogram_bindings(m);
|
||||
define_pedestal_bindings<double>(m, "Pedestal_d");
|
||||
define_pedestal_bindings<float>(m, "Pedestal_f");
|
||||
define_pedestal_bindings<int16_t>(m, "Pedestal_i16");
|
||||
define_fast_pedestal_bindings<double>(m, "FastPedestal_d");
|
||||
define_fast_pedestal_bindings<float>(m, "FastPedestal_f");
|
||||
define_fast_pedestal_bindings<int16_t>(m, "FastPedestal_i16");
|
||||
define_fit_bindings(m);
|
||||
define_interpolation_bindings(m);
|
||||
define_jungfrau_data_file_io_bindings(m);
|
||||
@@ -106,6 +114,7 @@ PYBIND11_MODULE(_aare, m) {
|
||||
DEFINE_BINDINGS_CLUSTERFINDER(int, 3, 3, uint16_t, i);
|
||||
DEFINE_BINDINGS_CLUSTERFINDER(double, 3, 3, uint16_t, d);
|
||||
DEFINE_BINDINGS_CLUSTERFINDER(float, 3, 3, uint16_t, f);
|
||||
DEFINE_BINDINGS_CLUSTERFINDER(int16_t, 3, 3, uint16_t, i16);
|
||||
|
||||
DEFINE_BINDINGS_CLUSTERFINDER(int, 5, 5, uint16_t, i);
|
||||
DEFINE_BINDINGS_CLUSTERFINDER(double, 5, 5, uint16_t, d);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// Configure module wide pedestal type for cluster finding
|
||||
using pd_type = double;
|
||||
@@ -52,6 +52,12 @@ void define_pedestal_bindings(py::module &m, const std::string &name) {
|
||||
*std = self.std();
|
||||
return return_image_data(std);
|
||||
})
|
||||
.def("cached_std",
|
||||
[](Pedestal<SUM_TYPE> &self) {
|
||||
auto standard_deviation = new NDArray<SUM_TYPE, 2>{};
|
||||
*standard_deviation = self.cached_std();
|
||||
return return_image_data(standard_deviation);
|
||||
})
|
||||
.def(
|
||||
"__array_ufunc__",
|
||||
[](py::object self, py::object ufunc, const std::string &method,
|
||||
|
||||
@@ -103,15 +103,18 @@ def test_max_sum():
|
||||
|
||||
def test_cluster_finder():
|
||||
"""Test ClusterFinder"""
|
||||
shape = [100,100]
|
||||
cf = _aare.ClusterFinder_Cluster3x3i(shape)
|
||||
|
||||
clusterfinder = _aare.ClusterFinder_Cluster3x3i([100,100])
|
||||
#Push 1000 frames to the pedestal
|
||||
for i in range(1000):
|
||||
frame = np.random.normal(loc = 100, scale = 5, size = shape).astype(np.uint16)
|
||||
cf.push_pedestal_frame(frame)
|
||||
cf.update_threshold()
|
||||
frame = np.zeros(shape=shape, dtype=np.uint16)
|
||||
cf.find_clusters(frame)
|
||||
|
||||
#frame = np.random.rand(100,100)
|
||||
frame = np.zeros(shape=[100,100])
|
||||
|
||||
clusterfinder.find_clusters(frame)
|
||||
|
||||
clusters = clusterfinder.steal_clusters(False) #conversion does not work
|
||||
clusters = cf.steal_clusters(False) #conversion does not work
|
||||
|
||||
assert clusters.size == 0
|
||||
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from aare import (
|
||||
FastPedestal,
|
||||
FastPedestal_d,
|
||||
FastPedestal_f,
|
||||
FastPedestal_i16,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("dtype", "pedestal_type"),
|
||||
[
|
||||
(np.float64, FastPedestal_d),
|
||||
(np.float32, FastPedestal_f),
|
||||
(np.int16, FastPedestal_i16),
|
||||
],
|
||||
)
|
||||
def test_fast_pedestal_factory(dtype, pedestal_type):
|
||||
pedestal = FastPedestal(2, 3, n_samples=4, dtype=dtype)
|
||||
|
||||
assert isinstance(pedestal, pedestal_type)
|
||||
assert pedestal.rows == 2
|
||||
assert pedestal.cols == 3
|
||||
assert pedestal.n_samples == 4
|
||||
|
||||
|
||||
def test_fast_pedestal_factory_defaults_to_double():
|
||||
assert isinstance(FastPedestal(2, 3), FastPedestal_d)
|
||||
|
||||
|
||||
def test_fast_pedestal_factory_rejects_unbound_dtype():
|
||||
with pytest.raises(ValueError, match="Unsupported dtype for FastPedestal"):
|
||||
FastPedestal(2, 3, dtype=np.int32)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("kwargs", "expected_n_samples"),
|
||||
[
|
||||
({"rows": 2, "cols": 3}, 1000),
|
||||
({"rows": 2, "cols": 3, "n_samples": 4}, 4),
|
||||
],
|
||||
)
|
||||
def test_fast_pedestal_binding_accepts_constructor_keywords(
|
||||
kwargs, expected_n_samples
|
||||
):
|
||||
pedestal = FastPedestal_d(**kwargs)
|
||||
|
||||
assert pedestal.rows == 2
|
||||
assert pedestal.cols == 3
|
||||
assert pedestal.n_samples == expected_n_samples
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("dtype", "pedestal_type", "expected_dtype"),
|
||||
[
|
||||
(np.float64, FastPedestal_d, np.float64),
|
||||
(np.float32, FastPedestal_f, np.float32),
|
||||
(np.int16, FastPedestal_i16, np.int16),
|
||||
],
|
||||
)
|
||||
def test_fast_pedestal_factory_from_file(
|
||||
tmp_path, dtype, pedestal_type, expected_dtype
|
||||
):
|
||||
frames = np.array(
|
||||
[[[100, 100]], [[2, 4]], [[4, 6]], [[5, 7]]], dtype=np.uint16
|
||||
)
|
||||
filename = tmp_path / "frames.npy"
|
||||
np.save(filename, frames)
|
||||
|
||||
pedestal = FastPedestal.from_file(
|
||||
filename, n_samples=2, skip_first=1, dtype=dtype
|
||||
)
|
||||
|
||||
assert isinstance(pedestal, pedestal_type)
|
||||
assert pedestal.ready
|
||||
assert pedestal.cur_samples == 2
|
||||
assert pedestal.mean().dtype == expected_dtype
|
||||
np.testing.assert_array_equal(pedestal.mean(), [[4, 6]])
|
||||
|
||||
|
||||
def test_fast_pedestal_factory_from_file_rejects_unbound_dtype():
|
||||
with pytest.raises(ValueError, match="Unsupported dtype for FastPedestal"):
|
||||
FastPedestal.from_file("unused.npy", dtype=np.int32)
|
||||
|
||||
|
||||
def test_fast_pedestal_from_file_rejects_skip_beyond_end(tmp_path):
|
||||
filename = tmp_path / "frames.npy"
|
||||
np.save(filename, np.zeros((1, 1, 1), dtype=np.uint16))
|
||||
|
||||
with pytest.raises(RuntimeError, match="less frames"):
|
||||
FastPedestal.from_file(filename, n_samples=1, skip_first=2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("pedestal_type", "expected_dtype"),
|
||||
[(FastPedestal_d, np.float64), (FastPedestal_f, np.float32)],
|
||||
)
|
||||
def test_fast_pedestal_initialization(pedestal_type, expected_dtype):
|
||||
pedestal = pedestal_type(2, 3, 2)
|
||||
first = np.array([[2, 4, 6], [8, 10, 12]], dtype=np.uint16)
|
||||
second = np.array([[4, 6, 8], [10, 12, 14]], dtype=np.uint16)
|
||||
|
||||
pedestal.add_init_frame(first)
|
||||
pedestal.add_init_frame(second)
|
||||
|
||||
|
||||
expected_mean = np.array(
|
||||
[[3, 5, 7], [9, 11, 13]], dtype=expected_dtype
|
||||
)
|
||||
np.testing.assert_array_equal(pedestal.mean(), expected_mean)
|
||||
np.testing.assert_array_equal(pedestal.std(), np.ones((2, 3)))
|
||||
|
||||
|
||||
def test_fast_pedestal_steady_state_push_ema():
|
||||
pedestal = FastPedestal_d(1, 2, 2)
|
||||
pedestal.add_init_frame(np.array([[2, 4]], dtype=np.uint16))
|
||||
pedestal.add_init_frame(np.array([[4, 6]], dtype=np.uint16))
|
||||
|
||||
|
||||
pedestal.push_ema(np.array([[6, 8]], dtype=np.uint16))
|
||||
|
||||
np.testing.assert_array_equal(pedestal.mean(), [[4.5, 6.5]])
|
||||
|
||||
|
||||
def test_fast_pedestal_exposes_read_only_buffer_and_subtraction():
|
||||
pedestal = FastPedestal_d(1, 2, 1)
|
||||
pedestal.add_init_frame(np.array([[2, 4]], dtype=np.uint16))
|
||||
|
||||
|
||||
view = np.asarray(pedestal)
|
||||
result = np.array([[12, 14]], dtype=np.uint16) - pedestal
|
||||
|
||||
np.testing.assert_array_equal(view, [[2, 4]])
|
||||
np.testing.assert_array_equal(result, [[10, 10]])
|
||||
assert np.shares_memory(view, pedestal.view())
|
||||
assert not view.flags.writeable
|
||||
|
||||
|
||||
def test_fast_pedestal_rejects_wrong_shape():
|
||||
pedestal = FastPedestal_d(2, 3)
|
||||
|
||||
with pytest.raises(RuntimeError, match="shape"):
|
||||
pedestal.add_init_frame(np.zeros((2, 2), dtype=np.uint16))
|
||||
Reference in New Issue
Block a user