mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-02-20 01:58:40 +01:00
Pedestal, calibration in g0 and counting pixels (#217)
- NDView operator()(size_t) now returns a view with one less dimension - Apply calibration takes also a 2D array and then ignores pixels that switch - Calculate pedestal from a dataset which contains all three gains - G0 variant of pedestal - Function to count pixels switching
This commit is contained in:
@@ -368,6 +368,7 @@ set(PUBLICHEADERS
|
|||||||
|
|
||||||
|
|
||||||
set(SourceFiles
|
set(SourceFiles
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/calibration.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/CtbRawFile.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/CtbRawFile.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.cpp
|
||||||
@@ -437,6 +438,7 @@ endif()
|
|||||||
if(AARE_TESTS)
|
if(AARE_TESTS)
|
||||||
set(TestSources
|
set(TestSources
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/algorithm.test.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/algorithm.test.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/calibration.test.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.test.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.test.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.test.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.test.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.test.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.test.cpp
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
# Release notes
|
# Release notes
|
||||||
|
|
||||||
|
|
||||||
|
### head
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
- Apply calibration works in G0 if passes a 2D calibration and pedestal
|
||||||
|
- count pixels that switch
|
||||||
|
- calculate pedestal (also g0 version)
|
||||||
|
|
||||||
|
|
||||||
### 2025.07.18
|
### 2025.07.18
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|||||||
@@ -17,8 +17,24 @@ Functions for applying calibration to data.
|
|||||||
# Apply calibration to raw data to convert from raw ADC values to keV
|
# Apply calibration to raw data to convert from raw ADC values to keV
|
||||||
data = aare.apply_calibration(raw_data, pd=pedestal, cal=calibration)
|
data = aare.apply_calibration(raw_data, pd=pedestal, cal=calibration)
|
||||||
|
|
||||||
|
# If you pass a 2D pedestal and calibration only G0 will be used for the conversion
|
||||||
|
# Pixels that switched to G1 or G2 will be set to 0
|
||||||
|
data = aare.apply_calibration(raw_data, pd=pedestal[0], cal=calibration[0])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. py:currentmodule:: aare
|
.. py:currentmodule:: aare
|
||||||
|
|
||||||
.. autofunction:: apply_calibration
|
.. autofunction:: apply_calibration
|
||||||
|
|
||||||
.. autofunction:: load_calibration
|
.. autofunction:: load_calibration
|
||||||
|
|
||||||
|
.. autofunction:: calculate_pedestal
|
||||||
|
|
||||||
|
.. autofunction:: calculate_pedestal_float
|
||||||
|
|
||||||
|
.. autofunction:: calculate_pedestal_g0
|
||||||
|
|
||||||
|
.. autofunction:: calculate_pedestal_g0_float
|
||||||
|
|
||||||
|
.. autofunction:: count_switching_pixels
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ template <typename T, ssize_t Ndim = 2>
|
|||||||
class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||||
std::array<ssize_t, Ndim> shape_;
|
std::array<ssize_t, Ndim> shape_;
|
||||||
std::array<ssize_t, Ndim> strides_;
|
std::array<ssize_t, Ndim> strides_;
|
||||||
size_t size_{};
|
size_t size_{}; //TODO! do we need to store size when we have shape?
|
||||||
T *data_;
|
T *data_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -33,7 +33,7 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
|||||||
* @brief Default constructor. Will construct an empty NDArray.
|
* @brief Default constructor. Will construct an empty NDArray.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
NDArray() : shape_(), strides_(c_strides<Ndim>(shape_)), data_(nullptr){};
|
NDArray() : shape_(), strides_(c_strides<Ndim>(shape_)), data_(nullptr) {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new NDArray object with a given shape.
|
* @brief Construct a new NDArray object with a given shape.
|
||||||
@@ -43,8 +43,7 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
|||||||
*/
|
*/
|
||||||
explicit NDArray(std::array<ssize_t, Ndim> shape)
|
explicit NDArray(std::array<ssize_t, Ndim> shape)
|
||||||
: shape_(shape), strides_(c_strides<Ndim>(shape_)),
|
: shape_(shape), strides_(c_strides<Ndim>(shape_)),
|
||||||
size_(std::accumulate(shape_.begin(), shape_.end(), 1,
|
size_(num_elements(shape_)),
|
||||||
std::multiplies<>())),
|
|
||||||
data_(new T[size_]) {}
|
data_(new T[size_]) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,6 +78,24 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
|||||||
other.reset(); // TODO! is this necessary?
|
other.reset(); // TODO! is this necessary?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Move constructor from an an array with Ndim + 1
|
||||||
|
template <ssize_t M, typename = std::enable_if_t<(M == Ndim + 1)>>
|
||||||
|
NDArray(NDArray<T, M> &&other)
|
||||||
|
: shape_(drop_first_dim(other.shape())),
|
||||||
|
strides_(c_strides<Ndim>(shape_)), size_(num_elements(shape_)),
|
||||||
|
data_(other.data()) {
|
||||||
|
|
||||||
|
// For now only allow move if the size matches, to avoid unreachable data
|
||||||
|
// if the use case arises we can remove this check
|
||||||
|
if(size() != other.size()) {
|
||||||
|
data_ = nullptr; // avoid double free, other will clean up the memory in it's destructor
|
||||||
|
throw std::runtime_error(LOCATION +
|
||||||
|
"Size mismatch in move constructor of NDArray<T, Ndim-1>");
|
||||||
|
}
|
||||||
|
other.reset();
|
||||||
|
}
|
||||||
|
|
||||||
// Copy constructor
|
// Copy constructor
|
||||||
NDArray(const NDArray &other)
|
NDArray(const NDArray &other)
|
||||||
: shape_(other.shape_), strides_(c_strides<Ndim>(shape_)),
|
: shape_(other.shape_), strides_(c_strides<Ndim>(shape_)),
|
||||||
@@ -380,12 +397,6 @@ NDArray<T, Ndim> NDArray<T, Ndim>::operator*(const T &value) {
|
|||||||
result *= value;
|
result *= value;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// template <typename T, ssize_t Ndim> void NDArray<T, Ndim>::Print() {
|
|
||||||
// if (shape_[0] < 20 && shape_[1] < 20)
|
|
||||||
// Print_all();
|
|
||||||
// else
|
|
||||||
// Print_some();
|
|
||||||
// }
|
|
||||||
|
|
||||||
template <typename T, ssize_t Ndim>
|
template <typename T, ssize_t Ndim>
|
||||||
std::ostream &operator<<(std::ostream &os, const NDArray<T, Ndim> &arr) {
|
std::ostream &operator<<(std::ostream &os, const NDArray<T, Ndim> &arr) {
|
||||||
@@ -437,4 +448,23 @@ NDArray<T, Ndim> load(const std::string &pathname,
|
|||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename RT, typename NT, typename DT, ssize_t Ndim>
|
||||||
|
NDArray<RT, Ndim> safe_divide(const NDArray<NT, Ndim> &numerator,
|
||||||
|
const NDArray<DT, Ndim> &denominator) {
|
||||||
|
if (numerator.shape() != denominator.shape()) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Shapes of numerator and denominator must match");
|
||||||
|
}
|
||||||
|
NDArray<RT, Ndim> result(numerator.shape());
|
||||||
|
for (ssize_t i = 0; i < numerator.size(); ++i) {
|
||||||
|
if (denominator[i] != 0) {
|
||||||
|
result[i] =
|
||||||
|
static_cast<RT>(numerator[i]) / static_cast<RT>(denominator[i]);
|
||||||
|
} else {
|
||||||
|
result[i] = RT{0}; // or handle division by zero as needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aare
|
} // namespace aare
|
||||||
@@ -26,6 +26,33 @@ Shape<Ndim> make_shape(const std::vector<size_t> &shape) {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper function to drop the first dimension of a shape.
|
||||||
|
* This is useful when you want to create a 2D view from a 3D array.
|
||||||
|
* @param shape The shape to drop the first dimension from.
|
||||||
|
* @return A new shape with the first dimension dropped.
|
||||||
|
*/
|
||||||
|
template<size_t Ndim>
|
||||||
|
Shape<Ndim-1> drop_first_dim(const Shape<Ndim> &shape) {
|
||||||
|
static_assert(Ndim > 1, "Cannot drop first dimension from a 1D shape");
|
||||||
|
Shape<Ndim - 1> new_shape;
|
||||||
|
std::copy(shape.begin() + 1, shape.end(), new_shape.begin());
|
||||||
|
return new_shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper function when constructing NDArray/NDView. Calculates the number
|
||||||
|
* of elements in the resulting array from a shape.
|
||||||
|
* @param shape The shape to calculate the number of elements for.
|
||||||
|
* @return The number of elements in and NDArray/NDView of that shape.
|
||||||
|
*/
|
||||||
|
template <size_t Ndim>
|
||||||
|
size_t num_elements(const Shape<Ndim> &shape) {
|
||||||
|
return std::accumulate(shape.begin(), shape.end(), 1,
|
||||||
|
std::multiplies<size_t>());
|
||||||
|
}
|
||||||
|
|
||||||
template <ssize_t Dim = 0, typename Strides>
|
template <ssize_t Dim = 0, typename Strides>
|
||||||
ssize_t element_offset(const Strides & /*unused*/) {
|
ssize_t element_offset(const Strides & /*unused*/) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -66,17 +93,28 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
|
|||||||
: buffer_(buffer), strides_(c_strides<Ndim>(shape)), shape_(shape),
|
: buffer_(buffer), strides_(c_strides<Ndim>(shape)), shape_(shape),
|
||||||
size_(std::accumulate(std::begin(shape), std::end(shape), 1,
|
size_(std::accumulate(std::begin(shape), std::end(shape), 1,
|
||||||
std::multiplies<>())) {}
|
std::multiplies<>())) {}
|
||||||
|
|
||||||
template <typename... Ix>
|
template <typename... Ix>
|
||||||
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
|
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
|
||||||
return buffer_[element_offset(strides_, index...)];
|
return buffer_[element_offset(strides_, index...)];
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Ix>
|
template <typename... Ix>
|
||||||
const std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
|
std::enable_if_t<sizeof...(Ix) == 1 && (Ndim > 1), NDView<T, Ndim - 1>> operator()(Ix... index) {
|
||||||
|
// return a view of the next dimension
|
||||||
|
std::array<ssize_t, Ndim - 1> new_shape{};
|
||||||
|
std::copy_n(shape_.begin() + 1, Ndim - 1, new_shape.begin());
|
||||||
|
return NDView<T, Ndim - 1>(&buffer_[element_offset(strides_, index...)],
|
||||||
|
new_shape);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ix>
|
||||||
|
std::enable_if_t<sizeof...(Ix) == Ndim, const T &> operator()(Ix... index) const {
|
||||||
return buffer_[element_offset(strides_, index...)];
|
return buffer_[element_offset(strides_, index...)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ssize_t size() const { return static_cast<ssize_t>(size_); }
|
ssize_t size() const { return static_cast<ssize_t>(size_); }
|
||||||
size_t total_bytes() const { return size_ * sizeof(T); }
|
size_t total_bytes() const { return size_ * sizeof(T); }
|
||||||
std::array<ssize_t, Ndim> strides() const noexcept { return strides_; }
|
std::array<ssize_t, Ndim> strides() const noexcept { return strides_; }
|
||||||
@@ -85,9 +123,19 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
|
|||||||
T *end() { return buffer_ + size_; }
|
T *end() { return buffer_ + size_; }
|
||||||
T const *begin() const { return buffer_; }
|
T const *begin() const { return buffer_; }
|
||||||
T const *end() const { return buffer_ + size_; }
|
T const *end() const { return buffer_ + size_; }
|
||||||
T &operator()(ssize_t i) { return buffer_[i]; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Access element at index i.
|
||||||
|
*/
|
||||||
T &operator[](ssize_t i) { return buffer_[i]; }
|
T &operator[](ssize_t i) { return buffer_[i]; }
|
||||||
const T &operator()(ssize_t i) const { return buffer_[i]; }
|
|
||||||
|
/**
|
||||||
|
* @brief Access element at index i.
|
||||||
|
*/
|
||||||
const T &operator[](ssize_t i) const { return buffer_[i]; }
|
const T &operator[](ssize_t i) const { return buffer_[i]; }
|
||||||
|
|
||||||
bool operator==(const NDView &other) const {
|
bool operator==(const NDView &other) const {
|
||||||
@@ -157,6 +205,22 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
|
|||||||
const T *data() const { return buffer_; }
|
const T *data() const { return buffer_; }
|
||||||
void print_all() const;
|
void print_all() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a subview of a range of the first dimension.
|
||||||
|
* This is useful for splitting a batches of frames in parallel processing.
|
||||||
|
* @param first The first index of the subview (inclusive).
|
||||||
|
* @param last The last index of the subview (exclusive).
|
||||||
|
* @return A new NDView that is a subview of the current view.
|
||||||
|
* @throws std::runtime_error if the range is invalid.
|
||||||
|
*/
|
||||||
|
NDView sub_view(ssize_t first, ssize_t last) const {
|
||||||
|
if (first < 0 || last > shape_[0] || first >= last)
|
||||||
|
throw std::runtime_error(LOCATION + "Invalid sub_view range");
|
||||||
|
auto new_shape = shape_;
|
||||||
|
new_shape[0] = last - first;
|
||||||
|
return NDView(buffer_ + first * strides_[0], new_shape);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T *buffer_{nullptr};
|
T *buffer_{nullptr};
|
||||||
std::array<ssize_t, Ndim> strides_{};
|
std::array<ssize_t, Ndim> strides_{};
|
||||||
|
|||||||
@@ -240,14 +240,14 @@ template <typename T> void VarClusterFinder<T>::first_pass() {
|
|||||||
|
|
||||||
for (ssize_t i = 0; i < original_.size(); ++i) {
|
for (ssize_t i = 0; i < original_.size(); ++i) {
|
||||||
if (use_noise_map)
|
if (use_noise_map)
|
||||||
threshold_ = 5 * noiseMap(i);
|
threshold_ = 5 * noiseMap[i];
|
||||||
binary_(i) = (original_(i) > threshold_);
|
binary_[i] = (original_[i] > threshold_);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < shape_[0]; ++i) {
|
for (int i = 0; i < shape_[0]; ++i) {
|
||||||
for (int j = 0; j < shape_[1]; ++j) {
|
for (int j = 0; j < shape_[1]; ++j) {
|
||||||
|
|
||||||
// do we have someting to process?
|
// do we have something to process?
|
||||||
if (binary_(i, j)) {
|
if (binary_(i, j)) {
|
||||||
auto tmp = check_neighbours(i, j);
|
auto tmp = check_neighbours(i, j);
|
||||||
if (tmp != 0) {
|
if (tmp != 0) {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "aare/NDArray.hpp"
|
||||||
|
#include "aare/NDView.hpp"
|
||||||
#include "aare/defs.hpp"
|
#include "aare/defs.hpp"
|
||||||
|
#include "aare/utils/par.hpp"
|
||||||
#include "aare/utils/task.hpp"
|
#include "aare/utils/task.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <future>
|
#include <future>
|
||||||
@@ -55,32 +58,152 @@ ALWAYS_INLINE std::pair<uint16_t, int16_t> get_value_and_gain(uint16_t raw) {
|
|||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void apply_calibration_impl(NDView<T, 3> res, NDView<uint16_t, 3> raw_data,
|
void apply_calibration_impl(NDView<T, 3> res, NDView<uint16_t, 3> raw_data,
|
||||||
NDView<T, 3> ped, NDView<T, 3> cal, int start,
|
NDView<T, 3> ped, NDView<T, 3> cal, int start,
|
||||||
int stop) {
|
int stop) {
|
||||||
|
|
||||||
for (int frame_nr = start; frame_nr != stop; ++frame_nr) {
|
for (int frame_nr = start; frame_nr != stop; ++frame_nr) {
|
||||||
for (int row = 0; row != raw_data.shape(1); ++row) {
|
for (int row = 0; row != raw_data.shape(1); ++row) {
|
||||||
for (int col = 0; col != raw_data.shape(2); ++col) {
|
for (int col = 0; col != raw_data.shape(2); ++col) {
|
||||||
auto [value, gain] = get_value_and_gain(raw_data(frame_nr, row, col));
|
auto [value, gain] =
|
||||||
|
get_value_and_gain(raw_data(frame_nr, row, col));
|
||||||
|
|
||||||
|
// Using multiplication does not seem to speed up the code here
|
||||||
|
// ADU/keV is the standard unit for the calibration which
|
||||||
|
// means rewriting the formula is not worth it.
|
||||||
res(frame_nr, row, col) =
|
res(frame_nr, row, col) =
|
||||||
(value - ped(gain, row, col)) / cal(gain, row, col); //TODO! use multiplication
|
(value - ped(gain, row, col)) / cal(gain, row, col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
void apply_calibration_impl(NDView<T, 3> res, NDView<uint16_t, 3> raw_data,
|
||||||
|
NDView<T, 2> ped, NDView<T, 2> cal, int start,
|
||||||
|
int stop) {
|
||||||
|
|
||||||
|
for (int frame_nr = start; frame_nr != stop; ++frame_nr) {
|
||||||
|
for (int row = 0; row != raw_data.shape(1); ++row) {
|
||||||
|
for (int col = 0; col != raw_data.shape(2); ++col) {
|
||||||
|
auto [value, gain] =
|
||||||
|
get_value_and_gain(raw_data(frame_nr, row, col));
|
||||||
|
|
||||||
|
// Using multiplication does not seem to speed up the code here
|
||||||
|
// ADU/keV is the standard unit for the calibration which
|
||||||
|
// means rewriting the formula is not worth it.
|
||||||
|
|
||||||
|
// Set the value to 0 if the gain is not 0
|
||||||
|
if (gain == 0)
|
||||||
|
res(frame_nr, row, col) =
|
||||||
|
(value - ped(row, col)) / cal(row, col);
|
||||||
|
else
|
||||||
|
res(frame_nr, row, col) = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, ssize_t Ndim = 3>
|
||||||
void apply_calibration(NDView<T, 3> res, NDView<uint16_t, 3> raw_data,
|
void apply_calibration(NDView<T, 3> res, NDView<uint16_t, 3> raw_data,
|
||||||
NDView<T, 3> ped, NDView<T, 3> cal,
|
NDView<T, Ndim> ped, NDView<T, Ndim> cal,
|
||||||
ssize_t n_threads = 4) {
|
ssize_t n_threads = 4) {
|
||||||
std::vector<std::future<void>> futures;
|
std::vector<std::future<void>> futures;
|
||||||
futures.reserve(n_threads);
|
futures.reserve(n_threads);
|
||||||
auto limits = split_task(0, raw_data.shape(0), n_threads);
|
auto limits = split_task(0, raw_data.shape(0), n_threads);
|
||||||
for (const auto &lim : limits)
|
for (const auto &lim : limits)
|
||||||
futures.push_back(std::async(&apply_calibration_impl<T>, res, raw_data, ped, cal,
|
futures.push_back(std::async(
|
||||||
lim.first, lim.second));
|
static_cast<void (*)(NDView<T, 3>, NDView<uint16_t, 3>,
|
||||||
|
NDView<T, Ndim>, NDView<T, Ndim>, int, int)>(
|
||||||
|
apply_calibration_impl),
|
||||||
|
res, raw_data, ped, cal, lim.first, lim.second));
|
||||||
for (auto &f : futures)
|
for (auto &f : futures)
|
||||||
f.get();
|
f.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool only_gain0>
|
||||||
|
std::pair<NDArray<size_t, 3>, NDArray<size_t, 3>>
|
||||||
|
sum_and_count_per_gain(NDView<uint16_t, 3> raw_data) {
|
||||||
|
constexpr ssize_t num_gains = only_gain0 ? 1 : 3;
|
||||||
|
NDArray<size_t, 3> accumulator(
|
||||||
|
std::array<ssize_t, 3>{num_gains, raw_data.shape(1), raw_data.shape(2)},
|
||||||
|
0);
|
||||||
|
NDArray<size_t, 3> count(
|
||||||
|
std::array<ssize_t, 3>{num_gains, raw_data.shape(1), raw_data.shape(2)},
|
||||||
|
0);
|
||||||
|
for (int frame_nr = 0; frame_nr != raw_data.shape(0); ++frame_nr) {
|
||||||
|
for (int row = 0; row != raw_data.shape(1); ++row) {
|
||||||
|
for (int col = 0; col != raw_data.shape(2); ++col) {
|
||||||
|
auto [value, gain] =
|
||||||
|
get_value_and_gain(raw_data(frame_nr, row, col));
|
||||||
|
if (gain != 0 && only_gain0)
|
||||||
|
continue;
|
||||||
|
accumulator(gain, row, col) += value;
|
||||||
|
count(gain, row, col) += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {std::move(accumulator), std::move(count)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, bool only_gain0 = false>
|
||||||
|
NDArray<T, 3 - static_cast<ssize_t>(only_gain0)>
|
||||||
|
calculate_pedestal(NDView<uint16_t, 3> raw_data, ssize_t n_threads) {
|
||||||
|
|
||||||
|
constexpr ssize_t num_gains = only_gain0 ? 1 : 3;
|
||||||
|
std::vector<std::future<std::pair<NDArray<size_t, 3>, NDArray<size_t, 3>>>>
|
||||||
|
futures;
|
||||||
|
futures.reserve(n_threads);
|
||||||
|
|
||||||
|
auto subviews = make_subviews(raw_data, n_threads);
|
||||||
|
|
||||||
|
for (auto view : subviews) {
|
||||||
|
futures.push_back(std::async(
|
||||||
|
static_cast<std::pair<NDArray<size_t, 3>, NDArray<size_t, 3>> (*)(
|
||||||
|
NDView<uint16_t, 3>)>(&sum_and_count_per_gain<only_gain0>),
|
||||||
|
view));
|
||||||
|
}
|
||||||
|
Shape<3> shape{num_gains, raw_data.shape(1), raw_data.shape(2)};
|
||||||
|
NDArray<size_t, 3> accumulator(shape, 0);
|
||||||
|
NDArray<size_t, 3> count(shape, 0);
|
||||||
|
|
||||||
|
// Combine the results from the futures
|
||||||
|
for (auto &f : futures) {
|
||||||
|
auto [acc, cnt] = f.get();
|
||||||
|
accumulator += acc;
|
||||||
|
count += cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Will move to a NDArray<T, 3 - static_cast<ssize_t>(only_gain0)>
|
||||||
|
// if only_gain0 is true
|
||||||
|
return safe_divide<T>(accumulator, count);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Count the number of switching pixels in the raw data.
|
||||||
|
* This function counts the number of pixels that switch between G1 and G2 gain.
|
||||||
|
* It returns an NDArray with the number of switching pixels per pixel.
|
||||||
|
* @param raw_data The NDView containing the raw data
|
||||||
|
* @return An NDArray with the number of switching pixels per pixel
|
||||||
|
*/
|
||||||
|
NDArray<int, 2> count_switching_pixels(NDView<uint16_t, 3> raw_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Count the number of switching pixels in the raw data.
|
||||||
|
* This function counts the number of pixels that switch between G1 and G2 gain.
|
||||||
|
* It returns an NDArray with the number of switching pixels per pixel.
|
||||||
|
* @param raw_data The NDView containing the raw data
|
||||||
|
* @param n_threads The number of threads to use for parallel processing
|
||||||
|
* @return An NDArray with the number of switching pixels per pixel
|
||||||
|
*/
|
||||||
|
NDArray<int, 2> count_switching_pixels(NDView<uint16_t, 3> raw_data,
|
||||||
|
ssize_t n_threads);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto calculate_pedestal_g0(NDView<uint16_t, 3> raw_data, ssize_t n_threads) {
|
||||||
|
return calculate_pedestal<T, true>(raw_data, n_threads);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aare
|
} // namespace aare
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "aare/utils/task.hpp"
|
||||||
|
|
||||||
namespace aare {
|
namespace aare {
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
@@ -15,4 +18,17 @@ void RunInParallel(F func, const std::vector<std::pair<int, int>> &tasks) {
|
|||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<NDView<T,3>> make_subviews(NDView<T, 3> &data, ssize_t n_threads) {
|
||||||
|
std::vector<NDView<T, 3>> subviews;
|
||||||
|
subviews.reserve(n_threads);
|
||||||
|
auto limits = split_task(0, data.shape(0), n_threads);
|
||||||
|
for (const auto &lim : limits) {
|
||||||
|
subviews.push_back(data.sub_view(lim.first, lim.second));
|
||||||
|
}
|
||||||
|
return subviews;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aare
|
} // namespace aare
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from .utils import random_pixels, random_pixel, flat_list, add_colorbar
|
|||||||
from .func import *
|
from .func import *
|
||||||
|
|
||||||
from .calibration import *
|
from .calibration import *
|
||||||
from ._aare import apply_calibration
|
from ._aare import apply_calibration, count_switching_pixels
|
||||||
|
from ._aare import calculate_pedestal, calculate_pedestal_float, calculate_pedestal_g0, calculate_pedestal_g0_float
|
||||||
|
|
||||||
from ._aare import VarClusterFinder
|
from ._aare import VarClusterFinder
|
||||||
|
|||||||
@@ -17,27 +17,137 @@ py::array_t<DataType> pybind_apply_calibration(
|
|||||||
calibration,
|
calibration,
|
||||||
int n_threads = 4) {
|
int n_threads = 4) {
|
||||||
|
|
||||||
auto data_span = make_view_3d(data);
|
auto data_span = make_view_3d(data); // data is always 3D
|
||||||
auto ped = make_view_3d(pedestal);
|
|
||||||
auto cal = make_view_3d(calibration);
|
|
||||||
|
|
||||||
/* No pointer is passed, so NumPy will allocate the buffer */
|
/* No pointer is passed, so NumPy will allocate the buffer */
|
||||||
auto result = py::array_t<DataType>(data_span.shape());
|
auto result = py::array_t<DataType>(data_span.shape());
|
||||||
auto res = make_view_3d(result);
|
auto res = make_view_3d(result);
|
||||||
|
if (data.ndim() == 3 && pedestal.ndim() == 3 && calibration.ndim() == 3) {
|
||||||
aare::apply_calibration<DataType>(res, data_span, ped, cal, n_threads);
|
auto ped = make_view_3d(pedestal);
|
||||||
|
auto cal = make_view_3d(calibration);
|
||||||
|
aare::apply_calibration<DataType, 3>(res, data_span, ped, cal,
|
||||||
|
n_threads);
|
||||||
|
} else if (data.ndim() == 3 && pedestal.ndim() == 2 &&
|
||||||
|
calibration.ndim() == 2) {
|
||||||
|
auto ped = make_view_2d(pedestal);
|
||||||
|
auto cal = make_view_2d(calibration);
|
||||||
|
aare::apply_calibration<DataType, 2>(res, data_span, ped, cal,
|
||||||
|
n_threads);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Invalid number of dimensions for data, pedestal or calibration");
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
py::array_t<int> pybind_count_switching_pixels(
|
||||||
|
py::array_t<uint16_t, py::array::c_style | py::array::forcecast> data,
|
||||||
|
ssize_t n_threads = 4) {
|
||||||
|
|
||||||
|
auto data_span = make_view_3d(data);
|
||||||
|
auto arr = new NDArray<int, 2>{};
|
||||||
|
*arr = aare::count_switching_pixels(data_span, n_threads);
|
||||||
|
return return_image_data(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
py::array_t<T> pybind_calculate_pedestal(
|
||||||
|
py::array_t<uint16_t, py::array::c_style | py::array::forcecast> data,
|
||||||
|
ssize_t n_threads) {
|
||||||
|
|
||||||
|
auto data_span = make_view_3d(data);
|
||||||
|
auto arr = new NDArray<T, 3>{};
|
||||||
|
*arr = aare::calculate_pedestal<T, false>(data_span, n_threads);
|
||||||
|
return return_image_data(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
py::array_t<T> pybind_calculate_pedestal_g0(
|
||||||
|
py::array_t<uint16_t, py::array::c_style | py::array::forcecast> data,
|
||||||
|
ssize_t n_threads) {
|
||||||
|
|
||||||
|
auto data_span = make_view_3d(data);
|
||||||
|
auto arr = new NDArray<T, 2>{};
|
||||||
|
*arr = aare::calculate_pedestal<T, true>(data_span, n_threads);
|
||||||
|
return return_image_data(arr);
|
||||||
|
}
|
||||||
|
|
||||||
void bind_calibration(py::module &m) {
|
void bind_calibration(py::module &m) {
|
||||||
|
m.def("apply_calibration", &pybind_apply_calibration<double>,
|
||||||
|
py::arg("raw_data").noconvert(), py::kw_only(),
|
||||||
|
py::arg("pd").noconvert(), py::arg("cal").noconvert(),
|
||||||
|
py::arg("n_threads") = 4);
|
||||||
|
|
||||||
m.def("apply_calibration", &pybind_apply_calibration<float>,
|
m.def("apply_calibration", &pybind_apply_calibration<float>,
|
||||||
py::arg("raw_data").noconvert(), py::kw_only(),
|
py::arg("raw_data").noconvert(), py::kw_only(),
|
||||||
py::arg("pd").noconvert(), py::arg("cal").noconvert(),
|
py::arg("pd").noconvert(), py::arg("cal").noconvert(),
|
||||||
py::arg("n_threads") = 4);
|
py::arg("n_threads") = 4);
|
||||||
|
|
||||||
m.def("apply_calibration", &pybind_apply_calibration<double>,
|
m.def("count_switching_pixels", &pybind_count_switching_pixels,
|
||||||
|
R"(
|
||||||
|
Count the number of time each pixel switches to G1 or G2.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
raw_data : array_like
|
||||||
|
3D array of shape (frames, rows, cols) to count the switching pixels from.
|
||||||
|
n_threads : int
|
||||||
|
The number of threads to use for the calculation.
|
||||||
|
)",
|
||||||
py::arg("raw_data").noconvert(), py::kw_only(),
|
py::arg("raw_data").noconvert(), py::kw_only(),
|
||||||
py::arg("pd").noconvert(), py::arg("cal").noconvert(),
|
|
||||||
py::arg("n_threads") = 4);
|
py::arg("n_threads") = 4);
|
||||||
|
|
||||||
|
m.def("calculate_pedestal", &pybind_calculate_pedestal<double>,
|
||||||
|
R"(
|
||||||
|
Calculate the pedestal for all three gains and return the result as a 3D array of doubles.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
raw_data : array_like
|
||||||
|
3D array of shape (frames, rows, cols) to calculate the pedestal from.
|
||||||
|
Needs to contain data for all three gains (G0, G1, G2).
|
||||||
|
n_threads : int
|
||||||
|
The number of threads to use for the calculation.
|
||||||
|
)",
|
||||||
|
py::arg("raw_data").noconvert(), py::arg("n_threads") = 4);
|
||||||
|
|
||||||
|
m.def("calculate_pedestal_float", &pybind_calculate_pedestal<float>,
|
||||||
|
R"(
|
||||||
|
Same as `calculate_pedestal` but returns a 3D array of floats.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
raw_data : array_like
|
||||||
|
3D array of shape (frames, rows, cols) to calculate the pedestal from.
|
||||||
|
Needs to contain data for all three gains (G0, G1, G2).
|
||||||
|
n_threads : int
|
||||||
|
The number of threads to use for the calculation.
|
||||||
|
)",
|
||||||
|
py::arg("raw_data").noconvert(), py::arg("n_threads") = 4);
|
||||||
|
|
||||||
|
m.def("calculate_pedestal_g0", &pybind_calculate_pedestal_g0<double>,
|
||||||
|
R"(
|
||||||
|
Calculate the pedestal for G0 and return the result as a 2D array of doubles.
|
||||||
|
Pixels in G1 and G2 are ignored.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
raw_data : array_like
|
||||||
|
3D array of shape (frames, rows, cols) to calculate the pedestal from.
|
||||||
|
n_threads : int
|
||||||
|
The number of threads to use for the calculation.
|
||||||
|
)",
|
||||||
|
py::arg("raw_data").noconvert(), py::arg("n_threads") = 4);
|
||||||
|
|
||||||
|
m.def("calculate_pedestal_g0_float", &pybind_calculate_pedestal_g0<float>,
|
||||||
|
R"(
|
||||||
|
Same as `calculate_pedestal_g0` but returns a 2D array of floats.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
raw_data : array_like
|
||||||
|
3D array of shape (frames, rows, cols) to calculate the pedestal from.
|
||||||
|
n_threads : int
|
||||||
|
The number of threads to use for the calculation.
|
||||||
|
)",
|
||||||
|
py::arg("raw_data").noconvert(), py::arg("n_threads") = 4);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from aare import apply_calibration
|
|
||||||
|
import aare
|
||||||
|
|
||||||
def test_apply_calibration_small_data():
|
def test_apply_calibration_small_data():
|
||||||
# The raw data consists of 10 4x5 images
|
# The raw data consists of 10 4x5 images
|
||||||
@@ -27,7 +28,7 @@ def test_apply_calibration_small_data():
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
data = apply_calibration(raw, pd = pedestal, cal = calibration)
|
data = aare.apply_calibration(raw, pd = pedestal, cal = calibration)
|
||||||
|
|
||||||
|
|
||||||
# The formula that is applied is:
|
# The formula that is applied is:
|
||||||
@@ -41,3 +42,94 @@ def test_apply_calibration_small_data():
|
|||||||
assert data[2,2,2] == 0
|
assert data[2,2,2] == 0
|
||||||
assert data[0,1,1] == 0
|
assert data[0,1,1] == 0
|
||||||
assert data[1,3,0] == 0
|
assert data[1,3,0] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def raw_data_3x2x2():
|
||||||
|
raw = np.zeros((3, 2, 2), dtype=np.uint16)
|
||||||
|
raw[0, 0, 0] = 100
|
||||||
|
raw[1,0, 0] = 200
|
||||||
|
raw[2, 0, 0] = 300
|
||||||
|
|
||||||
|
raw[0, 0, 1] = (1<<14) + 100
|
||||||
|
raw[1, 0, 1] = (1<<14) + 200
|
||||||
|
raw[2, 0, 1] = (1<<14) + 300
|
||||||
|
|
||||||
|
raw[0, 1, 0] = (1<<14) + 37
|
||||||
|
raw[1, 1, 0] = 38
|
||||||
|
raw[2, 1, 0] = (3<<14) + 39
|
||||||
|
|
||||||
|
raw[0, 1, 1] = (3<<14) + 100
|
||||||
|
raw[1, 1, 1] = (3<<14) + 200
|
||||||
|
raw[2, 1, 1] = (3<<14) + 300
|
||||||
|
return raw
|
||||||
|
|
||||||
|
def test_calculate_pedestal(raw_data_3x2x2):
|
||||||
|
# Calculate the pedestal
|
||||||
|
pd = aare.calculate_pedestal(raw_data_3x2x2)
|
||||||
|
assert pd.shape == (3, 2, 2)
|
||||||
|
assert pd.dtype == np.float64
|
||||||
|
assert pd[0, 0, 0] == 200
|
||||||
|
assert pd[1, 0, 0] == 0
|
||||||
|
assert pd[2, 0, 0] == 0
|
||||||
|
|
||||||
|
assert pd[0, 0, 1] == 0
|
||||||
|
assert pd[1, 0, 1] == 200
|
||||||
|
assert pd[2, 0, 1] == 0
|
||||||
|
|
||||||
|
assert pd[0, 1, 0] == 38
|
||||||
|
assert pd[1, 1, 0] == 37
|
||||||
|
assert pd[2, 1, 0] == 39
|
||||||
|
|
||||||
|
assert pd[0, 1, 1] == 0
|
||||||
|
assert pd[1, 1, 1] == 0
|
||||||
|
assert pd[2, 1, 1] == 200
|
||||||
|
|
||||||
|
def test_calculate_pedestal_float(raw_data_3x2x2):
|
||||||
|
#results should be the same for float
|
||||||
|
pd2 = aare.calculate_pedestal_float(raw_data_3x2x2)
|
||||||
|
assert pd2.shape == (3, 2, 2)
|
||||||
|
assert pd2.dtype == np.float32
|
||||||
|
assert pd2[0, 0, 0] == 200
|
||||||
|
assert pd2[1, 0, 0] == 0
|
||||||
|
assert pd2[2, 0, 0] == 0
|
||||||
|
|
||||||
|
assert pd2[0, 0, 1] == 0
|
||||||
|
assert pd2[1, 0, 1] == 200
|
||||||
|
assert pd2[2, 0, 1] == 0
|
||||||
|
|
||||||
|
assert pd2[0, 1, 0] == 38
|
||||||
|
assert pd2[1, 1, 0] == 37
|
||||||
|
assert pd2[2, 1, 0] == 39
|
||||||
|
|
||||||
|
assert pd2[0, 1, 1] == 0
|
||||||
|
assert pd2[1, 1, 1] == 0
|
||||||
|
assert pd2[2, 1, 1] == 200
|
||||||
|
|
||||||
|
def test_calculate_pedestal_g0(raw_data_3x2x2):
|
||||||
|
pd = aare.calculate_pedestal_g0(raw_data_3x2x2)
|
||||||
|
assert pd.shape == (2, 2)
|
||||||
|
assert pd.dtype == np.float64
|
||||||
|
assert pd[0, 0] == 200
|
||||||
|
assert pd[1, 0] == 38
|
||||||
|
assert pd[0, 1] == 0
|
||||||
|
assert pd[1, 1] == 0
|
||||||
|
|
||||||
|
def test_calculate_pedestal_g0_float(raw_data_3x2x2):
|
||||||
|
pd = aare.calculate_pedestal_g0_float(raw_data_3x2x2)
|
||||||
|
assert pd.shape == (2, 2)
|
||||||
|
assert pd.dtype == np.float32
|
||||||
|
assert pd[0, 0] == 200
|
||||||
|
assert pd[1, 0] == 38
|
||||||
|
assert pd[0, 1] == 0
|
||||||
|
assert pd[1, 1] == 0
|
||||||
|
|
||||||
|
def test_count_switching_pixels(raw_data_3x2x2):
|
||||||
|
# Count the number of pixels that switched gain
|
||||||
|
count = aare.count_switching_pixels(raw_data_3x2x2)
|
||||||
|
assert count.shape == (2, 2)
|
||||||
|
assert count.sum() == 8
|
||||||
|
assert count[0, 0] == 0
|
||||||
|
assert count[1, 0] == 2
|
||||||
|
assert count[0, 1] == 3
|
||||||
|
assert count[1, 1] == 3
|
||||||
@@ -25,13 +25,13 @@ TEST_CASE("Construct from an NDView") {
|
|||||||
REQUIRE(image.data() != view.data());
|
REQUIRE(image.data() != view.data());
|
||||||
|
|
||||||
for (uint32_t i = 0; i < image.size(); ++i) {
|
for (uint32_t i = 0; i < image.size(); ++i) {
|
||||||
REQUIRE(image(i) == view(i));
|
REQUIRE(image[i] == view[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changing the image doesn't change the view
|
// Changing the image doesn't change the view
|
||||||
image = 43;
|
image = 43;
|
||||||
for (uint32_t i = 0; i < image.size(); ++i) {
|
for (uint32_t i = 0; i < image.size(); ++i) {
|
||||||
REQUIRE(image(i) != view(i));
|
REQUIRE(image[i] != view[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,4 +427,30 @@ TEST_CASE("Construct an NDArray from an std::array") {
|
|||||||
for (uint32_t i = 0; i < a.size(); ++i) {
|
for (uint32_t i = 0; i < a.size(); ++i) {
|
||||||
REQUIRE(a(i) == b[i]);
|
REQUIRE(a(i) == b[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("Move construct from an array with Ndim + 1") {
|
||||||
|
NDArray<int, 3> a({{1,2,2}}, 0);
|
||||||
|
a(0, 0, 0) = 1;
|
||||||
|
a(0, 0, 1) = 2;
|
||||||
|
a(0, 1, 0) = 3;
|
||||||
|
a(0, 1, 1) = 4;
|
||||||
|
|
||||||
|
|
||||||
|
NDArray<int, 2> b(std::move(a));
|
||||||
|
REQUIRE(b.shape() == Shape<2>{2,2});
|
||||||
|
REQUIRE(b.size() == 4);
|
||||||
|
REQUIRE(b(0, 0) == 1);
|
||||||
|
REQUIRE(b(0, 1) == 2);
|
||||||
|
REQUIRE(b(1, 0) == 3);
|
||||||
|
REQUIRE(b(1, 1) == 4);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Move construct from an array with Ndim + 1 throws on size mismatch") {
|
||||||
|
NDArray<int, 3> a({{2,2,2}}, 0);
|
||||||
|
REQUIRE_THROWS(NDArray<int, 2>(std::move(a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
44
src/calibration.cpp
Normal file
44
src/calibration.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include "aare/calibration.hpp"
|
||||||
|
|
||||||
|
namespace aare {
|
||||||
|
|
||||||
|
NDArray<int, 2> count_switching_pixels(NDView<uint16_t, 3> raw_data) {
|
||||||
|
NDArray<int, 2> switched(
|
||||||
|
std::array<ssize_t, 2>{raw_data.shape(1), raw_data.shape(2)}, 0);
|
||||||
|
for (int frame_nr = 0; frame_nr != raw_data.shape(0); ++frame_nr) {
|
||||||
|
for (int row = 0; row != raw_data.shape(1); ++row) {
|
||||||
|
for (int col = 0; col != raw_data.shape(2); ++col) {
|
||||||
|
auto [value, gain] =
|
||||||
|
get_value_and_gain(raw_data(frame_nr, row, col));
|
||||||
|
if (gain != 0) {
|
||||||
|
switched(row, col) += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return switched;
|
||||||
|
}
|
||||||
|
|
||||||
|
NDArray<int, 2> count_switching_pixels(NDView<uint16_t, 3> raw_data,
|
||||||
|
ssize_t n_threads) {
|
||||||
|
NDArray<int, 2> switched(
|
||||||
|
std::array<ssize_t, 2>{raw_data.shape(1), raw_data.shape(2)}, 0);
|
||||||
|
std::vector<std::future<NDArray<int, 2>>> futures;
|
||||||
|
futures.reserve(n_threads);
|
||||||
|
|
||||||
|
auto subviews = make_subviews(raw_data, n_threads);
|
||||||
|
|
||||||
|
for (auto view : subviews) {
|
||||||
|
futures.push_back(
|
||||||
|
std::async(static_cast<NDArray<int, 2> (*)(NDView<uint16_t, 3>)>(
|
||||||
|
&count_switching_pixels),
|
||||||
|
view));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &f : futures) {
|
||||||
|
switched += f.get();
|
||||||
|
}
|
||||||
|
return switched;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aare
|
||||||
49
src/calibration.test.cpp
Normal file
49
src/calibration.test.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/************************************************
|
||||||
|
* @file test-Cluster.cpp
|
||||||
|
* @short test case for generic Cluster, ClusterVector, and calculate_eta2
|
||||||
|
***********************************************/
|
||||||
|
|
||||||
|
#include "aare/calibration.hpp"
|
||||||
|
|
||||||
|
// #include "catch.hpp"
|
||||||
|
#include <array>
|
||||||
|
#include <catch2/catch_all.hpp>
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
using namespace aare;
|
||||||
|
|
||||||
|
TEST_CASE("Test Pedestal Generation", "[.calibration]") {
|
||||||
|
NDArray<uint16_t, 3> raw(std::array<ssize_t, 3>{3, 2, 2}, 0);
|
||||||
|
|
||||||
|
// gain 0
|
||||||
|
raw(0, 0, 0) = 100;
|
||||||
|
raw(1, 0, 0) = 200;
|
||||||
|
raw(2, 0, 0) = 300;
|
||||||
|
|
||||||
|
// gain 1
|
||||||
|
raw(0, 0, 1) = (1 << 14) + 100;
|
||||||
|
raw(1, 0, 1) = (1 << 14) + 200;
|
||||||
|
raw(2, 0, 1) = (1 << 14) + 300;
|
||||||
|
|
||||||
|
raw(0, 1, 0) = (1 << 14) + 37;
|
||||||
|
raw(1, 1, 0) = 38;
|
||||||
|
raw(2, 1, 0) = (3 << 14) + 39;
|
||||||
|
|
||||||
|
// gain 2
|
||||||
|
raw(0, 1, 1) = (3 << 14) + 100;
|
||||||
|
raw(1, 1, 1) = (3 << 14) + 200;
|
||||||
|
raw(2, 1, 1) = (3 << 14) + 300;
|
||||||
|
|
||||||
|
auto pedestal = calculate_pedestal<double>(raw.view(), 4);
|
||||||
|
|
||||||
|
REQUIRE(pedestal.size() == raw.size());
|
||||||
|
CHECK(pedestal(0, 0, 0) == 200);
|
||||||
|
CHECK(pedestal(1, 0, 0) == 0);
|
||||||
|
CHECK(pedestal(1, 0, 1) == 200);
|
||||||
|
|
||||||
|
auto pedestal_gain0 = calculate_pedestal_g0<double>(raw.view(), 4);
|
||||||
|
|
||||||
|
REQUIRE(pedestal_gain0.size() == 4);
|
||||||
|
CHECK(pedestal_gain0(0, 0) == 200);
|
||||||
|
CHECK(pedestal_gain0(1, 0) == 38);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user