Dev/multiple rois in aare (#263)
Some checks failed
Build on RHEL8 / build (push) Successful in 2m23s
Build on RHEL9 / build (push) Successful in 2m32s
Run tests using data on local RHEL8 / build (push) Failing after 3m14s

Reading multiple ROI's for aare 

- read_frame, read_n etc throws for multiple ROIs
- new functions read_ROIs, read_n_ROIs 
-  read_roi_into (used for python bindings - to not copy) 

all these functions use get_frame or get_frame_into where one passes the
roi_index
## Refactoring:
- each roi keeps track of its subfiles that one has to open e.g.
subfiles can be opened several times
- refactored class DetectorGeometry - keep track of the updated module
geometries in new class ROIGeometry.
- ModuleGeometry updates based on ROI

## ROIGeometry: 
- stores number of modules overlapping with ROI and its indices
- size of ROI 

Note: only tested size of the resulting frames not the actual values

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
Co-authored-by: Erik Fröjdh <erik.frojdh@gmail.com>
This commit is contained in:
2026-02-18 10:57:56 +01:00
committed by GitHub
parent 7f64b9a616
commit 218f31ce60
17 changed files with 1242 additions and 421 deletions

View File

@@ -1,7 +1,10 @@
// SPDX-License-Identifier: MPL-2.0
#pragma once
#include "aare/ROIGeometry.hpp"
#include "aare/RawMasterFile.hpp" //ROI refactor away
#include "aare/defs.hpp"
#include <iostream>
namespace aare {
struct ModuleConfig {
@@ -18,16 +21,69 @@ struct ModuleConfig {
};
/**
* @brief Class to hold the geometry of a module. Where pixel 0 is located and
* the size of the module
* @brief Class to hold the geometry of a module. Where pixel 0 is located
* relative to full Detector/ROI and the size of the module e.g. size of ROI
* occupied by the module
*/
struct ModuleGeometry {
/// @brief start x coordinate of the module in the full detector/ROI
int origin_x{};
/// @brief start y coordinate of the module in the full detector/ROI
int origin_y{};
/// @brief height of the module in pixels
int height{};
/// @brief width of the module in pixels
int width{};
/// @brief module index of the module in the detector
int row_index{};
/// @brief module index of the module in the detector
int col_index{};
/**
* @brief checks if module is in the region of interest
*/
bool module_in_roi(const ROI &roi) {
// module is to the left of the roi
bool to_the_left = origin_x + width - 1 < roi.xmin;
bool to_the_right = origin_x >= roi.xmax;
bool above = origin_y + height - 1 < roi.ymin;
bool below = origin_y >= roi.ymax;
return !(to_the_left || to_the_right || below || above);
}
/**
* @brief Update the module geometry given a region of interest
*/
void update_geometry_with_roi(const ROI &roi) {
if (!(module_in_roi(roi))) {
return; // do nothing
}
int new_width = std::min(origin_x + width, static_cast<int>(roi.xmax)) -
std::max(origin_x, static_cast<int>(roi.xmin));
// ROI starts inside module
if (roi.xmin >= origin_x && roi.xmin < origin_x + width) {
origin_x = 0;
} else {
origin_x -= roi.xmin;
}
int new_height =
std::min(origin_y + height, static_cast<int>(roi.ymax)) -
std::max(origin_y, static_cast<int>(roi.ymin));
if (roi.ymin >= origin_y && roi.ymin < origin_y + height) {
origin_y = 0;
} else {
origin_y -= roi.ymin;
}
height = new_height;
width = new_width;
}
};
/**
@@ -49,34 +105,33 @@ class DetectorGeometry {
* @param roi
* @return DetectorGeometry
*/
void update_geometry_with_roi(ROI roi);
size_t n_modules() const;
size_t n_modules_in_roi() const;
size_t pixels_x() const;
size_t pixels_y() const;
size_t modules_x() const;
size_t modules_y() const;
const std::vector<ssize_t> &get_modules_in_roi() const;
ssize_t get_modules_in_roi(const size_t index) const;
const std::vector<ModuleGeometry> &get_module_geometries() const;
const ModuleGeometry &get_module_geometries(const size_t index) const;
ModuleGeometry &get_module_geometries(const size_t index);
private:
size_t m_modules_x{};
size_t m_modules_y{};
size_t m_pixels_x{};
size_t m_pixels_y{};
static constexpr ModuleConfig cfg{0, 0};
std::vector<ModuleGeometry> module_geometries{};
std::vector<ssize_t> modules_in_roi{};
// TODO: maybe remove - should be a member in ROIGeometry - in particular
// only works as no overlap between ROIs allowed? maybe just have another
// additional vector of ModuleGeometry for each ROIGeometry - some
// duplication but nicer design?
std::vector<ModuleGeometry> module_geometries;
};
} // namespace aare

View File

@@ -0,0 +1,48 @@
#pragma once
#include "aare/DetectorGeometry.hpp"
#include "aare/defs.hpp"
namespace aare {
class DetectorGeometry; // forward declaration to avoid circular dependency
/**
* @brief Class to hold the geometry of a region of interest (ROI)
*/
class ROIGeometry {
public:
/** @brief Constructor
* @param roi
* @param geometry general detector geometry
*/
ROIGeometry(const ROI &roi, DetectorGeometry &geometry);
/** @brief Constructor for ROI geometry expanding over full detector
* @param geometry general detector geometry
*/
ROIGeometry(DetectorGeometry &geometry);
/// @brief Get number of modules in the ROI
size_t num_modules_in_roi() const;
/// @brief Get the indices of modules in the ROI
const std::vector<size_t> &module_indices_in_roi() const;
size_t module_indices_in_roi(const size_t module_index) const;
size_t pixels_x() const;
size_t pixels_y() const;
private:
/// @brief Size of the ROI in pixels in x direction
size_t m_pixels_x{};
/// @brief Size of the ROI in pixels in y direction
size_t m_pixels_y{};
DetectorGeometry &m_geometry;
/// @brief Indices of modules included in the ROI
std::vector<size_t> m_module_indices_in_roi;
};
} // namespace aare

View File

@@ -4,6 +4,7 @@
#include "aare/FileInterface.hpp"
#include "aare/Frame.hpp"
#include "aare/NDArray.hpp" //for pixel map
#include "aare/ROIGeometry.hpp"
#include "aare/RawMasterFile.hpp"
#include "aare/RawSubFile.hpp"
@@ -23,13 +24,17 @@ namespace aare {
*/
class RawFile : public FileInterface {
std::vector<std::unique_ptr<RawSubFile>> m_subfiles;
std::vector<std::vector<std::unique_ptr<RawSubFile>>>
m_subfiles; // [ROI][modules_per_ROI]
RawMasterFile m_master;
size_t m_current_frame{};
DetectorGeometry m_geometry;
/// @brief Geometries e.g. number of modules, size etc. for each ROI
std::vector<ROIGeometry> m_ROI_geometries;
public:
/**
* @brief RawFile constructor
@@ -43,26 +48,94 @@ class RawFile : public FileInterface {
Frame read_frame() override;
Frame read_frame(size_t frame_number) override;
std::vector<Frame> read_n(size_t n_frames) override;
/**
* @brief Read one ROI defined in the master file
* @param roi_index index of the ROI to read
* @return Frame
* @note the frame index is incremented after calling this function so
* reading rois one after the other wont work.
*/
Frame read_roi(const size_t roi_index);
/**
* @brief Read all ROIs defined in the master file
* @return vector of Frames (one Frame per ROI)
*/
std::vector<Frame>read_rois();
/**
* @brief Read n frames for the given ROI index
* @param n_frames number of frames to read
* @param roi_index index of the ROI to read
* @return vector of Frames
*/
std::vector<Frame> read_n_with_roi(const size_t n_frames,
const size_t roi_index);
void read_into(std::byte *image_buf) override;
void read_into(std::byte *image_buf, size_t n_frames) override;
// TODO! do we need to adapt the API?
void read_into(std::byte *image_buf, DetectorHeader *header);
void read_into(std::byte *image_buf, DetectorHeader *header = nullptr);
void read_into(std::byte *image_buf, size_t n_frames,
DetectorHeader *header);
void read_roi_into(std::byte *image_buf, const size_t roi_index,
const size_t frame_number,
DetectorHeader *header =
nullptr); // maybe just make get_frame_into public
size_t frame_number(size_t frame_index) override;
size_t bytes_per_frame() override;
// TODO: mmh maybe also pass roi_index in Base class File. Leave it unused
// for NumpyFile and JungfrauDataFile
/**
* @brief bytes per frame for the given ROI
* @param roi_index index of the ROI
*/
size_t bytes_per_frame(const size_t roi_index);
size_t pixels_per_frame() override;
/**
* @brief pixels per frame for the given ROI
* @param roi_index index of the ROI
*/
size_t pixels_per_frame(const size_t roi_index);
size_t bytes_per_pixel() const;
void seek(size_t frame_index) override;
size_t tell() override;
size_t total_frames() const override;
size_t rows() const override;
/**
* @brief rows for the given ROI
* @param roi_index index of the ROI
*/
size_t rows(const size_t roi_index) const;
size_t cols() const override;
/**
* @brief cols for the given ROI
* @param roi_index index of the ROI
*/
size_t cols(const size_t roi_index) const;
size_t bitdepth() const override;
size_t n_modules() const;
size_t n_modules_in_roi() const;
/**
* @brief number of ROIs defined
*/
size_t num_rois() const;
/**
* @brief get the ROI geometry for the given ROI index
* @param roi_index index of the ROI
*/
const ROIGeometry &roi_geometries(size_t roi_index) const;
/**
* @brief number of modules in each ROI
*/
std::vector<size_t> n_modules_in_roi() const;
xy geometry() const;
RawMasterFile master() const;
@@ -79,21 +152,23 @@ class RawFile : public FileInterface {
private:
/**
* @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
* @param frame_index frame number to read
* @param frame_buffer buffer to store the frame
* @param roi_index index of the ROI to read (default is 0 e.g. full frame)
*/
void get_frame_into(size_t frame_index, std::byte *frame_buffer,
DetectorHeader *header = nullptr);
void get_frame_into(
size_t frame_index, std::byte *frame_buffer, const size_t roi_index = 0,
DetectorHeader *header = nullptr); // TODO read_into updates it!!!!
/**
* @brief get the frame at the given frame index
* @param frame_number frame number to read
* @param roi_index index of the ROI to read (default is 0 e.g. full frame)
* @return Frame
*/
Frame get_frame(size_t frame_index);
Frame get_frame(size_t frame_index, const size_t roi_index = 0);
void open_subfiles();
void open_subfiles(const size_t roi_index);
};
} // namespace aare

View File

@@ -2,11 +2,11 @@
#pragma once
#include "aare/defs.hpp"
#include <algorithm>
#include <chrono>
#include <filesystem>
#include <fmt/format.h>
#include <fstream>
#include <optional>
#include <chrono>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
@@ -109,7 +109,7 @@ class RawMasterFile {
std::optional<size_t> m_number_of_rows;
std::optional<uint8_t> m_counter_mask;
std::optional<ROI> m_roi;
std::optional<std::vector<ROI>> m_rois;
public:
RawMasterFile(const std::filesystem::path &fpath);
@@ -141,11 +141,15 @@ class RawMasterFile {
std::optional<size_t> number_of_rows() const;
std::optional<uint8_t> counter_mask() const;
std::optional<std::vector<ROI>> rois() const;
std::optional<ROI> roi() const;
ScanParameters scan_parameters() const;
std::optional<std::chrono::nanoseconds> exptime() const { return m_exptime; }
std::optional<std::chrono::nanoseconds> exptime() const {
return m_exptime;
}
std::chrono::nanoseconds period() const { return m_period; }
private: