Taking v1 as the first release (#92)

- file reading
- decoding master file
This commit is contained in:
Erik Fröjdh
2024-11-07 10:14:20 +01:00
committed by GitHub
parent ae71e23dd2
commit d8d1f0c517
87 changed files with 9860 additions and 0 deletions

View File

@ -0,0 +1,148 @@
#pragma once
#include "aare/core/defs.hpp"
#include <filesystem>
#include <string>
#include <fmt/format.h>
namespace aare {
struct ClusterHeader {
int32_t frame_number;
int32_t n_clusters;
std::string to_string() const {
return "frame_number: " + std::to_string(frame_number) + ", n_clusters: " + std::to_string(n_clusters);
}
};
struct ClusterV2_ {
int16_t x;
int16_t y;
std::array<int32_t, 9> data;
std::string to_string(bool detailed = false) const {
if (detailed) {
std::string data_str = "[";
for (auto &d : data) {
data_str += std::to_string(d) + ", ";
}
data_str += "]";
return "x: " + std::to_string(x) + ", y: " + std::to_string(y) + ", data: " + data_str;
}
return "x: " + std::to_string(x) + ", y: " + std::to_string(y);
}
};
struct ClusterV2 {
ClusterV2_ cluster;
int32_t frame_number;
std::string to_string() const {
return "frame_number: " + std::to_string(frame_number) + ", " + cluster.to_string();
}
};
/**
* @brief
* important not: fp always points to the clusters header and does not point to individual clusters
*
*/
class ClusterFileV2 {
std::filesystem::path m_fpath;
std::string m_mode;
FILE *fp{nullptr};
void check_open(){
if (!fp)
throw std::runtime_error(fmt::format("File: {} not open", m_fpath.string()));
}
public:
ClusterFileV2(std::filesystem::path const &fpath, std::string const &mode): m_fpath(fpath), m_mode(mode) {
if (m_mode != "r" && m_mode != "w")
throw std::invalid_argument("mode must be 'r' or 'w'");
if (m_mode == "r" && !std::filesystem::exists(m_fpath))
throw std::invalid_argument("File does not exist");
if (mode == "r") {
fp = fopen(fpath.string().c_str(), "rb");
} else if (mode == "w") {
if (std::filesystem::exists(fpath)) {
fp = fopen(fpath.string().c_str(), "r+b");
} else {
fp = fopen(fpath.string().c_str(), "wb");
}
}
if (fp == nullptr) {
throw std::runtime_error("Failed to open file");
}
}
~ClusterFileV2() { close(); }
std::vector<ClusterV2> read() {
check_open();
ClusterHeader header;
fread(&header, sizeof(ClusterHeader), 1, fp);
std::vector<ClusterV2_> clusters_(header.n_clusters);
fread(clusters_.data(), sizeof(ClusterV2_), header.n_clusters, fp);
std::vector<ClusterV2> clusters;
for (auto &c : clusters_) {
ClusterV2 cluster;
cluster.cluster = std::move(c);
cluster.frame_number = header.frame_number;
clusters.push_back(cluster);
}
return clusters;
}
std::vector<std::vector<ClusterV2>> read(int n_frames) {
std::vector<std::vector<ClusterV2>> clusters;
for (int i = 0; i < n_frames; i++) {
clusters.push_back(read());
}
return clusters;
}
size_t write(std::vector<ClusterV2> const &clusters) {
check_open();
if (m_mode != "w")
throw std::runtime_error("File not opened in write mode");
if (clusters.empty())
return 0;
ClusterHeader header;
header.frame_number = clusters[0].frame_number;
header.n_clusters = clusters.size();
fwrite(&header, sizeof(ClusterHeader), 1, fp);
for (auto &c : clusters) {
fwrite(&c.cluster, sizeof(ClusterV2_), 1, fp);
}
return clusters.size();
}
size_t write(std::vector<std::vector<ClusterV2>> const &clusters) {
check_open();
if (m_mode != "w")
throw std::runtime_error("File not opened in write mode");
size_t n_clusters = 0;
for (auto &c : clusters) {
n_clusters += write(c);
}
return n_clusters;
}
int seek_to_begin() { return fseek(fp, 0, SEEK_SET); }
int seek_to_end() { return fseek(fp, 0, SEEK_END); }
int32_t frame_number() {
auto pos = ftell(fp);
ClusterHeader header;
fread(&header, sizeof(ClusterHeader), 1, fp);
fseek(fp, pos, SEEK_SET);
return header.frame_number;
}
void close() {
if (fp) {
fclose(fp);
fp = nullptr;
}
}
};
} // namespace aare

View File

@ -0,0 +1,259 @@
#pragma once
#include "aare/Dtype.hpp"
#include "aare/NDArray.hpp"
#include "aare/NDView.hpp"
#include "aare/Pedestal.hpp"
#include "aare/defs.hpp"
#include <cstddef>
namespace aare {
/** enum to define the event types */
enum eventType {
PEDESTAL, /** pedestal */
NEIGHBOUR, /** neighbour i.e. below threshold, but in the cluster of a
photon */
PHOTON, /** photon i.e. above threshold */
PHOTON_MAX, /** maximum of a cluster satisfying the photon conditions */
NEGATIVE_PEDESTAL, /** negative value, will not be accounted for as pedestal
in order to avoid drift of the pedestal towards
negative values */
UNDEFINED_EVENT = -1 /** undefined */
};
template <typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double>
class ClusterFinder {
Shape<2> m_image_size;
const int m_cluster_sizeX;
const int m_cluster_sizeY;
const double m_threshold;
const double m_nSigma;
const double c2;
const double c3;
Pedestal<PEDESTAL_TYPE> m_pedestal;
public:
ClusterFinder(Shape<2> image_size, Shape<2>cluster_size, double nSigma = 5.0,
double threshold = 0.0)
: m_image_size(image_size), m_cluster_sizeX(cluster_size[0]), m_cluster_sizeY(cluster_size[1]),
m_threshold(threshold), m_nSigma(nSigma),
c2(sqrt((m_cluster_sizeY + 1) / 2 * (m_cluster_sizeX + 1) / 2)),
c3(sqrt(m_cluster_sizeX * m_cluster_sizeY)),
m_pedestal(image_size[0], image_size[1]) {
// c2 = sqrt((cluster_sizeY + 1) / 2 * (cluster_sizeX + 1) / 2);
// c3 = sqrt(cluster_sizeX * cluster_sizeY);
};
void push_pedestal_frame(NDView<FRAME_TYPE, 2> frame) {
m_pedestal.push(frame);
}
NDArray<PEDESTAL_TYPE, 2> pedestal() {
return m_pedestal.mean();
}
std::vector<Cluster>
find_clusters_without_threshold(NDView<FRAME_TYPE, 2> frame,
// Pedestal<PEDESTAL_TYPE> &pedestal,
bool late_update = false) {
struct pedestal_update {
int x;
int y;
FRAME_TYPE value;
};
std::vector<pedestal_update> pedestal_updates;
std::vector<Cluster> clusters;
std::vector<std::vector<eventType>> eventMask;
for (int i = 0; i < frame.shape(0); i++) {
eventMask.push_back(std::vector<eventType>(frame.shape(1)));
}
long double val;
long double max;
for (int iy = 0; iy < frame.shape(0); iy++) {
for (int ix = 0; ix < frame.shape(1); ix++) {
// initialize max and total
max = std::numeric_limits<FRAME_TYPE>::min();
long double total = 0;
eventMask[iy][ix] = PEDESTAL;
for (short ir = -(m_cluster_sizeY / 2);
ir < (m_cluster_sizeY / 2) + 1; ir++) {
for (short ic = -(m_cluster_sizeX / 2);
ic < (m_cluster_sizeX / 2) + 1; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) {
val = frame(iy + ir, ix + ic) -
m_pedestal.mean(iy + ir, ix + ic);
total += val;
if (val > max) {
max = val;
}
}
}
}
auto rms = m_pedestal.std(iy, ix);
if (frame(iy, ix) - m_pedestal.mean(iy, ix) < -m_nSigma * rms) {
eventMask[iy][ix] = NEGATIVE_PEDESTAL;
continue;
} else if (max > m_nSigma * rms) {
eventMask[iy][ix] = PHOTON;
} else if (total > c3 * m_nSigma * rms) {
eventMask[iy][ix] = PHOTON;
} else {
if (late_update) {
pedestal_updates.push_back({ix, iy, frame(iy, ix)});
} else {
m_pedestal.push(iy, ix, frame(iy, ix));
}
continue;
}
if (eventMask[iy][ix] == PHOTON &&
(frame(iy, ix) - m_pedestal.mean(iy, ix)) >= max) {
eventMask[iy][ix] = PHOTON_MAX;
Cluster cluster(m_cluster_sizeX, m_cluster_sizeY,
Dtype(typeid(PEDESTAL_TYPE)));
cluster.x = ix;
cluster.y = iy;
short i = 0;
for (short ir = -(m_cluster_sizeY / 2);
ir < (m_cluster_sizeY / 2) + 1; ir++) {
for (short ic = -(m_cluster_sizeX / 2);
ic < (m_cluster_sizeX / 2) + 1; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) {
PEDESTAL_TYPE tmp =
static_cast<PEDESTAL_TYPE>(
frame(iy + ir, ix + ic)) -
m_pedestal.mean(iy + ir, ix + ic);
cluster.set<PEDESTAL_TYPE>(i, tmp);
i++;
}
}
}
clusters.push_back(cluster);
}
}
}
if (late_update) {
for (auto &update : pedestal_updates) {
m_pedestal.push(update.y, update.x, update.value);
}
}
return clusters;
}
// template <typename FRAME_TYPE, typename PEDESTAL_TYPE>
std::vector<Cluster>
find_clusters_with_threshold(NDView<FRAME_TYPE, 2> frame,
Pedestal<PEDESTAL_TYPE> &pedestal) {
assert(m_threshold > 0);
std::vector<Cluster> clusters;
std::vector<std::vector<eventType>> eventMask;
for (int i = 0; i < frame.shape(0); i++) {
eventMask.push_back(std::vector<eventType>(frame.shape(1)));
}
double tthr, tthr1, tthr2;
NDArray<FRAME_TYPE, 2> rest({frame.shape(0), frame.shape(1)});
NDArray<int, 2> nph({frame.shape(0), frame.shape(1)});
// convert to n photons
// nph = (frame-pedestal.mean()+0.5*m_threshold)/m_threshold; // can be
// optimized with expression templates?
for (int iy = 0; iy < frame.shape(0); iy++) {
for (int ix = 0; ix < frame.shape(1); ix++) {
auto val = frame(iy, ix) - pedestal.mean(iy, ix);
nph(iy, ix) = (val + 0.5 * m_threshold) / m_threshold;
nph(iy, ix) = nph(iy, ix) < 0 ? 0 : nph(iy, ix);
rest(iy, ix) = val - nph(iy, ix) * m_threshold;
}
}
// iterate over frame pixels
for (int iy = 0; iy < frame.shape(0); iy++) {
for (int ix = 0; ix < frame.shape(1); ix++) {
eventMask[iy][ix] = PEDESTAL;
// initialize max and total
FRAME_TYPE max = std::numeric_limits<FRAME_TYPE>::min();
long double total = 0;
if (rest(iy, ix) <= 0.25 * m_threshold) {
pedestal.push(iy, ix, frame(iy, ix));
continue;
}
eventMask[iy][ix] = NEIGHBOUR;
// iterate over cluster pixels around the current pixel (ix,iy)
for (short ir = -(m_cluster_sizeY / 2);
ir < (m_cluster_sizeY / 2) + 1; ir++) {
for (short ic = -(m_cluster_sizeX / 2);
ic < (m_cluster_sizeX / 2) + 1; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) {
auto val = frame(iy + ir, ix + ic) -
pedestal.mean(iy + ir, ix + ic);
total += val;
if (val > max) {
max = val;
}
}
}
}
auto rms = pedestal.std(iy, ix);
if (m_nSigma == 0) {
tthr = m_threshold;
tthr1 = m_threshold;
tthr2 = m_threshold;
} else {
tthr = m_nSigma * rms;
tthr1 = m_nSigma * rms * c3;
tthr2 = m_nSigma * rms * c2;
if (m_threshold > 2 * tthr)
tthr = m_threshold - tthr;
if (m_threshold > 2 * tthr1)
tthr1 = tthr - tthr1;
if (m_threshold > 2 * tthr2)
tthr2 = tthr - tthr2;
}
if (total > tthr1 || max > tthr) {
eventMask[iy][ix] = PHOTON;
nph(iy, ix) += 1;
rest(iy, ix) -= m_threshold;
} else {
pedestal.push(iy, ix, frame(iy, ix));
continue;
}
if (eventMask[iy][ix] == PHOTON &&
frame(iy, ix) - pedestal.mean(iy, ix) >= max) {
eventMask[iy][ix] = PHOTON_MAX;
Cluster cluster(m_cluster_sizeX, m_cluster_sizeY,
Dtype(typeid(FRAME_TYPE)));
cluster.x = ix;
cluster.y = iy;
short i = 0;
for (short ir = -(m_cluster_sizeY / 2);
ir < (m_cluster_sizeY / 2) + 1; ir++) {
for (short ic = -(m_cluster_sizeX / 2);
ic < (m_cluster_sizeX / 2) + 1; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) {
auto tmp = frame(iy + ir, ix + ic) -
pedestal.mean(iy + ir, ix + ic);
cluster.set<FRAME_TYPE>(i, tmp);
i++;
}
}
}
clusters.push_back(cluster);
}
}
}
return clusters;
}
};
} // namespace aare

View File

@ -0,0 +1,41 @@
#pragma once
#include "aare/FileInterface.hpp"
#include "aare/RawMasterFile.hpp"
#include "aare/Frame.hpp"
#include <filesystem>
#include <fstream>
namespace aare{
class CtbRawFile{
RawMasterFile m_master;
std::ifstream m_file;
size_t m_current_frame{0};
size_t m_current_subfile{0};
size_t m_num_subfiles{0};
public:
CtbRawFile(const std::filesystem::path &fname);
void read_into(std::byte *image_buf, DetectorHeader* header = nullptr);
void seek(size_t frame_index); //!< seek to the given frame index
size_t tell() const; //!< get the frame index of the file pointer
// in the specific class we can expose more functionality
size_t image_size_in_bytes() const;
size_t frames_in_file() const;
RawMasterFile master() const;
private:
void find_subfiles();
size_t sub_file_index(size_t frame_index) const {
return frame_index / m_master.max_frames_per_file();
}
void open_data_file(size_t subfile_index);
};
}

83
include/aare/Dtype.hpp Normal file
View File

@ -0,0 +1,83 @@
#pragma once
#include <cstdint>
#include <map>
#include <string>
#include <typeinfo>
namespace aare {
// The format descriptor is a single character that specifies the type of the data
// - python documentation: https://docs.python.org/3/c-api/arg.html#numbers
// - py::format_descriptor<T>::format() (in pybind11) does not return the same format as
// written in python.org documentation.
// - numpy also doesn't use the same format. and also numpy associates the format
// with variable bitdepth types. (e.g. long is int64 on linux64 and int32 on win64)
// https://numpy.org/doc/stable/reference/arrays.scalars.html
//
// github issue discussing this:
// https://github.com/pybind/pybind11/issues/1908#issuecomment-658358767
//
// [IN LINUX] the difference is for int64 (long) and uint64 (unsigned long). The format
// descriptor is 'q' and 'Q' respectively and in the documentation it is 'l' and 'k'.
// in practice numpy doesn't seem to care when reading buffer info: the library
// interprets 'q' or 'l' as int64 and 'Q' or 'L' as uint64.
// for this reason we decided to use the same format descriptor as pybind to avoid
// any further discrepancies.
// in the following order:
// int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, double
const char DTYPE_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i', 'I', 'q', 'Q', 'f', 'd'};
// on linux64 & apple
const char NUMPY_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'f', 'd'};
/**
* @brief enum class to define the endianess of the system
*/
enum class endian {
#ifdef _WIN32
little = 0,
big = 1,
native = little
#else
little = __ORDER_LITTLE_ENDIAN__,
big = __ORDER_BIG_ENDIAN__,
native = __BYTE_ORDER__
#endif
};
/**
* @brief class to define the data type of the pixels
* @note only native endianess is supported
*/
class Dtype {
public:
enum TypeIndex { INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT, DOUBLE, ERROR, NONE };
uint8_t bitdepth() const;
size_t bytes() const;
std::string format_descr() const { return std::string(1, DTYPE_FORMAT_DSC[static_cast<int>(m_type)]); }
std::string numpy_descr() const { return std::string(1, NUMPY_FORMAT_DSC[static_cast<int>(m_type)]); }
explicit Dtype(const std::type_info &t);
explicit Dtype(std::string_view sv);
static Dtype from_bitdepth(uint8_t bitdepth);
// not explicit to allow conversions form enum to DType
Dtype(Dtype::TypeIndex ti); // NOLINT
bool operator==(const Dtype &other) const noexcept;
bool operator!=(const Dtype &other) const noexcept;
bool operator==(const std::type_info &t) const;
bool operator!=(const std::type_info &t) const;
// bool operator==(DType::TypeIndex ti) const;
// bool operator!=(DType::TypeIndex ti) const;
std::string to_string() const;
void set_type(Dtype::TypeIndex ti) { m_type = ti; }
private:
TypeIndex m_type{TypeIndex::ERROR};
};
} // namespace aare

63
include/aare/File.hpp Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include "aare/FileInterface.hpp"
#include <memory>
namespace aare {
/**
* @brief RAII File class for reading, and in the future potentially writing
* image files in various formats. Minimal generic interface. For specail fuctions
* plase use the RawFile or NumpyFile classes directly.
* Wraps FileInterface to abstract the underlying file format
* @note **frame_number** refers the the frame number sent by the detector while **frame_index**
* is the position of the frame in the file
*/
class File {
std::unique_ptr<FileInterface> file_impl;
public:
/**
* @brief Construct a new File object
* @param fname path to the file
* @param mode file mode (r, w, a)
* @param cfg file configuration
* @throws std::runtime_error if the file cannot be opened
* @throws std::invalid_argument if the file mode is not supported
*
*/
File(const std::filesystem::path &fname, const std::string &mode="r", const FileConfig &cfg = {});
/**Since the object is responsible for managing the file we disable copy construction */
File(File const &other) = delete;
/**The same goes for copy assignment */
File& operator=(File const &other) = delete;
File(File &&other) noexcept;
File& operator=(File &&other) noexcept;
~File() = default;
Frame read_frame(); //!< read one frame from the file at the current position
Frame read_frame(size_t frame_index); //!< read one frame at the position given by frame number
std::vector<Frame> read_n(size_t n_frames); //!< read n_frames from the file at the current position
void read_into(std::byte *image_buf);
void read_into(std::byte *image_buf, size_t n_frames);
size_t frame_number(size_t frame_index); //!< get the frame number at the given frame index
size_t bytes_per_frame() const;
size_t pixels_per_frame() const;
size_t bytes_per_pixel() const;
size_t bitdepth() const;
void seek(size_t frame_index); //!< seek to the given frame index
size_t tell() const; //!< get the frame index of the file pointer
size_t total_frames() const;
size_t rows() const;
size_t cols() const;
DetectorType detector_type() const;
};
} // namespace aare

View File

@ -0,0 +1,161 @@
#pragma once
#include "aare/Dtype.hpp"
#include "aare/Frame.hpp"
#include "aare/defs.hpp"
#include <filesystem>
#include <vector>
namespace aare {
/**
* @brief FileConfig structure to store the configuration of a file
* dtype: data type of the file
* rows: number of rows in the file
* cols: number of columns in the file
* geometry: geometry of the file
*/
struct FileConfig {
aare::Dtype dtype{typeid(uint16_t)};
uint64_t rows{};
uint64_t cols{};
bool operator==(const FileConfig &other) const {
return dtype == other.dtype && rows == other.rows && cols == other.cols && geometry == other.geometry &&
detector_type == other.detector_type && max_frames_per_file == other.max_frames_per_file;
}
bool operator!=(const FileConfig &other) const { return !(*this == other); }
// rawfile specific
std::string version{};
xy geometry{1, 1};
DetectorType detector_type{DetectorType::Unknown};
int max_frames_per_file{};
size_t total_frames{};
std::string to_string() const {
return "{ dtype: " + dtype.to_string() + ", rows: " + std::to_string(rows) + ", cols: " + std::to_string(cols) +
", geometry: " + geometry.to_string() + ", detector_type: " + ToString(detector_type) +
", max_frames_per_file: " + std::to_string(max_frames_per_file) +
", total_frames: " + std::to_string(total_frames) + " }";
}
};
/**
* @brief FileInterface class to define the interface for file operations
* @note parent class for NumpyFile and RawFile
* @note all functions are pure virtual and must be implemented by the derived classes
*/
class FileInterface {
public:
/**
* @brief one frame from the file at the current position
* @return Frame
*/
virtual Frame read_frame() = 0;
/**
* @brief read one frame from the file at the given frame number
* @param frame_number frame number to read
* @return frame
*/
virtual Frame read_frame(size_t frame_number) = 0;
/**
* @brief read n_frames from the file at the current position
* @param n_frames number of frames to read
* @return vector of frames
*/
virtual std::vector<Frame> read_n(size_t n_frames) = 0; // Is this the right interface?
/**
* @brief read one frame from the file at the current position and store it in the provided buffer
* @param image_buf buffer to store the frame
* @return void
*/
virtual void read_into(std::byte *image_buf) = 0;
/**
* @brief read n_frames from the file at the current position and store them in the provided buffer
* @param image_buf buffer to store the frames
* @param n_frames number of frames to read
* @return void
*/
virtual void read_into(std::byte *image_buf, size_t n_frames) = 0;
/**
* @brief get the frame number at the given frame index
* @param frame_index index of the frame
* @return frame number
*/
virtual size_t frame_number(size_t frame_index) = 0;
/**
* @brief get the size of one frame in bytes
* @return size of one frame
*/
virtual size_t bytes_per_frame() = 0;
/**
* @brief get the number of pixels in one frame
* @return number of pixels in one frame
*/
virtual size_t pixels_per_frame() = 0;
/**
* @brief seek to the given frame number
* @param frame_number frame number to seek to
* @return void
*/
virtual void seek(size_t frame_number) = 0;
/**
* @brief get the current position of the file pointer
* @return current position of the file pointer
*/
virtual size_t tell() = 0;
/**
* @brief get the total number of frames in the file
* @return total number of frames in the file
*/
virtual size_t total_frames() const = 0;
/**
* @brief get the number of rows in the file
* @return number of rows in the file
*/
virtual size_t rows() const = 0;
/**
* @brief get the number of columns in the file
* @return number of columns in the file
*/
virtual size_t cols() const = 0;
/**
* @brief get the bitdepth of the file
* @return bitdepth of the file
*/
virtual size_t bitdepth() const = 0;
virtual DetectorType detector_type() const = 0;
// function to query the data type of the file
/*virtual DataType dtype = 0; */
virtual ~FileInterface() = default;
protected:
std::string m_mode{};
// std::filesystem::path m_fname{};
// std::filesystem::path m_base_path{};
// std::string m_base_name{}, m_ext{};
// int m_findex{};
// size_t m_total_frames{};
// size_t max_frames_per_file{};
// std::string version{};
// DetectorType m_type{DetectorType::Unknown};
// size_t m_rows{};
// size_t m_cols{};
// size_t m_bitdepth{};
// size_t current_frame{};
};
} // namespace aare

124
include/aare/Frame.hpp Normal file
View File

@ -0,0 +1,124 @@
#pragma once
#include "aare/Dtype.hpp"
#include "aare/NDArray.hpp"
#include "aare/defs.hpp"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
namespace aare {
/**
* @brief Frame class to represent a single frame of data. Not much more than a
* pointer and some info. Limited interface to accept frames from many sources.
*/
class Frame {
uint32_t m_rows;
uint32_t m_cols;
Dtype m_dtype;
std::byte *m_data;
//TODO! Add frame number?
public:
/**
* @brief Construct a new Frame
* @param rows number of rows
* @param cols number of columns
* @param dtype data type of the pixels
* @note the data is initialized to zero
*/
Frame(uint32_t rows, uint32_t cols, Dtype dtype);
/**
* @brief Construct a new Frame
* @param bytes pointer to the data to be copied into the frame
* @param rows number of rows
* @param cols number of columns
* @param dtype data type of the pixels
*/
Frame(const std::byte *bytes, uint32_t rows, uint32_t cols, Dtype dtype);
~Frame(){ delete[] m_data; };
/** @warning Copy is disabled to ensure performance when passing
* frames around. Can discuss enabling it.
*
*/
Frame &operator=(const Frame &other) = delete;
Frame(const Frame &other) = delete;
// enable move
Frame &operator=(Frame &&other) noexcept;
Frame(Frame &&other) noexcept;
Frame clone() const; //<- Explicit copy
uint32_t rows() const;
uint32_t cols() const;
size_t bitdepth() const;
Dtype dtype() const;
uint64_t size() const;
size_t bytes() const;
std::byte *data() const;
/**
* @brief Get the pointer to the pixel at the given row and column
* @param row row index
* @param col column index
* @return pointer to the pixel
* @warning The user should cast the pointer to the appropriate type. Think
* twice if this is the function you want to use.
*/
std::byte *pixel_ptr(uint32_t row, uint32_t col) const;
/**
* @brief Set the pixel at the given row and column to the given value
* @tparam T type of the value
* @param row row index
* @param col column index
* @param data value to set
*/
template <typename T> void set(uint32_t row, uint32_t col, T data) {
assert(sizeof(T) == m_dtype.bytes());
if (row >= m_rows || col >= m_cols) {
throw std::out_of_range("Invalid row or column index");
}
std::memcpy(m_data + (row * m_cols + col) * m_dtype.bytes(), &data,
m_dtype.bytes());
}
template <typename T> T get(uint32_t row, uint32_t col) {
assert(sizeof(T) == m_dtype.bytes());
if (row >= m_rows || col >= m_cols) {
throw std::out_of_range("Invalid row or column index");
}
//TODO! add tests then reimplement using pixel_ptr
T data;
std::memcpy(&data, m_data + (row * m_cols + col) * m_dtype.bytes(),
m_dtype.bytes());
return data;
}
/**
* @brief Return an NDView of the frame. This is the preferred way to access
* data in the frame.
*
* @tparam T type of the pixels
* @return NDView<T, 2>
*/
template <typename T> NDView<T, 2> view() {
std::array<int64_t, 2> shape = {static_cast<int64_t>(m_rows),
static_cast<int64_t>(m_cols)};
T *data = reinterpret_cast<T *>(m_data);
return NDView<T, 2>(data, shape);
}
/**
* @brief Copy the frame data into a new NDArray. This is a deep copy.
*/
template <typename T> NDArray<T> image() {
return NDArray<T>(this->view<T>());
}
};
} // namespace aare

380
include/aare/NDArray.hpp Normal file
View File

@ -0,0 +1,380 @@
#pragma once
/*
Container holding image data, or a time series of image data in contigious
memory.
TODO! Add expression templates for operators
*/
#include "aare/NDView.hpp"
#include <algorithm>
#include <array>
#include <cmath>
#include <fmt/format.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <numeric>
namespace aare {
template <typename T, int64_t Ndim = 2> class NDArray {
public:
NDArray() : shape_(), strides_(c_strides<Ndim>(shape_)), data_(nullptr){};
explicit NDArray(std::array<int64_t, Ndim> shape)
: shape_(shape), strides_(c_strides<Ndim>(shape_)),
size_(std::accumulate(shape_.begin(), shape_.end(), 1, std::multiplies<>())), data_(new T[size_]){};
NDArray(std::array<int64_t, Ndim> shape, T value) : NDArray(shape) { this->operator=(value); }
/* When constructing from a NDView we need to copy the data since
NDArray expect to own its data, and span is just a view*/
explicit NDArray(NDView<T, Ndim> span) : NDArray(span.shape()) {
std::copy(span.begin(), span.end(), begin());
// fmt::print("NDArray(NDView<T, Ndim> span)\n");
}
// Move constructor
NDArray(NDArray &&other) noexcept
: shape_(other.shape_), strides_(c_strides<Ndim>(shape_)), size_(other.size_), data_(other.data_) {
other.reset();
// fmt::print("NDArray(NDArray &&other)\n");
}
// Copy constructor
NDArray(const NDArray &other)
: shape_(other.shape_), strides_(c_strides<Ndim>(shape_)), size_(other.size_), data_(new T[size_]) {
std::copy(other.data_, other.data_ + size_, data_);
// fmt::print("NDArray(const NDArray &other)\n");
}
~NDArray() { delete[] data_; }
auto begin() { return data_; }
auto end() { return data_ + size_; }
using value_type = T;
NDArray &operator=(NDArray &&other) noexcept; // Move assign
NDArray &operator=(const NDArray &other); // Copy assign
NDArray operator+(const NDArray &other);
NDArray &operator+=(const NDArray &other);
NDArray operator-(const NDArray &other);
NDArray &operator-=(const NDArray &other);
NDArray operator*(const NDArray &other);
NDArray &operator*=(const NDArray &other);
NDArray operator/(const NDArray &other);
// NDArray& operator/=(const NDArray& other);
template <typename V> NDArray &operator/=(const NDArray<V, Ndim> &other) {
// check shape
if (shape_ == other.shape()) {
for (uint32_t i = 0; i < size_; ++i) {
data_[i] /= other(i);
}
return *this;
}
throw(std::runtime_error("Shape of NDArray must match"));
}
NDArray<bool, Ndim> operator>(const NDArray &other);
bool operator==(const NDArray &other) const;
bool operator!=(const NDArray &other) const;
NDArray &operator=(const T & /*value*/);
NDArray &operator+=(const T & /*value*/);
NDArray operator+(const T & /*value*/);
NDArray &operator-=(const T & /*value*/);
NDArray operator-(const T & /*value*/);
NDArray &operator*=(const T & /*value*/);
NDArray operator*(const T & /*value*/);
NDArray &operator/=(const T & /*value*/);
NDArray operator/(const T & /*value*/);
NDArray &operator&=(const T & /*mask*/);
void sqrt() {
for (int i = 0; i < size_; ++i) {
data_[i] = std::sqrt(data_[i]);
}
}
NDArray &operator++(); // pre inc
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
return data_[element_offset(strides_, index...)];
}
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
return data_[element_offset(strides_, index...)];
}
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T> value(Ix... index) {
return data_[element_offset(strides_, index...)];
}
T &operator()(int i) { return data_[i]; }
const T &operator()(int i) const { return data_[i]; }
T *data() { return data_; }
std::byte *buffer() { return reinterpret_cast<std::byte *>(data_); }
uint64_t size() const { return size_; }
size_t total_bytes() const { return size_ * sizeof(T); }
std::array<int64_t, Ndim> shape() const noexcept { return shape_; }
int64_t shape(int64_t i) const noexcept { return shape_[i]; }
std::array<int64_t, Ndim> strides() const noexcept { return strides_; }
size_t bitdepth() const noexcept { return sizeof(T) * 8; }
std::array<int64_t, Ndim> byte_strides() const noexcept {
auto byte_strides = strides_;
for (auto &val : byte_strides)
val *= sizeof(T);
return byte_strides;
// return strides_;
}
NDView<T, Ndim> span() const { return NDView<T, Ndim>{data_, shape_}; }
void Print();
void Print_all();
void Print_some();
void reset() {
data_ = nullptr;
size_ = 0;
std::fill(shape_.begin(), shape_.end(), 0);
std::fill(strides_.begin(), strides_.end(), 0);
}
private:
std::array<int64_t, Ndim> shape_;
std::array<int64_t, Ndim> strides_;
uint64_t size_{};
T *data_;
};
// Move assign
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(NDArray<T, Ndim> &&other) noexcept {
if (this != &other) {
delete[] data_;
data_ = other.data_;
shape_ = other.shape_;
size_ = other.size_;
strides_ = other.strides_;
other.reset();
}
return *this;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> NDArray<T, Ndim>::operator+(const NDArray &other) {
NDArray result(*this);
result += other;
return result;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) {
// check shape
if (shape_ == other.shape_) {
for (uint32_t i = 0; i < size_; ++i) {
data_[i] += other.data_[i];
}
return *this;
}
throw(std::runtime_error("Shape of ImageDatas must match"));
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> NDArray<T, Ndim>::operator-(const NDArray &other) {
NDArray result{*this};
result -= other;
return result;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) {
// check shape
if (shape_ == other.shape_) {
for (uint32_t i = 0; i < size_; ++i) {
data_[i] -= other.data_[i];
}
return *this;
}
throw(std::runtime_error("Shape of ImageDatas must match"));
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> NDArray<T, Ndim>::operator*(const NDArray &other) {
NDArray result = *this;
result *= other;
return result;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) {
// check shape
if (shape_ == other.shape_) {
for (uint32_t i = 0; i < size_; ++i) {
data_[i] *= other.data_[i];
}
return *this;
}
throw(std::runtime_error("Shape of ImageDatas must match"));
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> NDArray<T, Ndim>::operator/(const NDArray &other) {
NDArray result = *this;
result /= other;
return result;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator&=(const T &mask) {
for (auto it = begin(); it != end(); ++it)
*it &= mask;
return *this;
}
// template <typename T, int64_t Ndim>
// NDArray<T, Ndim>& NDArray<T, Ndim>::operator/=(const NDArray<T, Ndim>&
// other)
// {
// //check shape
// if (shape_ == other.shape_) {
// for (int i = 0; i < size_; ++i) {
// data_[i] /= other.data_[i];
// }
// return *this;
// } else {
// throw(std::runtime_error("Shape of ImageDatas must match"));
// }
// }
template <typename T, int64_t Ndim> NDArray<bool, Ndim> NDArray<T, Ndim>::operator>(const NDArray &other) {
if (shape_ == other.shape_) {
NDArray<bool> result{shape_};
for (int i = 0; i < size_; ++i) {
result(i) = (data_[i] > other.data_[i]);
}
return result;
}
throw(std::runtime_error("Shape of ImageDatas must match"));
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const NDArray<T, Ndim> &other) {
if (this != &other) {
delete[] data_;
shape_ = other.shape_;
strides_ = other.strides_;
size_ = other.size_;
data_ = new T[size_];
std::copy(other.data_, other.data_ + size_, data_);
}
return *this;
}
template <typename T, int64_t Ndim> bool NDArray<T, Ndim>::operator==(const NDArray<T, Ndim> &other) const {
if (shape_ != other.shape_)
return false;
for (uint32_t i = 0; i != size_; ++i)
if (data_[i] != other.data_[i])
return false;
return true;
}
template <typename T, int64_t Ndim> bool NDArray<T, Ndim>::operator!=(const NDArray<T, Ndim> &other) const {
return !((*this) == other);
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator++() {
for (uint32_t i = 0; i < size_; ++i)
data_[i] += 1;
return *this;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const T &value) {
std::fill_n(data_, size_, value);
return *this;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const T &value) {
for (uint32_t i = 0; i < size_; ++i)
data_[i] += value;
return *this;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> NDArray<T, Ndim>::operator+(const T &value) {
NDArray result = *this;
result += value;
return result;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const T &value) {
for (uint32_t i = 0; i < size_; ++i)
data_[i] -= value;
return *this;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> NDArray<T, Ndim>::operator-(const T &value) {
NDArray result = *this;
result -= value;
return result;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator/=(const T &value) {
for (uint32_t i = 0; i < size_; ++i)
data_[i] /= value;
return *this;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> NDArray<T, Ndim>::operator/(const T &value) {
NDArray result = *this;
result /= value;
return result;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const T &value) {
for (uint32_t i = 0; i < size_; ++i)
data_[i] *= value;
return *this;
}
template <typename T, int64_t Ndim> NDArray<T, Ndim> NDArray<T, Ndim>::operator*(const T &value) {
NDArray result = *this;
result *= value;
return result;
}
template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print() {
if (shape_[0] < 20 && shape_[1] < 20)
Print_all();
else
Print_some();
}
template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_all() {
for (auto row = 0; row < shape_[0]; ++row) {
for (auto col = 0; col < shape_[1]; ++col) {
std::cout << std::setw(3);
std::cout << (*this)(row, col) << " ";
}
std::cout << "\n";
}
}
template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_some() {
for (auto row = 0; row < 5; ++row) {
for (auto col = 0; col < 5; ++col) {
std::cout << std::setw(7);
std::cout << (*this)(row, col) << " ";
}
std::cout << "\n";
}
}
template <typename T, int64_t Ndim> void save(NDArray<T, Ndim> &img, std::string &pathname) {
std::ofstream f;
f.open(pathname, std::ios::binary);
f.write(img.buffer(), img.size() * sizeof(T));
f.close();
}
template <typename T, int64_t Ndim>
NDArray<T, Ndim> load(const std::string &pathname, std::array<int64_t, Ndim> shape) {
NDArray<T, Ndim> img{shape};
std::ifstream f;
f.open(pathname, std::ios::binary);
f.read(img.buffer(), img.size() * sizeof(T));
f.close();
return img;
}
} // namespace aare

159
include/aare/NDView.hpp Normal file
View File

@ -0,0 +1,159 @@
#pragma once
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <functional>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <stdexcept>
#include <vector>
namespace aare {
template <int64_t Ndim> using Shape = std::array<int64_t, Ndim>;
// TODO! fix mismatch between signed and unsigned
template <int64_t Ndim> Shape<Ndim> make_shape(const std::vector<size_t> &shape) {
if (shape.size() != Ndim)
throw std::runtime_error("Shape size mismatch");
Shape<Ndim> arr;
std::copy_n(shape.begin(), Ndim, arr.begin());
return arr;
}
template <int64_t Dim = 0, typename Strides> int64_t element_offset(const Strides & /*unused*/) { return 0; }
template <int64_t Dim = 0, typename Strides, typename... Ix>
int64_t element_offset(const Strides &strides, int64_t i, Ix... index) {
return i * strides[Dim] + element_offset<Dim + 1>(strides, index...);
}
template <int64_t Ndim> std::array<int64_t, Ndim> c_strides(const std::array<int64_t, Ndim> &shape) {
std::array<int64_t, Ndim> strides{};
std::fill(strides.begin(), strides.end(), 1);
for (int64_t i = Ndim - 1; i > 0; --i) {
strides[i - 1] = strides[i] * shape[i];
}
return strides;
}
template <int64_t Ndim> std::array<int64_t, Ndim> make_array(const std::vector<int64_t> &vec) {
assert(vec.size() == Ndim);
std::array<int64_t, Ndim> arr{};
std::copy_n(vec.begin(), Ndim, arr.begin());
return arr;
}
template <typename T, int64_t Ndim = 2> class NDView {
public:
NDView() = default;
~NDView() = default;
NDView(const NDView &) = default;
NDView(NDView &&) = default;
NDView(T *buffer, std::array<int64_t, Ndim> shape)
: buffer_(buffer), strides_(c_strides<Ndim>(shape)), shape_(shape),
size_(std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<>())) {}
// NDView(T *buffer, const std::vector<int64_t> &shape)
// : buffer_(buffer), strides_(c_strides<Ndim>(make_array<Ndim>(shape))), shape_(make_array<Ndim>(shape)),
// size_(std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<>())) {}
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
return buffer_[element_offset(strides_, index...)];
}
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
return buffer_[element_offset(strides_, index...)];
}
uint64_t size() const { return size_; }
size_t total_bytes() const { return size_ * sizeof(T); }
std::array<int64_t, Ndim> strides() const noexcept { return strides_; }
T *begin() { return buffer_; }
T *end() { return buffer_ + size_; }
T &operator()(int64_t i) const { return buffer_[i]; }
T &operator[](int64_t i) const { return buffer_[i]; }
bool operator==(const NDView &other) const {
if (size_ != other.size_)
return false;
for (uint64_t i = 0; i != size_; ++i) {
if (buffer_[i] != other.buffer_[i])
return false;
}
return true;
}
NDView &operator+=(const T val) { return elemenwise(val, std::plus<T>()); }
NDView &operator-=(const T val) { return elemenwise(val, std::minus<T>()); }
NDView &operator*=(const T val) { return elemenwise(val, std::multiplies<T>()); }
NDView &operator/=(const T val) { return elemenwise(val, std::divides<T>()); }
NDView &operator/=(const NDView &other) { return elemenwise(other, std::divides<T>()); }
NDView &operator=(const T val) {
for (auto it = begin(); it != end(); ++it)
*it = val;
return *this;
}
NDView &operator=(const NDView &other) {
if (this == &other)
return *this;
shape_ = other.shape_;
strides_ = other.strides_;
size_ = other.size_;
buffer_ = other.buffer_;
return *this;
}
NDView &operator=(NDView &&other) noexcept {
if (this == &other)
return *this;
shape_ = std::move(other.shape_);
strides_ = std::move(other.strides_);
size_ = other.size_;
buffer_ = other.buffer_;
other.buffer_ = nullptr;
return *this;
}
auto &shape() { return shape_; }
auto shape(int64_t i) const { return shape_[i]; }
T *data() { return buffer_; }
void print_all() const;
private:
T *buffer_{nullptr};
std::array<int64_t, Ndim> strides_{};
std::array<int64_t, Ndim> shape_{};
uint64_t size_{};
template <class BinaryOperation> NDView &elemenwise(T val, BinaryOperation op) {
for (uint64_t i = 0; i != size_; ++i) {
buffer_[i] = op(buffer_[i], val);
}
return *this;
}
template <class BinaryOperation> NDView &elemenwise(const NDView &other, BinaryOperation op) {
for (uint64_t i = 0; i != size_; ++i) {
buffer_[i] = op(buffer_[i], other.buffer_[i]);
}
return *this;
}
};
template <typename T, int64_t Ndim> void NDView<T, Ndim>::print_all() const {
for (auto row = 0; row < shape_[0]; ++row) {
for (auto col = 0; col < shape_[1]; ++col) {
std::cout << std::setw(3);
std::cout << (*this)(row, col) << " ";
}
std::cout << "\n";
}
}
} // namespace aare

119
include/aare/NumpyFile.hpp Normal file
View File

@ -0,0 +1,119 @@
#pragma once
#include "aare/Dtype.hpp"
#include "aare/defs.hpp"
#include "aare/FileInterface.hpp"
#include "aare/NumpyHelpers.hpp"
#include <filesystem>
#include <iostream>
#include <numeric>
namespace aare {
/**
* @brief NumpyFile class to read and write numpy files
* @note derived from FileInterface
* @note implements all the pure virtual functions from FileInterface
* @note documentation for the functions can also be found in the FileInterface class
*/
class NumpyFile : public FileInterface {
public:
/**
* @brief NumpyFile constructor
* @param fname path to the numpy file
* @param mode file mode (r, w)
* @param cfg file configuration
*/
explicit NumpyFile(const std::filesystem::path &fname, const std::string &mode = "r", FileConfig cfg = {});
void write(Frame &frame);
Frame read_frame() override { return get_frame(this->current_frame++); }
Frame read_frame(size_t frame_number) override { return get_frame(frame_number); }
std::vector<Frame> read_n(size_t n_frames) override;
void read_into(std::byte *image_buf) override { return get_frame_into(this->current_frame++, image_buf); }
void read_into(std::byte *image_buf, size_t n_frames) override;
size_t frame_number(size_t frame_index) override { return frame_index; };
size_t bytes_per_frame() override;
size_t pixels_per_frame() override;
void seek(size_t frame_number) override { this->current_frame = frame_number; }
size_t tell() override { return this->current_frame; }
size_t total_frames() const override { return m_header.shape[0]; }
size_t rows() const override { return m_header.shape[1]; }
size_t cols() const override { return m_header.shape[2]; }
size_t bitdepth() const override { return m_header.dtype.bitdepth(); }
DetectorType detector_type() const override { return DetectorType::Unknown; }
/**
* @brief get the data type of the numpy file
* @return DType
*/
Dtype dtype() const { return m_header.dtype; }
/**
* @brief get the shape of the numpy file
* @return vector of type size_t
*/
std::vector<size_t> shape() const { return m_header.shape; }
/**
* @brief load the numpy file into an NDArray
* @tparam T data type of the NDArray
* @tparam NDim number of dimensions of the NDArray
* @return NDArray<T, NDim>
*/
template <typename T, size_t NDim> NDArray<T, NDim> load() {
NDArray<T, NDim> arr(make_shape<NDim>(m_header.shape));
if (fseek(fp, static_cast<int64_t>(header_size), SEEK_SET)) {
throw std::runtime_error(LOCATION + "Error seeking to the start of the data");
}
size_t rc = fread(arr.data(), sizeof(T), arr.size(), fp);
if (rc != static_cast<size_t>(arr.size())) {
throw std::runtime_error(LOCATION + "Error reading data from file");
}
return arr;
}
template <typename A, typename TYPENAME, A Ndim> void write(NDView<TYPENAME, Ndim> &frame) {
write_impl(frame.data(), frame.total_bytes());
}
template <typename A, typename TYPENAME, A Ndim> void write(NDArray<TYPENAME, Ndim> &frame) {
write_impl(frame.data(), frame.total_bytes());
}
template <typename A, typename TYPENAME, A Ndim> void write(NDView<TYPENAME, Ndim> &&frame) {
write_impl(frame.data(), frame.total_bytes());
}
template <typename A, typename TYPENAME, A Ndim> void write(NDArray<TYPENAME, Ndim> &&frame) {
write_impl(frame.data(), frame.total_bytes());
}
~NumpyFile() noexcept override;
private:
FILE *fp = nullptr;
size_t initial_header_len = 0;
size_t current_frame{};
uint32_t header_len{};
uint8_t header_len_size{};
size_t header_size{};
NumpyHeader m_header;
uint8_t major_ver_{};
uint8_t minor_ver_{};
size_t m_bytes_per_frame{};
size_t m_pixels_per_frame{};
size_t m_cols;
size_t m_rows;
size_t m_bitdepth;
void load_metadata();
void get_frame_into(size_t /*frame_number*/, std::byte * /*image_buf*/);
Frame get_frame(size_t frame_number);
void write_impl(void *data, uint64_t size);
};
} // namespace aare

View File

@ -0,0 +1,55 @@
#pragma once
#include <algorithm>
#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <numeric>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include "aare/Dtype.hpp"
#include "aare/defs.hpp"
namespace aare {
struct NumpyHeader {
Dtype dtype{aare::Dtype::ERROR};
bool fortran_order{false};
std::vector<size_t> shape{};
std::string to_string() const;
};
namespace NumpyHelpers {
const constexpr std::array<char, 6> magic_str{'\x93', 'N', 'U', 'M', 'P', 'Y'};
const uint8_t magic_string_length{6};
std::string parse_str(const std::string &in);
/**
Removes leading and trailing whitespaces
*/
std::string trim(const std::string &str);
std::vector<std::string> parse_tuple(std::string in);
bool parse_bool(const std::string &in);
std::string get_value_from_map(const std::string &mapstr);
std::unordered_map<std::string, std::string> parse_dict(std::string in, const std::vector<std::string> &keys);
template <typename T, size_t N> bool in_array(T val, const std::array<T, N> &arr) {
return std::find(std::begin(arr), std::end(arr), val) != std::end(arr);
}
bool is_digits(const std::string &str);
aare::Dtype parse_descr(std::string typestring);
size_t write_header(const std::filesystem::path &fname, const NumpyHeader &header);
size_t write_header(std::ostream &out, const NumpyHeader &header);
} // namespace NumpyHelpers
} // namespace aare

149
include/aare/Pedestal.hpp Normal file
View File

@ -0,0 +1,149 @@
#pragma once
#include "aare/Frame.hpp"
#include "aare/NDArray.hpp"
#include "aare/NDView.hpp"
#include <cstddef>
namespace aare {
/**
* @brief Calculate the pedestal of a series of frames. Can be used as
* standalone but mostly used in the ClusterFinder.
*
* @tparam SUM_TYPE type of the sum
*/
template <typename SUM_TYPE = double> class Pedestal {
uint32_t m_rows;
uint32_t m_cols;
uint32_t m_samples;
NDArray<uint32_t, 2> m_cur_samples;
NDArray<SUM_TYPE, 2> m_sum;
NDArray<SUM_TYPE, 2> m_sum2;
public:
Pedestal(uint32_t rows, uint32_t cols, uint32_t n_samples = 1000)
: m_rows(rows), m_cols(cols), m_samples(n_samples),
m_cur_samples(NDArray<uint32_t, 2>({rows, cols}, 0)),
m_sum(NDArray<SUM_TYPE, 2>({rows, cols})),
m_sum2(NDArray<SUM_TYPE, 2>({rows, cols})) {
assert(rows > 0 && cols > 0 && n_samples > 0);
m_sum = 0;
m_sum2 = 0;
}
~Pedestal() = default;
NDArray<SUM_TYPE, 2> mean() {
NDArray<SUM_TYPE, 2> mean_array({m_rows, m_cols});
for (uint32_t i = 0; i < m_rows * m_cols; i++) {
mean_array(i / m_cols, i % m_cols) = mean(i / m_cols, i % m_cols);
}
return mean_array;
}
SUM_TYPE mean(const uint32_t row, const uint32_t col) const {
if (m_cur_samples(row, col) == 0) {
return 0.0;
}
return m_sum(row, col) / m_cur_samples(row, col);
}
NDArray<SUM_TYPE, 2> variance() {
NDArray<SUM_TYPE, 2> variance_array({m_rows, m_cols});
for (uint32_t i = 0; i < m_rows * m_cols; i++) {
variance_array(i / m_cols, i % m_cols) =
variance(i / m_cols, i % m_cols);
}
return variance_array;
}
SUM_TYPE variance(const uint32_t row, const uint32_t col) const {
if (m_cur_samples(row, col) == 0) {
return 0.0;
}
return m_sum2(row, col) / m_cur_samples(row, col) -
mean(row, col) * mean(row, col);
}
NDArray<SUM_TYPE, 2> std() {
NDArray<SUM_TYPE, 2> standard_deviation_array({m_rows, m_cols});
for (uint32_t i = 0; i < m_rows * m_cols; i++) {
standard_deviation_array(i / m_cols, i % m_cols) =
std(i / m_cols, i % m_cols);
}
return standard_deviation_array;
}
SUM_TYPE std(const uint32_t row, const uint32_t col) const {
return std::sqrt(variance(row, col));
}
void clear() {
for (uint32_t i = 0; i < m_rows * m_cols; i++) {
clear(i / m_cols, i % m_cols);
}
}
void clear(const uint32_t row, const uint32_t col) {
m_sum(row, col) = 0;
m_sum2(row, col) = 0;
m_cur_samples(row, col) = 0;
}
// frame level operations
template <typename T> void push(NDView<T, 2> frame) {
assert(frame.size() == m_rows * m_cols);
// TODO! move away from m_rows, m_cols
if (frame.shape() != std::array<int64_t, 2>{m_rows, m_cols}) {
throw std::runtime_error(
"Frame shape does not match pedestal shape");
}
for (uint32_t row = 0; row < m_rows; row++) {
for (uint32_t col = 0; col < m_cols; col++) {
push<T>(row, col, frame(row, col));
}
}
// // TODO: test the effect of #pragma omp parallel for
// for (uint32_t index = 0; index < m_rows * m_cols; index++) {
// push<T>(index / m_cols, index % m_cols, frame(index));
// }
}
template <typename T> void push(Frame &frame) {
assert(frame.rows() == static_cast<size_t>(m_rows) &&
frame.cols() == static_cast<size_t>(m_cols));
push<T>(frame.view<T>());
}
// getter functions
uint32_t rows() const { return m_rows; }
uint32_t cols() const { return m_cols; }
uint32_t n_samples() const { return m_samples; }
NDArray<uint32_t, 2> cur_samples() const { return m_cur_samples; }
NDArray<SUM_TYPE, 2> get_sum() const { return m_sum; }
NDArray<SUM_TYPE, 2> get_sum2() const { return m_sum2; }
// pixel level operations (should be refactored to allow users to implement
// their own pixel level operations)
template <typename T>
void push(const uint32_t row, const uint32_t col, const T val_) {
SUM_TYPE val = static_cast<SUM_TYPE>(val_);
const uint32_t idx = index(row, col);
if (m_cur_samples(idx) < m_samples) {
m_sum(idx) += val;
m_sum2(idx) += val * val;
m_cur_samples(idx)++;
} else {
m_sum(idx) += val - m_sum(idx) / m_cur_samples(idx);
m_sum2(idx) += val * val - m_sum2(idx) / m_cur_samples(idx);
}
}
uint32_t index(const uint32_t row, const uint32_t col) const {
return row * m_cols + col;
};
};
} // namespace aare

15
include/aare/PixelMap.hpp Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include "aare/defs.hpp"
#include "aare/NDArray.hpp"
namespace aare {
NDArray<ssize_t, 2> GenerateMoench03PixelMap();
NDArray<ssize_t, 2> GenerateMoench05PixelMap();
NDArray<ssize_t, 2>GenerateMH02SingleCounterPixelMap();
NDArray<ssize_t, 3> GenerateMH02FourCounterPixelMap();
} // namespace aare

110
include/aare/RawFile.hpp Normal file
View File

@ -0,0 +1,110 @@
#pragma once
#include "aare/FileInterface.hpp"
#include "aare/RawMasterFile.hpp"
#include "aare/Frame.hpp"
#include "aare/NDArray.hpp" //for pixel map
#include "aare/SubFile.hpp"
#include <optional>
namespace aare {
struct ModuleConfig {
int module_gap_row{};
int module_gap_col{};
bool operator==(const ModuleConfig &other) const {
if (module_gap_col != other.module_gap_col)
return false;
if (module_gap_row != other.module_gap_row)
return false;
return true;
}
};
/**
* @brief Class to read .raw files. The class will parse the master file
* to find the correct geometry for the frames.
* @note A more generic interface is available in the aare::File class.
* Consider using that unless you need raw file specific functionality.
*/
class RawFile : public FileInterface {
size_t n_subfiles{};
size_t n_subfile_parts{};
std::vector<std::vector<SubFile *>> subfiles;
std::vector<xy> positions;
ModuleConfig cfg{0, 0};
RawMasterFile m_master;
size_t m_current_frame{};
size_t m_rows{};
size_t m_cols{};
public:
/**
* @brief RawFile constructor
* @param fname path to the master file (.json)
* @param mode file mode (only "r" is supported at the moment)
*/
RawFile(const std::filesystem::path &fname, const std::string &mode = "r");
virtual ~RawFile() override;
Frame read_frame() override;
Frame read_frame(size_t frame_number) override;
std::vector<Frame> read_n(size_t n_frames) override;
void read_into(std::byte *image_buf) override;
void read_into(std::byte *image_buf, size_t n_frames) override;
size_t frame_number(size_t frame_index) override;
size_t bytes_per_frame() override;
size_t pixels_per_frame() override;
void seek(size_t frame_index) override;
size_t tell() override;
size_t total_frames() const override;
size_t rows() const override;
size_t cols() const override;
size_t bitdepth() const override;
xy geometry();
DetectorType detector_type() const override;
private:
/**
* @brief check if the file is a master file
* @param fpath path to the file
*/
static bool is_master_file(const std::filesystem::path &fpath);
// TODO! Deal with fast quad and missing files
/**
* @brief read the frame at the given frame index into the image buffer
* @param frame_number frame number to read
* @param image_buf buffer to store the frame
*/
void get_frame_into(size_t frame_index, std::byte *frame_buffer);
/**
* @brief get the frame at the given frame index
* @param frame_number frame number to read
* @return Frame
*/
Frame get_frame(size_t frame_index);
/**
* @brief read the header of the file
* @param fname path to the data subfile
* @return DetectorHeader
*/
static DetectorHeader read_header(const std::filesystem::path &fname);
void find_number_of_subfiles();
void open_subfiles();
void find_geometry();
};
} // namespace aare

View File

@ -0,0 +1,131 @@
#pragma once
#include "aare/defs.hpp"
#include <filesystem>
#include <fmt/format.h>
#include <fstream>
#include <optional>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace aare {
/**
* @brief Implementation used in RawMasterFile to parse the file name
*/
class RawFileNameComponents {
std::filesystem::path m_base_path{};
std::string m_base_name{};
std::string m_ext{};
int m_file_index{}; // TODO! is this measurement_index?
public:
RawFileNameComponents(const std::filesystem::path &fname);
/// @brief Get the filename including path of the master file.
/// (i.e. what was passed in to the constructor))
std::filesystem::path master_fname() const;
/// @brief Get the filename including path of the data file.
/// @param mod_id module id run_d[module_id]_f0_0
/// @param file_id file id run_d0_f[file_id]_0
std::filesystem::path data_fname(size_t mod_id, size_t file_id) const;
const std::filesystem::path &base_path() const;
const std::string &base_name() const;
const std::string &ext() const;
int file_index() const;
};
class ScanParameters {
bool m_enabled = false;
std::string m_dac;
int m_start = 0;
int m_stop = 0;
int m_step = 0;
//TODO! add settleTime, requires string to time conversion
public:
ScanParameters(const std::string &par);
ScanParameters() = default;
ScanParameters(const ScanParameters &) = default;
ScanParameters &operator=(const ScanParameters &) = default;
ScanParameters(ScanParameters &&) = default;
int start() const;
int stop() const;
int step() const;
const std::string &dac() const;
bool enabled() const;
};
/**
* @brief Class for parsing a master file either in our .json format or the old
* .raw format
*/
class RawMasterFile {
RawFileNameComponents m_fnc;
std::string m_version;
DetectorType m_type;
TimingMode m_timing_mode;
size_t m_image_size_in_bytes{};
size_t m_frames_in_file{};
size_t m_total_frames_expected{};
size_t m_pixels_y{};
size_t m_pixels_x{};
size_t m_bitdepth{};
xy m_geometry;
size_t m_max_frames_per_file{};
uint32_t m_adc_mask{};
FrameDiscardPolicy m_frame_discard_policy{};
size_t m_frame_padding{};
// TODO! should these be bool?
uint8_t m_analog_flag{};
uint8_t m_digital_flag{};
uint8_t m_transceiver_flag{};
ScanParameters m_scan_parameters;
std::optional<size_t> m_analog_samples;
std::optional<size_t> m_digital_samples;
std::optional<size_t> m_transceiver_samples;
std::optional<size_t> m_number_of_rows;
std::optional<uint8_t> m_quad;
public:
RawMasterFile(const std::filesystem::path &fpath);
std::filesystem::path data_fname(size_t mod_id, size_t file_id) const;
const std::string &version() const; //!< For example "7.2"
const DetectorType &detector_type() const;
const TimingMode &timing_mode() const;
size_t image_size_in_bytes() const;
size_t frames_in_file() const;
size_t pixels_y() const;
size_t pixels_x() const;
size_t max_frames_per_file() const;
size_t bitdepth() const;
size_t frame_padding() const;
const FrameDiscardPolicy &frame_discard_policy() const;
size_t total_frames_expected() const;
xy geometry() const;
std::optional<size_t> analog_samples() const;
std::optional<size_t> digital_samples() const;
std::optional<size_t> transceiver_samples() const;
std::optional<size_t> number_of_rows() const;
std::optional<uint8_t> quad() const;
ScanParameters scan_parameters() const;
private:
void parse_json(const std::filesystem::path &fpath);
void parse_raw(const std::filesystem::path &fpath);
};
} // namespace aare

80
include/aare/SubFile.hpp Normal file
View File

@ -0,0 +1,80 @@
#pragma once
#include "aare/Frame.hpp"
#include "aare/defs.hpp"
#include <cstdint>
#include <filesystem>
#include <map>
#include <optional>
namespace aare {
/**
* @brief Class to read a subfile from a RawFile
*/
class SubFile {
public:
size_t write_part(std::byte *buffer, DetectorHeader header, size_t frame_index);
/**
* @brief SubFile constructor
* @param fname path to the subfile
* @param detector detector type
* @param rows number of rows in the subfile
* @param cols number of columns in the subfile
* @param bitdepth bitdepth of the subfile
* @throws std::invalid_argument if the detector,type pair is not supported
*/
SubFile(const std::filesystem::path &fname, DetectorType detector, size_t rows, size_t cols, size_t bitdepth,
const std::string &mode = "r");
/**
* @brief read the subfile into a buffer
* @param buffer pointer to the buffer to read the data into
* @return number of bytes read
*/
size_t read_impl_normal(std::byte *buffer);
/**
* @brief read the subfile into a buffer with the bytes flipped
* @param buffer pointer to the buffer to read the data into
* @return number of bytes read
*/
template <typename DataType> size_t read_impl_flip(std::byte *buffer);
/**
* @brief read the subfile into a buffer with the bytes reordered
* @param buffer pointer to the buffer to read the data into
* @return number of bytes read
*/
template <typename DataType> size_t read_impl_reorder(std::byte *buffer);
/**
* @brief read the subfile into a buffer with the bytes reordered and flipped
* @param buffer pointer to the buffer to read the data into
* @param frame_number frame number to read
* @return number of bytes read
*/
size_t get_part(std::byte *buffer, size_t frame_index);
size_t frame_number(size_t frame_index);
// TODO: define the inlines as variables and assign them in constructor
inline size_t bytes_per_part() const { return (m_bitdepth / 8) * m_rows * m_cols; }
inline size_t pixels_per_part() const { return m_rows * m_cols; }
~SubFile();
protected:
FILE *fp = nullptr;
size_t m_bitdepth;
std::filesystem::path m_fname;
size_t m_rows{};
size_t m_cols{};
std::string m_mode;
size_t n_frames{};
int m_sub_file_index_{};
DetectorType m_detector_type;
std::optional<NDArray<ssize_t, 2>> pixel_map;
};
} // namespace aare

View File

@ -0,0 +1,307 @@
#pragma once
#include <algorithm>
#include <map>
#include <unordered_map>
#include <vector>
#include "aare/NDArray.hpp"
const int MAX_CLUSTER_SIZE = 200;
namespace aare {
template <typename T> class VarClusterFinder {
public:
struct Hit {
int16_t size{};
int16_t row{};
int16_t col{};
uint16_t reserved{}; // for alignment
T energy{};
T max{};
// std::vector<int16_t> rows{};
// std::vector<int16_t> cols{};
int16_t rows[MAX_CLUSTER_SIZE] = {0};
int16_t cols[MAX_CLUSTER_SIZE] = {0};
double enes[MAX_CLUSTER_SIZE] = {0};
};
private:
const std::array<int64_t, 2> shape_;
NDView<T, 2> original_;
NDArray<int, 2> labeled_;
NDArray<int, 2> peripheral_labeled_;
NDArray<bool, 2> binary_; // over threshold flag
T threshold_;
NDView<T, 2> noiseMap;
bool use_noise_map = false;
int peripheralThresholdFactor_ = 5;
int current_label;
const std::array<int, 4> di{{0, -1, -1, -1}}; // row ### 8-neighbour by scaning from left to right
const std::array<int, 4> dj{{-1, -1, 0, 1}}; // col ### 8-neighbour by scaning from top to bottom
const std::array<int, 8> di_{{0, 0, -1, 1, -1, 1, -1, 1}}; // row
const std::array<int, 8> dj_{{-1, 1, 0, 0, 1, -1, -1, 1}}; // col
std::map<int, int> child; // heirachy: key: child; val: parent
std::unordered_map<int, Hit> h_size;
std::vector<Hit> hits;
// std::vector<std::vector<int16_t>> row
int check_neighbours(int i, int j);
public:
VarClusterFinder(Shape<2> shape, T threshold)
: shape_(shape), labeled_(shape, 0), peripheral_labeled_(shape, 0), binary_(shape), threshold_(threshold) {
hits.reserve(2000);
}
NDArray<int, 2> labeled() { return labeled_; }
void set_noiseMap(NDView<T, 2> noise_map) {
noiseMap = noise_map;
use_noise_map = true;
}
void set_peripheralThresholdFactor(int factor) { peripheralThresholdFactor_ = factor; }
void find_clusters(NDView<T, 2> img);
void find_clusters_X(NDView<T, 2> img);
void rec_FillHit(int clusterIndex, int i, int j);
void single_pass(NDView<T, 2> img);
void first_pass();
void second_pass();
void store_clusters();
std::vector<Hit> steal_hits() {
std::vector<Hit> tmp;
std::swap(tmp, hits);
return tmp;
};
void clear_hits() { hits.clear(); };
void print_connections() {
fmt::print("Connections:\n");
for (auto it = child.begin(); it != child.end(); ++it) {
fmt::print("{} -> {}\n", it->first, it->second);
}
}
size_t total_clusters() const {
// TODO! fix for stealing
return hits.size();
}
private:
void add_link(int from, int to) {
// we want to add key from -> value to
// fmt::print("add_link({},{})\n", from, to);
auto it = child.find(from);
if (it == child.end()) {
child[from] = to;
} else {
// found need to disambiguate
if (it->second == to)
return;
else {
if (it->second > to) {
// child[from] = to;
auto old = it->second;
it->second = to;
add_link(old, to);
} else {
// found value is smaller than what we want to link
add_link(to, it->second);
}
}
}
}
};
template <typename T> int VarClusterFinder<T>::check_neighbours(int i, int j) {
std::vector<int> neighbour_labels;
for (int k = 0; k < 4; ++k) {
const auto row = i + di[k];
const auto col = j + dj[k];
if (row >= 0 && col >= 0 && row < shape_[0] && col < shape_[1]) {
auto tmp = labeled_.value(i + di[k], j + dj[k]);
if (tmp != 0)
neighbour_labels.push_back(tmp);
}
}
if (neighbour_labels.size() == 0) {
return 0;
} else {
// need to sort and add to union field
std::sort(neighbour_labels.rbegin(), neighbour_labels.rend());
auto first = neighbour_labels.begin();
auto last = std::unique(first, neighbour_labels.end());
if (last - first == 1)
return *neighbour_labels.begin();
for (auto current = first; current != last - 1; ++current) {
auto next = current + 1;
add_link(*current, *next);
}
return neighbour_labels.back(); // already sorted
}
}
template <typename T> void VarClusterFinder<T>::find_clusters(NDView<T, 2> img) {
original_ = img;
labeled_ = 0;
peripheral_labeled_ = 0;
current_label = 0;
child.clear();
first_pass();
// print_connections();
second_pass();
store_clusters();
}
template <typename T> void VarClusterFinder<T>::find_clusters_X(NDView<T, 2> img) {
original_ = img;
int clusterIndex = 0;
for (int i = 0; i < shape_[0]; ++i) {
for (int j = 0; j < shape_[1]; ++j) {
if (use_noise_map)
threshold_ = 5 * noiseMap(i, j);
if (original_(i, j) > threshold_) {
// printf("========== Cluster index: %d\n", clusterIndex);
rec_FillHit(clusterIndex, i, j);
clusterIndex++;
}
}
}
for (const auto &h : h_size)
hits.push_back(h.second);
h_size.clear();
}
template <typename T> void VarClusterFinder<T>::rec_FillHit(int clusterIndex, int i, int j) {
// printf("original_(%d, %d)=%f\n", i, j, original_(i,j));
// printf("h_size[%d].size=%d\n", clusterIndex, h_size[clusterIndex].size);
if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE) {
h_size[clusterIndex].rows[h_size[clusterIndex].size] = i;
h_size[clusterIndex].cols[h_size[clusterIndex].size] = j;
h_size[clusterIndex].enes[h_size[clusterIndex].size] = original_(i, j);
}
h_size[clusterIndex].size += 1;
h_size[clusterIndex].energy += original_(i, j);
if (h_size[clusterIndex].max < original_(i, j)) {
h_size[clusterIndex].row = i;
h_size[clusterIndex].col = j;
h_size[clusterIndex].max = original_(i, j);
}
original_(i, j) = 0;
for (int k = 0; k < 8; ++k) { // 8 for 8-neighbour
const auto row = i + di_[k];
const auto col = j + dj_[k];
if (row >= 0 && col >= 0 && row < shape_[0] && col < shape_[1]) {
if (use_noise_map)
threshold_ = peripheralThresholdFactor_ * noiseMap(row, col);
if (original_(row, col) > threshold_) {
rec_FillHit(clusterIndex, row, col);
} else {
// if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE){
// h_size[clusterIndex].size += 1;
// h_size[clusterIndex].rows[h_size[clusterIndex].size] = row;
// h_size[clusterIndex].cols[h_size[clusterIndex].size] = col;
// h_size[clusterIndex].enes[h_size[clusterIndex].size] = original_(row, col);
// }// ? weather to include peripheral pixels
original_(row, col) = 0; // remove peripheral pixels, to avoid potential influence for pedestal updating
}
}
}
}
template <typename T> void VarClusterFinder<T>::single_pass(NDView<T, 2> img) {
original_ = img;
labeled_ = 0;
current_label = 0;
child.clear();
first_pass();
// print_connections();
// second_pass();
// store_clusters();
}
template <typename T> void VarClusterFinder<T>::first_pass() {
for (int i = 0; i < original_.size(); ++i) {
if (use_noise_map)
threshold_ = 5 * noiseMap(i);
binary_(i) = (original_(i) > threshold_);
}
for (int i = 0; i < shape_[0]; ++i) {
for (int j = 0; j < shape_[1]; ++j) {
// do we have someting to process?
if (binary_(i, j)) {
auto tmp = check_neighbours(i, j);
if (tmp != 0) {
labeled_(i, j) = tmp;
} else {
labeled_(i, j) = ++current_label;
}
}
}
}
}
template <typename T> void VarClusterFinder<T>::second_pass() {
for (int64_t i = 0; i != labeled_.size(); ++i) {
auto current_label = labeled_(i);
if (current_label != 0) {
auto it = child.find(current_label);
while (it != child.end()) {
current_label = it->second;
it = child.find(current_label);
// do this once before doing the second pass?
// all values point to the final one...
}
labeled_(i) = current_label;
}
}
}
template <typename T> void VarClusterFinder<T>::store_clusters() {
// Accumulate hit information in a map
// Do we always have monotonic increasing
// labels? Then vector?
// here the translation is label -> Hit
std::unordered_map<int, Hit> h_size;
for (int i = 0; i < shape_[0]; ++i) {
for (int j = 0; j < shape_[1]; ++j) {
if (labeled_(i, j) != 0 || false
// (i-1 >= 0 and labeled_(i-1, j) != 0) or // another circle of peripheral pixels
// (j-1 >= 0 and labeled_(i, j-1) != 0) or
// (i+1 < shape_[0] and labeled_(i+1, j) != 0) or
// (j+1 < shape_[1] and labeled_(i, j+1) != 0)
) {
Hit &record = h_size[labeled_(i, j)];
if (record.size < MAX_CLUSTER_SIZE) {
record.rows[record.size] = i;
record.cols[record.size] = j;
record.enes[record.size] = original_(i, j);
} else {
continue;
}
record.size += 1;
record.energy += original_(i, j);
if (record.max < original_(i, j)) {
record.row = i;
record.col = j;
record.max = original_(i, j);
}
}
}
}
for (const auto &h : h_size)
hits.push_back(h.second);
}
} // namespace aare

197
include/aare/defs.hpp Normal file
View File

@ -0,0 +1,197 @@
#pragma once
#include "aare/Dtype.hpp"
// #include "aare/utils/logger.hpp"
#include <array>
#include <stdexcept>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
/**
* @brief LOCATION macro to get the current location in the code
*/
#define LOCATION \
std::string(__FILE__) + std::string(":") + std::to_string(__LINE__) + \
":" + std::string(__func__) + ":"
namespace aare {
class Cluster {
public:
int cluster_sizeX;
int cluster_sizeY;
int16_t x;
int16_t y;
Dtype dt;
private:
std::byte *m_data;
public:
Cluster(int cluster_sizeX_, int cluster_sizeY_,
Dtype dt_ = Dtype(typeid(int32_t)))
: cluster_sizeX(cluster_sizeX_), cluster_sizeY(cluster_sizeY_),
dt(dt_) {
m_data = new std::byte[cluster_sizeX * cluster_sizeY * dt.bytes()]{};
}
Cluster() : Cluster(3, 3) {}
Cluster(const Cluster &other)
: Cluster(other.cluster_sizeX, other.cluster_sizeY, other.dt) {
if (this == &other)
return;
x = other.x;
y = other.y;
memcpy(m_data, other.m_data, other.bytes());
}
Cluster &operator=(const Cluster &other) {
if (this == &other)
return *this;
this->~Cluster();
new (this) Cluster(other);
return *this;
}
Cluster(Cluster &&other) noexcept
: cluster_sizeX(other.cluster_sizeX),
cluster_sizeY(other.cluster_sizeY), x(other.x), y(other.y),
dt(other.dt), m_data(other.m_data) {
other.m_data = nullptr;
other.dt = Dtype(Dtype::TypeIndex::ERROR);
}
~Cluster() { delete[] m_data; }
template <typename T> T get(int idx) {
(sizeof(T) == dt.bytes())
? 0
: throw std::invalid_argument("[ERROR] Type size mismatch");
return *reinterpret_cast<T *>(m_data + idx * dt.bytes());
}
template <typename T> auto set(int idx, T val) {
(sizeof(T) == dt.bytes())
? 0
: throw std::invalid_argument("[ERROR] Type size mismatch");
return memcpy(m_data + idx * dt.bytes(), &val, (size_t)dt.bytes());
}
// auto x() const { return x; }
// auto y() const { return y; }
// auto x(int16_t x_) { return x = x_; }
// auto y(int16_t y_) { return y = y_; }
template <typename T> std::string to_string() const {
(sizeof(T) == dt.bytes())
? 0
: throw std::invalid_argument("[ERROR] Type size mismatch");
std::string s = "x: " + std::to_string(x) + " y: " + std::to_string(y) +
"\nm_data: [";
for (int i = 0; i < cluster_sizeX * cluster_sizeY; i++) {
s += std::to_string(
*reinterpret_cast<T *>(m_data + i * dt.bytes())) +
" ";
}
s += "]";
return s;
}
/**
* @brief size of the cluster in bytes when saved to a file
*/
size_t size() const { return cluster_sizeX * cluster_sizeY; }
size_t bytes() const { return cluster_sizeX * cluster_sizeY * dt.bytes(); }
auto begin() const { return m_data; }
auto end() const {
return m_data + cluster_sizeX * cluster_sizeY * dt.bytes();
}
std::byte *data() { return m_data; }
};
/**
* @brief header contained in parts of frames
*/
struct DetectorHeader {
uint64_t frameNumber;
uint32_t expLength;
uint32_t packetNumber;
uint64_t bunchId;
uint64_t timestamp;
uint16_t modId;
uint16_t row;
uint16_t column;
uint16_t reserved;
uint32_t debug;
uint16_t roundRNumber;
uint8_t detType;
uint8_t version;
std::array<uint8_t, 64> packetMask;
std::string to_string() {
std::string packetMaskStr = "[";
for (auto &i : packetMask) {
packetMaskStr += std::to_string(i) + ", ";
}
packetMaskStr += "]";
return "frameNumber: " + std::to_string(frameNumber) + "\n" +
"expLength: " + std::to_string(expLength) + "\n" +
"packetNumber: " + std::to_string(packetNumber) + "\n" +
"bunchId: " + std::to_string(bunchId) + "\n" +
"timestamp: " + std::to_string(timestamp) + "\n" +
"modId: " + std::to_string(modId) + "\n" +
"row: " + std::to_string(row) + "\n" +
"column: " + std::to_string(column) + "\n" +
"reserved: " + std::to_string(reserved) + "\n" +
"debug: " + std::to_string(debug) + "\n" +
"roundRNumber: " + std::to_string(roundRNumber) + "\n" +
"detType: " + std::to_string(detType) + "\n" +
"version: " + std::to_string(version) + "\n" +
"packetMask: " + packetMaskStr + "\n";
}
};
template <typename T> struct t_xy {
T row;
T col;
bool operator==(const t_xy &other) const {
return row == other.row && col == other.col;
}
bool operator!=(const t_xy &other) const { return !(*this == other); }
std::string to_string() const {
return "{ x: " + std::to_string(row) + " y: " + std::to_string(col) +
" }";
}
};
using xy = t_xy<uint32_t>;
using dynamic_shape = std::vector<int64_t>;
//TODO! Can we uniform enums between the libraries?
enum class DetectorType {
Jungfrau,
Eiger,
Mythen3,
Moench,
Moench03,
Moench03_old,
ChipTestBoard,
Unknown
};
enum class TimingMode { Auto, Trigger };
enum class FrameDiscardPolicy { NoDiscard, Discard, DiscardPartial };
template <class T> T StringTo(const std::string &arg) { return T(arg); }
template <class T> std::string ToString(T arg) { return T(arg); }
template <> DetectorType StringTo(const std::string & /*name*/);
template <> std::string ToString(DetectorType arg);
template <> TimingMode StringTo(const std::string & /*mode*/);
template <> FrameDiscardPolicy StringTo(const std::string & /*mode*/);
using DataTypeVariants = std::variant<uint16_t, uint32_t>;
} // namespace aare