diff --git a/include/aare/hist/PedestalTrackingPixelHistogram.hpp b/include/aare/hist/PedestalTrackingPixelHistogram.hpp index 263ea45..3e6c89c 100644 --- a/include/aare/hist/PedestalTrackingPixelHistogram.hpp +++ b/include/aare/hist/PedestalTrackingPixelHistogram.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -76,6 +77,9 @@ class PedestalTrackingPixelHistogram { std::size_t max_batch_size_; std::atomic n_sigma_; + /// @brief optional mask. True indicates a bad pixel + const std::optional> mask_{std::nullopt}; + // Private worker thread method void worker_loop(int thread_id); void coordinator_loop(); @@ -94,11 +98,10 @@ class PedestalTrackingPixelHistogram { void fill_with_threshold_batch_(std::vector> &batch); public: - PedestalTrackingPixelHistogram(int rows, int cols, int n_bins, - AxisType xmin, AxisType xmax, - int n_threads = 1, - std::size_t max_pending = 16, - AxisType n_sigma = 1.0); + PedestalTrackingPixelHistogram( + int rows, int cols, int n_bins, AxisType xmin, AxisType xmax, + int n_threads = 1, std::size_t max_pending = 16, AxisType n_sigma = 1.0, + std::optional> mask = std::nullopt); ~PedestalTrackingPixelHistogram(); void push_pedestal_no_update(const NDView &frame); diff --git a/python/src/bind_PedestalTrackingPixelHistogram.hpp b/python/src/bind_PedestalTrackingPixelHistogram.hpp index 7984273..13a7047 100644 --- a/python/src/bind_PedestalTrackingPixelHistogram.hpp +++ b/python/src/bind_PedestalTrackingPixelHistogram.hpp @@ -15,9 +15,21 @@ void define_pedestal_tracking_pixel_histogram_bindings(py::module &m) { m, "PedestalTrackingPixelHistogram", "A pixel-wise histogram of frame - pedestal residuals, with a " "per-pixel running pedestal estimate sharded across worker threads") - .def( - py::init(), - R"( + .def(py::init([](int rows, int cols, int n_bins, double xmin, double xmax, int n_threads, std::size_t max_pending, + double n_sigma, std::optional> mask) { + if (mask.has_value()) { + auto view = make_view_2d(mask.value()); + return new PedestalTrackingPixelHistogram( + rows, cols, n_bins, xmin, xmax, n_threads, max_pending, + n_sigma, view); + } else { + return new PedestalTrackingPixelHistogram( + rows, cols, n_bins, xmin, xmax, n_threads, max_pending, + n_sigma); + } + }), + R"( Initialize a PedestalTrackingPixelHistogram. Args: @@ -42,10 +54,16 @@ void define_pedestal_tracking_pixel_histogram_bindings(py::module &m) { ``0.0`` to disable the pedestal update and get histogram-only async behaviour (default: 1.0). Also exposed live via the ``n_sigma`` property. + mask: Optional 2D boolean array (shape: rows x cols) that + indicates bad pixels. True values indicate bad pixels + that will be skipped during pedestal tracking and + histogramming. If not provided, all pixels are treated + as good. )", - py::kw_only(), py::arg("rows"), py::arg("cols"), py::arg("n_bins"), - py::arg("xmin"), py::arg("xmax"), py::arg("n_threads") = 1, - py::arg("max_pending") = std::size_t{16}, py::arg("n_sigma") = 1.0) + py::kw_only(), py::arg("rows"), py::arg("cols"), py::arg("n_bins"), + py::arg("xmin"), py::arg("xmax"), py::arg("n_threads") = 1, + py::arg("max_pending") = std::size_t{16}, py::arg("n_sigma") = 1.0, + py::arg("mask") = std::nullopt) .def( "push_pedestal_no_update", diff --git a/src/hist/PedestalTrackingPixelHistogram.cpp b/src/hist/PedestalTrackingPixelHistogram.cpp index 0695c2f..f913e71 100644 --- a/src/hist/PedestalTrackingPixelHistogram.cpp +++ b/src/hist/PedestalTrackingPixelHistogram.cpp @@ -14,11 +14,13 @@ namespace aare { PedestalTrackingPixelHistogram::PedestalTrackingPixelHistogram( int rows, int cols, int n_bins, AxisType xmin, AxisType xmax, int n_threads, - std::size_t max_pending, AxisType n_sigma) + std::size_t max_pending, AxisType n_sigma, + std::optional> mask) : rows_(rows), cols_(cols), n_threads_(n_threads), xmin_(xmin), xmax_(xmax), current_work_kind_(WorkKind::FillWithThreshold), current_image_(nullptr), current_images_(nullptr), completed_threads_(0), stop_workers_(false), - work_generation_(0), max_batch_size_(max_pending / 3), n_sigma_(n_sigma) { + work_generation_(0), max_batch_size_(max_pending / 3), n_sigma_(n_sigma), + mask_(mask) { if (rows_ < 1 || cols_ < 1 || n_bins < 1) { throw std::invalid_argument("PedestalTrackingPixelHistogram requires " "positive rows, cols and bins"); @@ -56,6 +58,7 @@ PedestalTrackingPixelHistogram::PedestalTrackingPixelHistogram( partial_hists_.reserve(n_threads_); partial_pedestals_.reserve(n_threads_); partial_std_.reserve(n_threads_); + for (int i = 0; i < n_threads_; ++i) { const auto local_rows = row_count(i); partial_hists_.emplace_back(local_rows, cols, n_bins, xmin_, xmax_); @@ -200,6 +203,8 @@ void PedestalTrackingPixelHistogram::worker_loop(int thread_id) { auto &my_pedestal = partial_pedestals_[thread_id]; auto &my_hist = partial_hists_[thread_id]; + const bool has_mask = mask_.has_value(); + switch (kind) { case WorkKind::PushPedestal: { // Accumulate raw frame values into this thread's pedestal @@ -208,6 +213,9 @@ void PedestalTrackingPixelHistogram::worker_loop(int thread_id) { for (int local_row = 0; local_row < local_rows; ++local_row) { const auto row = static_cast(first_row + local_row); for (ssize_t col = 0; col < image->shape(1); ++col) { + if (has_mask && mask_.value()(row, col)) { + continue; // Skip masked pixels + } my_pedestal.template push_no_update( static_cast(local_row), static_cast(col), (*image)(row, col)); @@ -249,6 +257,9 @@ void PedestalTrackingPixelHistogram::worker_loop(int thread_id) { const auto row = static_cast(first_row + local_row); for (ssize_t col = 0; col < cols; ++col) { + if (has_mask && mask_.value()(row, col)) { + continue; // Skip masked pixels + } const FrameType raw = frame(row, col); const AxisType val = static_cast(raw) - @@ -272,6 +283,9 @@ void PedestalTrackingPixelHistogram::worker_loop(int thread_id) { const auto row = static_cast(first_row + local_row); for (ssize_t col = 0; col < cols; ++col) { + if (has_mask && mask_.value()(row, col)) { + continue; // Skip masked pixels + } const FrameType raw = frame(row, col); const AxisType val = static_cast(raw) -