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

@@ -352,6 +352,7 @@ set(PUBLICHEADERS
include/aare/FilePtr.hpp include/aare/FilePtr.hpp
include/aare/Frame.hpp include/aare/Frame.hpp
include/aare/GainMap.hpp include/aare/GainMap.hpp
include/aare/ROIGeometry.hpp
include/aare/DetectorGeometry.hpp include/aare/DetectorGeometry.hpp
include/aare/JungfrauDataFile.hpp include/aare/JungfrauDataFile.hpp
include/aare/logger.hpp include/aare/logger.hpp
@@ -374,6 +375,7 @@ set(SourceFiles
${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
${CMAKE_CURRENT_SOURCE_DIR}/src/ROIGeometry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/File.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/File.cpp

View File

@@ -12,6 +12,10 @@
- Removed redundant arr.value(ix,iy...) on NDArray use arr(ix,iy...) - Removed redundant arr.value(ix,iy...) on NDArray use arr(ix,iy...)
- Removed Print/Print_some/Print_all form NDArray (operator << still works) - Removed Print/Print_some/Print_all form NDArray (operator << still works)
- Added const* version of .data() - Added const* version of .data()
- reading multiple ROI's supported for aare.
- Use ``read_roi/rois`` to read multiple ROIs for one frame, the index of a specific ROI to only read that ROI
- Use ``read_n_with_roi`` to read multiple frames for a specific ROI.
- Note ``read_frame`` and ``read_n`` is not supported for multiple ROI's.
### Bugfixes: ### Bugfixes:

View File

@@ -1,7 +1,10 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
#pragma once #pragma once
#include "aare/ROIGeometry.hpp"
#include "aare/RawMasterFile.hpp" //ROI refactor away #include "aare/RawMasterFile.hpp" //ROI refactor away
#include "aare/defs.hpp" #include "aare/defs.hpp"
#include <iostream>
namespace aare { namespace aare {
struct ModuleConfig { struct ModuleConfig {
@@ -18,16 +21,69 @@ struct ModuleConfig {
}; };
/** /**
* @brief Class to hold the geometry of a module. Where pixel 0 is located and * @brief Class to hold the geometry of a module. Where pixel 0 is located
* the size of the module * relative to full Detector/ROI and the size of the module e.g. size of ROI
* occupied by the module
*/ */
struct ModuleGeometry { struct ModuleGeometry {
/// @brief start x coordinate of the module in the full detector/ROI
int origin_x{}; int origin_x{};
/// @brief start y coordinate of the module in the full detector/ROI
int origin_y{}; int origin_y{};
/// @brief height of the module in pixels
int height{}; int height{};
/// @brief width of the module in pixels
int width{}; int width{};
/// @brief module index of the module in the detector
int row_index{}; int row_index{};
/// @brief module index of the module in the detector
int col_index{}; 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 * @param roi
* @return DetectorGeometry * @return DetectorGeometry
*/ */
void update_geometry_with_roi(ROI roi);
size_t n_modules() const; size_t n_modules() const;
size_t n_modules_in_roi() const;
size_t pixels_x() const; size_t pixels_x() const;
size_t pixels_y() const; size_t pixels_y() const;
size_t modules_x() const; size_t modules_x() const;
size_t modules_y() 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 std::vector<ModuleGeometry> &get_module_geometries() const;
const ModuleGeometry &get_module_geometries(const size_t index) const; const ModuleGeometry &get_module_geometries(const size_t index) const;
ModuleGeometry &get_module_geometries(const size_t index);
private: private:
size_t m_modules_x{}; size_t m_modules_x{};
size_t m_modules_y{}; size_t m_modules_y{};
size_t m_pixels_x{}; size_t m_pixels_x{};
size_t m_pixels_y{}; size_t m_pixels_y{};
static constexpr ModuleConfig cfg{0, 0}; 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 } // 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/FileInterface.hpp"
#include "aare/Frame.hpp" #include "aare/Frame.hpp"
#include "aare/NDArray.hpp" //for pixel map #include "aare/NDArray.hpp" //for pixel map
#include "aare/ROIGeometry.hpp"
#include "aare/RawMasterFile.hpp" #include "aare/RawMasterFile.hpp"
#include "aare/RawSubFile.hpp" #include "aare/RawSubFile.hpp"
@@ -23,13 +24,17 @@ namespace aare {
*/ */
class RawFile : public FileInterface { 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; RawMasterFile m_master;
size_t m_current_frame{}; size_t m_current_frame{};
DetectorGeometry m_geometry; DetectorGeometry m_geometry;
/// @brief Geometries e.g. number of modules, size etc. for each ROI
std::vector<ROIGeometry> m_ROI_geometries;
public: public:
/** /**
* @brief RawFile constructor * @brief RawFile constructor
@@ -43,26 +48,94 @@ class RawFile : public FileInterface {
Frame read_frame() override; Frame read_frame() override;
Frame read_frame(size_t frame_number) override; Frame read_frame(size_t frame_number) override;
std::vector<Frame> read_n(size_t n_frames) 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) override;
void read_into(std::byte *image_buf, size_t n_frames) override; void read_into(std::byte *image_buf, size_t n_frames) override;
// TODO! do we need to adapt the API? // 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, void read_into(std::byte *image_buf, size_t n_frames,
DetectorHeader *header); 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 frame_number(size_t frame_index) override;
size_t bytes_per_frame() 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; 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; size_t bytes_per_pixel() const;
void seek(size_t frame_index) override; void seek(size_t frame_index) override;
size_t tell() override; size_t tell() override;
size_t total_frames() const override; size_t total_frames() const override;
size_t rows() 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; 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 bitdepth() const override;
size_t n_modules() const; 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; xy geometry() const;
RawMasterFile master() const; RawMasterFile master() const;
@@ -79,21 +152,23 @@ class RawFile : public FileInterface {
private: private:
/** /**
* @brief read the frame at the given frame index into the image buffer * @brief read the frame at the given frame index into the image buffer
* @param frame_number frame number to read * @param frame_index frame number to read
* @param image_buf buffer to store the frame * @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(
void get_frame_into(size_t frame_index, std::byte *frame_buffer, size_t frame_index, std::byte *frame_buffer, const size_t roi_index = 0,
DetectorHeader *header = nullptr); DetectorHeader *header = nullptr); // TODO read_into updates it!!!!
/** /**
* @brief get the frame at the given frame index * @brief get the frame at the given frame index
* @param frame_number frame number to read * @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 * @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 } // namespace aare

View File

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

311
python/src/bind_RawFile.hpp Normal file
View File

@@ -0,0 +1,311 @@
// SPDX-License-Identifier: MPL-2.0
#include "aare/CtbRawFile.hpp"
#include "aare/File.hpp"
#include "aare/Frame.hpp"
#include "aare/RawFile.hpp"
#include "aare/RawMasterFile.hpp"
#include "aare/RawSubFile.hpp"
#include "aare/defs.hpp"
#include "np_helper.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/iostream.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl/filesystem.h>
#include <string>
namespace py = pybind11;
using namespace ::aare;
void define_raw_file_io_bindings(py::module &m) {
py::class_<RawFile>(m, "RawFile")
.def(py::init<const std::filesystem::path &>())
.def("read_frame",
[](RawFile &self) {
if (self.n_modules_in_roi().size() > 1) {
throw std::runtime_error(
"File contains multiple ROIs - use read_ROIs()");
}
std::vector<size_t> shape;
shape.reserve(2);
shape.push_back(self.rows());
shape.push_back(self.cols());
// return headers from all subfiles
py::array_t<DetectorHeader> header(self.n_modules());
py::array image = allocate_image_data(self.bytes_per_pixel(), shape);
self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data()),
header.mutable_data());
return py::make_tuple(header, image);
})
.def(
"read_n",
[](RawFile &self, size_t n_frames) {
if (self.n_modules_in_roi().size() > 1) {
throw std::runtime_error(
"File contains multiple ROIs - use read_n_ROIs() to "
"read a specific ROI or use read_ROIs and "
"read one frame at a time.");
}
// adjust for actual frames left in the file
n_frames =
std::min(n_frames, self.total_frames() - self.tell());
if (n_frames == 0) {
throw std::runtime_error("No frames left in file");
}
std::vector<size_t> shape{n_frames, self.rows(), self.cols()};
// return headers from all subfiles
py::array_t<DetectorHeader> header;
if (self.n_modules() == 1) {
header = py::array_t<DetectorHeader>(n_frames);
} else {
header = py::array_t<DetectorHeader>(
{self.n_modules_in_roi()[0], n_frames});
}
py::array images = allocate_image_data(self.bytes_per_pixel(), shape);
self.read_into(
reinterpret_cast<std::byte *>(images.mutable_data()),
n_frames, header.mutable_data());
return py::make_tuple(header, images);
},
R"(
Read n frames from the file.
)")
.def(
"read_roi",
[](RawFile &self,
const size_t roi_index) {
if (self.num_rois() == 0) {
throw std::runtime_error(LOCATION + "No ROIs defined.");
}
if ( roi_index >= self.num_rois()) {
throw std::runtime_error(LOCATION +
"ROI index out of range.");
}
// return headers from all subfiles
py::array_t<DetectorHeader> header(self.n_modules_in_roi()[roi_index]);
std::vector<size_t> shape;
shape.reserve(2);
shape.push_back(self.roi_geometries(roi_index).pixels_y());
shape.push_back(self.roi_geometries(roi_index).pixels_x());
py::array image = allocate_image_data(self.bytes_per_pixel(), shape);
self.read_roi_into(
reinterpret_cast<std::byte *>(image.mutable_data()),
roi_index, self.tell(), header.mutable_data());
self.seek(self.tell() + 1); // advance frame number so the
return py::make_tuple(header, image);
},
R"(
Read one ROI from the current frame.
Parameters
----------
roi_index : int
Index of the ROI to read.
Notes
-----
The method advances the frame number, so reading ROIs one after the other won't work.
Returns
-------
tuple (header, image)
)",
py::arg("roi_index"))
.def(
"read_rois",
[](RawFile &self) {
if (self.num_rois() == 0) {
throw std::runtime_error(LOCATION + "No ROIs defined.");
}
size_t number_of_ROIs = self.num_rois();
// const uint8_t item_size = self.bytes_per_pixel();
std::vector<py::array> images(number_of_ROIs);
// return headers from all subfiles
std::vector<py::array_t<DetectorHeader>> headers(number_of_ROIs);
for (size_t r = 0; r < number_of_ROIs; r++) {
headers[r] =
py::array_t<DetectorHeader>(self.n_modules_in_roi()[r]);
}
for (size_t r = 0; r < number_of_ROIs; r++) {
std::vector<size_t> shape;
shape.reserve(2);
shape.push_back(self.roi_geometries(r).pixels_y());
shape.push_back(self.roi_geometries(r).pixels_x());
images[r] = allocate_image_data(self.bytes_per_pixel(), shape);
self.read_roi_into(
reinterpret_cast<std::byte *>(images[r].mutable_data()),
r, self.tell(),headers[r].mutable_data());
}
self.seek(self.tell() + 1); // advance frame number so the
return py::make_tuple(headers, images);
},
R"(
Read all ROIs for specific frame.
Parameters
----------
frame_number : int
Frame number to read.
roi_index : Optional[int]
Index of the ROI to read. If not provided, all ROIs are read.
Returns
-------
list of numpy.ndarray
One array per ROI.)")
.def(
"read_n_with_roi",
[](RawFile &self, const size_t num_frames, const size_t roi_index) {
if (self.num_rois() == 0) {
throw std::runtime_error(LOCATION + "No ROIs defined.");
}
if (roi_index >= self.num_rois()) {
throw std::runtime_error(LOCATION +
"ROI index out of range.");
}
// adjust for actual frames left in the file
size_t n_frames =
std::min(num_frames, self.total_frames() - self.tell());
if (n_frames == 0) {
throw std::runtime_error("No frames left in file");
}
std::vector<size_t> shape{
n_frames, self.roi_geometries(roi_index).pixels_y(),
self.roi_geometries(roi_index).pixels_x()};
// return headers from all subfiles
auto n_mod = self.n_modules_in_roi()[roi_index];
py::array_t<DetectorHeader> header({
n_frames, n_mod}
);
py::array images = allocate_image_data(self.bytes_per_pixel(), shape);
auto image_buffer =
reinterpret_cast<std::byte *>(images.mutable_data());
auto h = header.mutable_data();
for (size_t i = 0; i < n_frames; i++) {
self.read_roi_into(image_buffer, roi_index, self.tell(), h);
self.seek(self.tell() + 1); // advance frame number
image_buffer += self.bytes_per_frame(roi_index);
h += n_mod;
}
return py::make_tuple(header, images);
},
R"(
Read n frames for a specific ROI
Parameters
----------
num_frames : int
Number of frames to read.
roi_index : int
Index of the ROI to read.
Returns
-------
three dimensional numpy.ndarray.)",
py::arg("num_frames"), py::kw_only(), py::arg("roi_index"))
.def("frame_number", &RawFile::frame_number)
.def("bytes_per_frame",
static_cast<size_t (RawFile::*)()>(&RawFile::bytes_per_frame))
.def(
"bytes_per_frame",
[](RawFile &self, const size_t roi_index) {
return self.bytes_per_frame(roi_index);
},
R"(
Bytes per frame for the given ROI.
)")
.def("pixels_per_frame",
static_cast<size_t (RawFile::*)()>(&RawFile::pixels_per_frame))
.def(
"pixels_per_frame",
[](RawFile &self, const size_t roi_index) {
return self.pixels_per_frame(roi_index);
},
R"(
Pixels per frame for the given ROI.
)")
.def_property_readonly("bytes_per_pixel", &RawFile::bytes_per_pixel)
.def("seek", &RawFile::seek, R"(
Seek to a frame index in file.
)")
.def("tell", &RawFile::tell, R"(
Return the current frame number.)")
.def_property_readonly("total_frames", &RawFile::total_frames)
.def("rows", static_cast<size_t (RawFile::*)() const>(&RawFile::rows))
.def(
"rows",
[](RawFile &self, const size_t roi_index) {
return self.rows(roi_index);
},
R"(
Rows for the given ROI.
)")
.def("cols", static_cast<size_t (RawFile::*)() const>(&RawFile::cols))
.def(
"cols",
[](RawFile &self, const size_t roi_index) {
return self.cols(roi_index);
},
R"(
Cols for the given ROI.
)")
.def_property_readonly("bitdepth", &RawFile::bitdepth)
.def_property_readonly("geometry", &RawFile::geometry)
.def_property_readonly("detector_type", &RawFile::detector_type)
.def_property_readonly("master", &RawFile::master)
.def_property_readonly("n_modules", &RawFile::n_modules)
.def_property_readonly("n_modules_in_roi", &RawFile::n_modules_in_roi)
.def_property_readonly("num_rois", &RawFile::num_rois);
}

View File

@@ -11,6 +11,7 @@
#include "bind_ClusterVector.hpp" #include "bind_ClusterVector.hpp"
#include "bind_Eta.hpp" #include "bind_Eta.hpp"
#include "bind_Interpolator.hpp" #include "bind_Interpolator.hpp"
#include "bind_RawFile.hpp"
#include "bind_calibration.hpp" #include "bind_calibration.hpp"
// TODO! migrate the other names // TODO! migrate the other names
@@ -20,7 +21,6 @@
#include "jungfrau_data_file.hpp" #include "jungfrau_data_file.hpp"
#include "pedestal.hpp" #include "pedestal.hpp"
#include "pixel_map.hpp" #include "pixel_map.hpp"
#include "raw_file.hpp"
#include "raw_master_file.hpp" #include "raw_master_file.hpp"
#include "raw_sub_file.hpp" #include "raw_sub_file.hpp"
#include "var_cluster.hpp" #include "var_cluster.hpp"

View File

@@ -85,3 +85,20 @@ struct fmt_format_trait<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> {
template <typename ClusterType> template <typename ClusterType>
auto fmt_format = fmt_format_trait<ClusterType>::value(); auto fmt_format = fmt_format_trait<ClusterType>::value();
/**
* Helper function to allocate image data given item size and shape
* used when we want to fill a numpy array and return to python
*/
py::array allocate_image_data(size_t item_size,
const std::vector<size_t> &shape) {
py::array image_data;
if (item_size == 1) {
image_data = py::array_t<uint8_t>(shape);
} else if (item_size == 2) {
image_data = py::array_t<uint16_t>(shape);
} else if (item_size == 4) {
image_data = py::array_t<uint32_t>(shape);
}
return image_data;
}

View File

@@ -1,109 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
#include "aare/CtbRawFile.hpp"
#include "aare/File.hpp"
#include "aare/Frame.hpp"
#include "aare/RawFile.hpp"
#include "aare/RawMasterFile.hpp"
#include "aare/RawSubFile.hpp"
#include "aare/defs.hpp"
// #include "aare/fClusterFileV2.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/iostream.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl/filesystem.h>
#include <string>
namespace py = pybind11;
using namespace ::aare;
void define_raw_file_io_bindings(py::module &m) {
py::class_<RawFile>(m, "RawFile")
.def(py::init<const std::filesystem::path &>())
.def("read_frame",
[](RawFile &self) {
py::array image;
std::vector<ssize_t> shape;
shape.reserve(2);
shape.push_back(self.rows());
shape.push_back(self.cols());
// return headers from all subfiles
py::array_t<DetectorHeader> header(self.n_modules());
const uint8_t item_size = self.bytes_per_pixel();
if (item_size == 1) {
image = py::array_t<uint8_t>(shape);
} else if (item_size == 2) {
image = py::array_t<uint16_t>(shape);
} else if (item_size == 4) {
image = py::array_t<uint32_t>(shape);
}
self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data()),
header.mutable_data());
return py::make_tuple(header, image);
})
.def(
"read_n",
[](RawFile &self, size_t n_frames) {
// adjust for actual frames left in the file
n_frames =
std::min(n_frames, self.total_frames() - self.tell());
if (n_frames == 0) {
throw std::runtime_error("No frames left in file");
}
std::vector<size_t> shape{n_frames, self.rows(), self.cols()};
// return headers from all subfiles
py::array_t<DetectorHeader> header;
if (self.n_modules() == 1) {
header = py::array_t<DetectorHeader>(n_frames);
} else {
header = py::array_t<DetectorHeader>(
{self.n_modules_in_roi(), n_frames});
}
// py::array_t<DetectorHeader> header({self.n_mod(), n_frames});
py::array image;
const uint8_t item_size = self.bytes_per_pixel();
if (item_size == 1) {
image = py::array_t<uint8_t>(shape);
} else if (item_size == 2) {
image = py::array_t<uint16_t>(shape);
} else if (item_size == 4) {
image = py::array_t<uint32_t>(shape);
}
self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data()),
n_frames, header.mutable_data());
return py::make_tuple(header, image);
},
R"(
Read n frames from the file.
)")
.def("frame_number", &RawFile::frame_number)
.def_property_readonly("bytes_per_frame", &RawFile::bytes_per_frame)
.def_property_readonly("pixels_per_frame", &RawFile::pixels_per_frame)
.def_property_readonly("bytes_per_pixel", &RawFile::bytes_per_pixel)
.def("seek", &RawFile::seek, R"(
Seek to a frame index in file.
)")
.def("tell", &RawFile::tell, R"(
Return the current frame number.)")
.def_property_readonly("total_frames", &RawFile::total_frames)
.def_property_readonly("rows", &RawFile::rows)
.def_property_readonly("cols", &RawFile::cols)
.def_property_readonly("bitdepth", &RawFile::bitdepth)
.def_property_readonly("geometry", &RawFile::geometry)
.def_property_readonly("detector_type", &RawFile::detector_type)
.def_property_readonly("master", &RawFile::master)
.def_property_readonly("n_modules", &RawFile::n_modules)
.def_property_readonly("n_modules_in_roi", &RawFile::n_modules_in_roi);
}

View File

@@ -4,13 +4,45 @@ from aare import RawFile
import numpy as np import numpy as np
@pytest.mark.withdata @pytest.mark.withdata
def test_read_rawfile_with_roi(test_data_path): def test_read_rawfile_with_roi_spanning_over_one_module(test_data_path):
with RawFile(test_data_path / "raw/ROITestData/SingleChipROI/Data_master_0.json") as f: with RawFile(test_data_path / "raw/ROITestData/SingleChipROI/Data_master_0.json") as f:
headers, frames = f.read() headers, frames = f.read()
assert headers.size == 10100 num_rois = f.num_rois
assert frames.shape == (10100, 256, 256) assert num_rois == 1
assert headers.size == 10100
assert frames.shape == (10100, 256, 256)
@pytest.mark.withdata
def test_read_rawfile_with_multiple_rois(test_data_path):
with RawFile(test_data_path / "raw/ROITestData/MultipleROIs/run_master_0.json") as f:
num_rois = f.num_rois
#cannot read_frame for multiple ROIs
with pytest.raises(RuntimeError):
f.read_frame()
assert f.tell() == 0
frames = f.read_ROIs()
assert num_rois == 2
assert len(frames) == 2
assert frames[0].shape == (301, 101)
assert frames[1].shape == (101, 101)
assert f.tell() == 1
# read multiple ROIs at once
frames = f.read_n_ROIs(2, 1)
assert frames.shape == (2, 101, 101)
assert f.tell() == 3
# read specific ROI
frame = f.read_ROIs(1, 0)
assert len(frame) == 1
assert frame[0].shape == (301, 101)
assert f.tell() == 2
@pytest.mark.withdata @pytest.mark.withdata
def test_read_rawfile_quad_eiger_and_compare_to_numpy(test_data_path): def test_read_rawfile_quad_eiger_and_compare_to_numpy(test_data_path):

View File

@@ -41,31 +41,16 @@ DetectorGeometry::DetectorGeometry(const xy &geometry,
m_modules_x = geometry.col; m_modules_x = geometry.col;
m_modules_y = geometry.row; m_modules_y = geometry.row;
m_pixels_y += static_cast<size_t>((geometry.row - 1) * cfg.module_gap_row); m_pixels_y += static_cast<size_t>((geometry.row - 1) * cfg.module_gap_row);
modules_in_roi.resize(num_modules);
std::iota(modules_in_roi.begin(), modules_in_roi.end(), 0);
} }
size_t DetectorGeometry::n_modules() const { return m_modules_x * m_modules_y; } size_t DetectorGeometry::n_modules() const { return m_modules_x * m_modules_y; }
size_t DetectorGeometry::n_modules_in_roi() const {
return modules_in_roi.size();
};
size_t DetectorGeometry::pixels_x() const { return m_pixels_x; } size_t DetectorGeometry::pixels_x() const { return m_pixels_x; }
size_t DetectorGeometry::pixels_y() const { return m_pixels_y; } size_t DetectorGeometry::pixels_y() const { return m_pixels_y; }
size_t DetectorGeometry::modules_x() const { return m_modules_x; }; size_t DetectorGeometry::modules_x() const { return m_modules_x; };
size_t DetectorGeometry::modules_y() const { return m_modules_y; }; size_t DetectorGeometry::modules_y() const { return m_modules_y; };
const std::vector<ssize_t> &DetectorGeometry::get_modules_in_roi() const {
return modules_in_roi;
}
ssize_t DetectorGeometry::get_modules_in_roi(const size_t index) const {
return modules_in_roi[index];
}
const std::vector<ModuleGeometry> & const std::vector<ModuleGeometry> &
DetectorGeometry::get_module_geometries() const { DetectorGeometry::get_module_geometries() const {
return module_geometries; return module_geometries;
@@ -76,77 +61,8 @@ DetectorGeometry::get_module_geometries(const size_t index) const {
return module_geometries[index]; return module_geometries[index];
} }
void DetectorGeometry::update_geometry_with_roi(ROI roi) { ModuleGeometry &DetectorGeometry::get_module_geometries(const size_t index) {
#ifdef AARE_VERBOSE return module_geometries[index];
fmt::println("update_geometry_with_roi() called with ROI: {} {} {} {}",
roi.xmin, roi.xmax, roi.ymin, roi.ymax);
fmt::println("Geometry: {} {} {} {} {} {}", m_modules_x, m_modules_y,
m_pixels_x, m_pixels_y, cfg.module_gap_row,
cfg.module_gap_col);
#endif
modules_in_roi.clear();
modules_in_roi.reserve(m_modules_x * m_modules_y);
int pos_y = 0;
int pos_y_increment = 0;
for (size_t row = 0; row < m_modules_y; row++) {
int pos_x = 0;
for (size_t col = 0; col < m_modules_x; col++) {
auto &m = module_geometries[row * m_modules_x + col];
auto original_height = m.height;
auto original_width = m.width;
// module is to the left of the roi
if (m.origin_x + m.width < roi.xmin) {
m.width = 0;
// roi is in module
} else {
// here we only arrive when the roi is in or to the left of
// the module
if (roi.xmin > m.origin_x) {
m.width -= roi.xmin - m.origin_x;
}
if (roi.xmax < m.origin_x + original_width) {
m.width -= m.origin_x + original_width - roi.xmax;
}
m.origin_x = pos_x;
pos_x += m.width;
}
if (m.origin_y + m.height < roi.ymin) {
m.height = 0;
} else {
if ((roi.ymin > m.origin_y) &&
(roi.ymin < m.origin_y + m.height)) {
m.height -= roi.ymin - m.origin_y;
}
if (roi.ymax < m.origin_y + original_height) {
m.height -= m.origin_y + original_height - roi.ymax;
}
m.origin_y = pos_y;
pos_y_increment = m.height;
}
if (m.height != 0 && m.width != 0) {
modules_in_roi.push_back(row * m_modules_x + col);
}
#ifdef AARE_VERBOSE
fmt::println("Module {} {} {} {}", m.origin_x, m.origin_y, m.width,
m.height);
#endif
}
// increment pos_y
pos_y += pos_y_increment;
}
// m_rows = roi.height();
// m_cols = roi.width();
m_pixels_x = roi.width();
m_pixels_y = roi.height();
} }
} // namespace aare } // namespace aare

View File

@@ -7,17 +7,20 @@
#include <filesystem> #include <filesystem>
#include "aare/DetectorGeometry.hpp" #include "aare/DetectorGeometry.hpp"
#include "aare/ROIGeometry.hpp"
#include "test_config.hpp" #include "test_config.hpp"
TEST_CASE("Simple ROIs on one module") { TEST_CASE("Simple ROIs on one module", "[DetectorGeometry]") {
// DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI
// roi)
aare::DetectorGeometry geo(aare::xy{1, 1}, 1024, 512); aare::DetectorGeometry geo(aare::xy{1, 1}, 1024, 512);
REQUIRE(geo.get_module_geometries(0).origin_x == 0); REQUIRE(geo.get_module_geometries(0).origin_x == 0);
REQUIRE(geo.get_module_geometries(0).origin_y == 0); REQUIRE(geo.get_module_geometries(0).origin_y == 0);
REQUIRE(geo.get_module_geometries(0).width == 1024); REQUIRE(geo.get_module_geometries(0).width == 1024);
REQUIRE(geo.get_module_geometries(0).height == 512); REQUIRE(geo.get_module_geometries(0).height == 512);
REQUIRE(geo.modules_x() == 1);
REQUIRE(geo.modules_y() == 1);
REQUIRE(geo.pixels_x() == 1024);
REQUIRE(geo.pixels_y() == 512);
SECTION("ROI is the whole module") { SECTION("ROI is the whole module") {
aare::ROI roi; aare::ROI roi;
@@ -25,14 +28,18 @@ TEST_CASE("Simple ROIs on one module") {
roi.xmax = 1024; roi.xmax = 1024;
roi.ymin = 0; roi.ymin = 0;
roi.ymax = 512; roi.ymax = 512;
geo.update_geometry_with_roi(roi);
REQUIRE(geo.pixels_x() == 1024); aare::ROIGeometry roi_geometry(roi, geo);
REQUIRE(geo.pixels_y() == 512);
REQUIRE(geo.modules_x() == 1); REQUIRE(roi_geometry.pixels_x() == 1024);
REQUIRE(geo.modules_y() == 1); REQUIRE(roi_geometry.pixels_y() == 512);
REQUIRE(geo.get_module_geometries(0).height == 512); REQUIRE(roi_geometry.num_modules_in_roi() == 1);
REQUIRE(geo.get_module_geometries(0).width == 1024); auto module_geometry =
geo.get_module_geometries(roi_geometry.module_indices_in_roi(0));
REQUIRE(module_geometry.height == 512);
REQUIRE(module_geometry.width == 1024);
REQUIRE(module_geometry.origin_x == 0);
REQUIRE(module_geometry.origin_y == 0);
} }
SECTION("ROI is the top left corner of the module") { SECTION("ROI is the top left corner of the module") {
aare::ROI roi; aare::ROI roi;
@@ -40,51 +47,22 @@ TEST_CASE("Simple ROIs on one module") {
roi.xmax = 200; roi.xmax = 200;
roi.ymin = 150; roi.ymin = 150;
roi.ymax = 200; roi.ymax = 200;
geo.update_geometry_with_roi(roi);
REQUIRE(geo.pixels_x() == 100); aare::ROIGeometry roi_geometry(roi, geo);
REQUIRE(geo.pixels_y() == 50);
REQUIRE(geo.modules_x() == 1);
REQUIRE(geo.modules_y() == 1);
REQUIRE(geo.get_module_geometries(0).height == 50);
REQUIRE(geo.get_module_geometries(0).width == 100);
}
SECTION("ROI is a small square") { REQUIRE(roi_geometry.pixels_x() == 100);
aare::ROI roi; REQUIRE(roi_geometry.pixels_y() == 50);
roi.xmin = 1000; REQUIRE(roi_geometry.num_modules_in_roi() == 1);
roi.xmax = 1010; auto module_geometry =
roi.ymin = 500; geo.get_module_geometries(roi_geometry.module_indices_in_roi(0));
roi.ymax = 510; REQUIRE(module_geometry.height == 50);
geo.update_geometry_with_roi(roi); REQUIRE(module_geometry.width == 100);
REQUIRE(module_geometry.origin_x == 0);
REQUIRE(geo.pixels_x() == 10); REQUIRE(module_geometry.origin_y == 0);
REQUIRE(geo.pixels_y() == 10);
REQUIRE(geo.modules_x() == 1);
REQUIRE(geo.modules_y() == 1);
REQUIRE(geo.get_module_geometries(0).height == 10);
REQUIRE(geo.get_module_geometries(0).width == 10);
}
SECTION("ROI is a few columns") {
aare::ROI roi;
roi.xmin = 750;
roi.xmax = 800;
roi.ymin = 0;
roi.ymax = 512;
geo.update_geometry_with_roi(roi);
REQUIRE(geo.pixels_x() == 50);
REQUIRE(geo.pixels_y() == 512);
REQUIRE(geo.modules_x() == 1);
REQUIRE(geo.modules_y() == 1);
REQUIRE(geo.get_module_geometries(0).height == 512);
REQUIRE(geo.get_module_geometries(0).width == 50);
} }
} }
TEST_CASE("Two modules side by side") { TEST_CASE("Two modules side by side", "[DetectorGeometry]") {
// DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI
// roi)
aare::DetectorGeometry geo(aare::xy{1, 2}, 1024, 512); aare::DetectorGeometry geo(aare::xy{1, 2}, 1024, 512);
REQUIRE(geo.get_module_geometries(0).origin_x == 0); REQUIRE(geo.get_module_geometries(0).origin_x == 0);
@@ -93,6 +71,10 @@ TEST_CASE("Two modules side by side") {
REQUIRE(geo.get_module_geometries(0).height == 512); REQUIRE(geo.get_module_geometries(0).height == 512);
REQUIRE(geo.get_module_geometries(1).origin_x == 1024); REQUIRE(geo.get_module_geometries(1).origin_x == 1024);
REQUIRE(geo.get_module_geometries(1).origin_y == 0); REQUIRE(geo.get_module_geometries(1).origin_y == 0);
REQUIRE(geo.modules_x() == 2);
REQUIRE(geo.modules_y() == 1);
REQUIRE(geo.pixels_x() == 2048);
REQUIRE(geo.pixels_y() == 512);
SECTION("ROI is the whole image") { SECTION("ROI is the whole image") {
aare::ROI roi; aare::ROI roi;
@@ -100,12 +82,24 @@ TEST_CASE("Two modules side by side") {
roi.xmax = 2048; roi.xmax = 2048;
roi.ymin = 0; roi.ymin = 0;
roi.ymax = 512; roi.ymax = 512;
geo.update_geometry_with_roi(roi);
REQUIRE(geo.pixels_x() == 2048); aare::ROIGeometry roi_geometry(roi, geo);
REQUIRE(geo.pixels_y() == 512);
REQUIRE(geo.modules_x() == 2); REQUIRE(roi_geometry.pixels_x() == 2048);
REQUIRE(geo.modules_y() == 1); REQUIRE(roi_geometry.pixels_y() == 512);
REQUIRE(roi_geometry.num_modules_in_roi() == 2);
auto module_geometry_0 = geo.get_module_geometries(0);
auto module_geometry_1 = geo.get_module_geometries(1);
REQUIRE(module_geometry_0.height == 512);
REQUIRE(module_geometry_0.width == 1024);
REQUIRE(module_geometry_1.height == 512);
REQUIRE(module_geometry_1.width == 1024);
REQUIRE(module_geometry_0.origin_x == 0);
REQUIRE(module_geometry_1.origin_x == 1024);
REQUIRE(module_geometry_0.origin_y == 0);
REQUIRE(module_geometry_1.origin_y == 0);
} }
SECTION("rectangle on both modules") { SECTION("rectangle on both modules") {
aare::ROI roi; aare::ROI roi;
@@ -113,28 +107,30 @@ TEST_CASE("Two modules side by side") {
roi.xmax = 1300; roi.xmax = 1300;
roi.ymin = 200; roi.ymin = 200;
roi.ymax = 499; roi.ymax = 499;
geo.update_geometry_with_roi(roi);
REQUIRE(geo.pixels_x() == 500); aare::ROIGeometry roi_geometry(roi, geo);
REQUIRE(geo.pixels_y() == 299);
REQUIRE(geo.modules_x() == 2); REQUIRE(roi_geometry.pixels_x() == 500);
REQUIRE(geo.modules_y() == 1); REQUIRE(roi_geometry.pixels_y() == 299);
REQUIRE(geo.get_module_geometries(0).height == 299); REQUIRE(roi_geometry.num_modules_in_roi() == 2);
REQUIRE(geo.get_module_geometries(0).width == 224);
REQUIRE(geo.get_module_geometries(1).height == 299); auto module_geometry_0 = geo.get_module_geometries(0);
REQUIRE(geo.get_module_geometries(1).width == 276); auto module_geometry_1 = geo.get_module_geometries(1);
REQUIRE(module_geometry_0.height == 299);
REQUIRE(module_geometry_0.width == 224);
REQUIRE(module_geometry_1.height == 299);
REQUIRE(module_geometry_1.width == 276);
REQUIRE(module_geometry_0.origin_x == 0);
REQUIRE(module_geometry_1.origin_x == 224);
REQUIRE(module_geometry_0.origin_y == 0);
REQUIRE(module_geometry_1.origin_y == 0);
} }
} }
TEST_CASE("Three modules side by side") { TEST_CASE("Three modules side by side", "[DetectorGeometry]") {
// DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI
// roi)
aare::DetectorGeometry geo(aare::xy{1, 3}, 1024, 512); aare::DetectorGeometry geo(aare::xy{1, 3}, 1024, 512);
aare::ROI roi;
roi.xmin = 700;
roi.xmax = 2500;
roi.ymin = 0;
roi.ymax = 123;
REQUIRE(geo.get_module_geometries(0).origin_x == 0); REQUIRE(geo.get_module_geometries(0).origin_x == 0);
REQUIRE(geo.get_module_geometries(0).origin_y == 0); REQUIRE(geo.get_module_geometries(0).origin_y == 0);
@@ -142,24 +138,78 @@ TEST_CASE("Three modules side by side") {
REQUIRE(geo.get_module_geometries(0).height == 512); REQUIRE(geo.get_module_geometries(0).height == 512);
REQUIRE(geo.get_module_geometries(1).origin_x == 1024); REQUIRE(geo.get_module_geometries(1).origin_x == 1024);
REQUIRE(geo.get_module_geometries(2).origin_x == 2048); REQUIRE(geo.get_module_geometries(2).origin_x == 2048);
geo.update_geometry_with_roi(roi);
REQUIRE(geo.pixels_x() == 1800);
REQUIRE(geo.pixels_y() == 123);
REQUIRE(geo.modules_x() == 3); REQUIRE(geo.modules_x() == 3);
REQUIRE(geo.modules_y() == 1); REQUIRE(geo.modules_y() == 1);
REQUIRE(geo.get_module_geometries(0).height == 123); REQUIRE(geo.pixels_x() == 3072);
REQUIRE(geo.get_module_geometries(0).width == 324); REQUIRE(geo.pixels_y() == 512);
REQUIRE(geo.get_module_geometries(1).height == 123);
REQUIRE(geo.get_module_geometries(1).width == 1024); SECTION("ROI overlapping all three modules") {
REQUIRE(geo.get_module_geometries(2).height == 123); aare::ROI roi;
REQUIRE(geo.get_module_geometries(2).width == 452); roi.xmin = 500;
roi.xmax = 2500;
roi.ymin = 0;
roi.ymax = 123;
aare::ROIGeometry roi_geometry(roi, geo);
REQUIRE(roi_geometry.pixels_x() == 2000);
REQUIRE(roi_geometry.pixels_y() == 123);
REQUIRE(roi_geometry.num_modules_in_roi() == 3);
auto module_geometry_0 = geo.get_module_geometries(0);
auto module_geometry_1 = geo.get_module_geometries(1);
auto module_geometry_2 = geo.get_module_geometries(2);
REQUIRE(module_geometry_0.height == 123);
REQUIRE(module_geometry_0.width == 524);
REQUIRE(module_geometry_0.origin_x == 0);
REQUIRE(module_geometry_0.origin_y == 0);
REQUIRE(module_geometry_1.height == 123);
REQUIRE(module_geometry_1.width == 1024);
REQUIRE(module_geometry_1.origin_x == 524);
REQUIRE(module_geometry_1.origin_y == 0);
REQUIRE(module_geometry_2.height == 123);
REQUIRE(module_geometry_2.width == 452);
REQUIRE(module_geometry_2.origin_x == 1548);
REQUIRE(module_geometry_2.origin_y == 0);
}
SECTION("ROI overlapping last two modules") {
aare::ROI roi;
roi.xmin = 1050;
roi.xmax = 2500;
roi.ymin = 0;
roi.ymax = 123;
aare::ROIGeometry roi_geometry(roi, geo);
REQUIRE(roi_geometry.pixels_x() == 1450);
REQUIRE(roi_geometry.pixels_y() == 123);
REQUIRE(roi_geometry.num_modules_in_roi() == 2);
auto module_geometry_0 = geo.get_module_geometries(0);
auto module_geometry_1 = geo.get_module_geometries(1);
auto module_geometry_2 = geo.get_module_geometries(2);
REQUIRE(module_geometry_0.height == 512);
REQUIRE(module_geometry_0.width == 1024);
REQUIRE(module_geometry_0.origin_x == 0);
REQUIRE(module_geometry_0.origin_y == 0);
REQUIRE(module_geometry_1.height == 123);
REQUIRE(module_geometry_1.width == 998);
REQUIRE(module_geometry_1.origin_x == 0);
REQUIRE(module_geometry_1.origin_y == 0);
REQUIRE(module_geometry_2.height == 123);
REQUIRE(module_geometry_2.width == 452);
REQUIRE(module_geometry_2.origin_x == 998);
REQUIRE(module_geometry_2.origin_y == 0);
}
} }
TEST_CASE("Four modules as a square") { TEST_CASE("Four modules as a square", "[DetectorGeometry]") {
// DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI
// roi)
aare::DetectorGeometry geo(aare::xy{2, 2}, 1024, 512, aare::xy{1, 2}); aare::DetectorGeometry geo(aare::xy{2, 2}, 1024, 512, aare::xy{1, 2});
aare::ROI roi; aare::ROI roi;
roi.xmin = 500; roi.xmin = 500;
@@ -177,19 +227,99 @@ TEST_CASE("Four modules as a square") {
REQUIRE(geo.get_module_geometries(2).origin_y == 512); REQUIRE(geo.get_module_geometries(2).origin_y == 512);
REQUIRE(geo.get_module_geometries(3).origin_x == 1024); REQUIRE(geo.get_module_geometries(3).origin_x == 1024);
REQUIRE(geo.get_module_geometries(3).origin_y == 512); REQUIRE(geo.get_module_geometries(3).origin_y == 512);
geo.update_geometry_with_roi(roi);
REQUIRE(geo.pixels_x() == 1500);
REQUIRE(geo.pixels_y() == 100);
REQUIRE(geo.modules_x() == 2); REQUIRE(geo.modules_x() == 2);
REQUIRE(geo.modules_y() == 2); REQUIRE(geo.modules_y() == 2);
REQUIRE(geo.pixels_x() == 2048);
REQUIRE(geo.pixels_y() == 1024);
aare::ROIGeometry roi_geometry(roi, geo);
REQUIRE(roi_geometry.pixels_x() == 1500);
REQUIRE(roi_geometry.pixels_y() == 100);
REQUIRE(roi_geometry.num_modules_in_roi() == 4);
REQUIRE(geo.get_module_geometries(0).height == 12); REQUIRE(geo.get_module_geometries(0).height == 12);
REQUIRE(geo.get_module_geometries(0).width == 524); REQUIRE(geo.get_module_geometries(0).width == 524);
REQUIRE(geo.get_module_geometries(0).origin_x == 0);
REQUIRE(geo.get_module_geometries(0).origin_y == 0);
REQUIRE(geo.get_module_geometries(1).height == 12); REQUIRE(geo.get_module_geometries(1).height == 12);
REQUIRE(geo.get_module_geometries(1).width == 976); REQUIRE(geo.get_module_geometries(1).width == 976);
REQUIRE(geo.get_module_geometries(1).origin_x == 524);
REQUIRE(geo.get_module_geometries(1).origin_y == 0);
REQUIRE(geo.get_module_geometries(2).height == 88); REQUIRE(geo.get_module_geometries(2).height == 88);
REQUIRE(geo.get_module_geometries(2).width == 524); REQUIRE(geo.get_module_geometries(2).width == 524);
REQUIRE(geo.get_module_geometries(2).origin_x == 0);
REQUIRE(geo.get_module_geometries(2).origin_y == 12);
REQUIRE(geo.get_module_geometries(3).height == 88); REQUIRE(geo.get_module_geometries(3).height == 88);
REQUIRE(geo.get_module_geometries(3).width == 976); REQUIRE(geo.get_module_geometries(3).width == 976);
REQUIRE(geo.get_module_geometries(3).origin_x == 524);
REQUIRE(geo.get_module_geometries(3).origin_y == 12);
}
TEST_CASE("check if module in ROI", "[DetectorGeometry]") {
aare::DetectorGeometry geo(aare::xy{2, 2}, 1024, 512, aare::xy{1, 2});
SECTION("all modules in ROI") {
aare::ROI roi;
roi.xmin = 500;
roi.xmax = 2000;
roi.ymin = 500;
roi.ymax = 600;
REQUIRE(geo.get_module_geometries(0).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(1).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(2).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(3).module_in_roi(roi) == true);
}
SECTION("some modules on border of ROI") {
aare::ROI roi;
roi.xmin = 1024;
roi.xmax = 2000;
roi.ymin = 500;
roi.ymax = 600;
REQUIRE(geo.get_module_geometries(0).module_in_roi(roi) == false);
REQUIRE(geo.get_module_geometries(1).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(2).module_in_roi(roi) == false);
REQUIRE(geo.get_module_geometries(3).module_in_roi(roi) == true);
}
SECTION("one module on border of ROI") {
aare::ROI roi;
roi.xmin = 1025;
roi.xmax = 2000;
roi.ymin = 513;
roi.ymax = 600;
REQUIRE(geo.get_module_geometries(0).module_in_roi(roi) == false);
REQUIRE(geo.get_module_geometries(1).module_in_roi(roi) == false);
REQUIRE(geo.get_module_geometries(2).module_in_roi(roi) == false);
REQUIRE(geo.get_module_geometries(3).module_in_roi(roi) == true);
}
SECTION("some modules to the left of ROI") {
aare::ROI roi;
roi.xmin = 500;
roi.xmax = 1024;
roi.ymin = 500;
roi.ymax = 600;
REQUIRE(geo.get_module_geometries(0).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(1).module_in_roi(roi) == false);
REQUIRE(geo.get_module_geometries(2).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(3).module_in_roi(roi) == false);
}
SECTION("all modules in ROI") {
aare::ROI roi;
roi.xmin = 500;
roi.xmax = 2000;
roi.ymin = 10;
roi.ymax = 513;
REQUIRE(geo.get_module_geometries(0).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(1).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(2).module_in_roi(roi) == true);
REQUIRE(geo.get_module_geometries(3).module_in_roi(roi) == true);
}
} }

42
src/ROIGeometry.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include "aare/ROIGeometry.hpp"
#include <vector>
namespace aare {
ROIGeometry::ROIGeometry(const ROI &roi, DetectorGeometry &geometry)
: m_pixels_x(roi.width()), m_pixels_y(roi.height()), m_geometry(geometry) {
m_module_indices_in_roi.reserve(m_geometry.n_modules());
// determine which modules are in the roi
for (size_t i = 0; i < m_geometry.n_modules(); ++i) {
auto &module_geometry = m_geometry.get_module_geometries(i);
if (module_geometry.module_in_roi(roi)) {
module_geometry.update_geometry_with_roi(roi);
m_module_indices_in_roi.push_back(i);
}
}
};
ROIGeometry::ROIGeometry(DetectorGeometry &geometry)
: m_pixels_x(geometry.pixels_x()), m_pixels_y(geometry.pixels_y()),
m_geometry(geometry) {
m_module_indices_in_roi.resize(m_geometry.n_modules());
std::iota(m_module_indices_in_roi.begin(), m_module_indices_in_roi.end(),
0);
};
size_t ROIGeometry::num_modules_in_roi() const {
return m_module_indices_in_roi.size();
}
const std::vector<size_t> &ROIGeometry::module_indices_in_roi() const {
return m_module_indices_in_roi;
}
size_t ROIGeometry::module_indices_in_roi(const size_t module_index) const {
return m_module_indices_in_roi.at(module_index);
}
size_t ROIGeometry::pixels_x() const { return m_pixels_x; }
size_t ROIGeometry::pixels_y() const { return m_pixels_y; }
} // namespace aare

View File

@@ -2,6 +2,7 @@
#include "aare/RawFile.hpp" #include "aare/RawFile.hpp"
#include "aare/DetectorGeometry.hpp" #include "aare/DetectorGeometry.hpp"
#include "aare/PixelMap.hpp" #include "aare/PixelMap.hpp"
#include "aare/ROIGeometry.hpp"
#include "aare/algorithm.hpp" #include "aare/algorithm.hpp"
#include "aare/defs.hpp" #include "aare/defs.hpp"
#include "aare/logger.hpp" #include "aare/logger.hpp"
@@ -17,28 +18,98 @@ RawFile::RawFile(const std::filesystem::path &fname, const std::string &mode)
: m_master(fname), : m_master(fname),
m_geometry(m_master.geometry(), m_master.pixels_x(), m_master.pixels_y(), m_geometry(m_master.geometry(), m_master.pixels_x(), m_master.pixels_y(),
m_master.udp_interfaces_per_module(), m_master.quad()) { m_master.udp_interfaces_per_module(), m_master.quad()) {
m_mode = mode; m_mode = mode;
m_subfiles.resize(m_master.rois().has_value() ? m_master.rois()->size()
: 1);
if (mode == "r") { if (mode == "r") {
if (m_master.roi()) { if (m_master.rois().has_value()) {
m_geometry.update_geometry_with_roi(m_master.roi().value()); m_ROI_geometries.reserve(m_master.rois()->size());
// iterate over all ROIS
size_t roi_index = 0;
const auto rois = m_master.rois().value();
for (const auto &roi : rois) {
m_ROI_geometries.push_back(ROIGeometry(roi, m_geometry));
// open subfiles
open_subfiles(roi_index);
++roi_index;
}
} else {
// no ROI use full detector
m_ROI_geometries.reserve(1);
m_ROI_geometries.push_back(ROIGeometry(m_geometry));
open_subfiles(0);
} }
open_subfiles();
} else { } else {
throw std::runtime_error(LOCATION + throw std::runtime_error(LOCATION +
" Unsupported mode. Can only read RawFiles."); " Unsupported mode. Can only read RawFiles.");
} }
} }
Frame RawFile::read_frame() { return get_frame(m_current_frame++); } Frame RawFile::read_roi(const size_t roi_index) {
if (!m_master.rois()) {
throw std::runtime_error(LOCATION +
"No ROIs defined in the master file.");
}
if (roi_index >= m_ROI_geometries.size()) {
throw std::runtime_error(LOCATION + "ROI index out of range.");
}
return get_frame(m_current_frame++, roi_index);
}
std::vector<Frame> RawFile::read_rois() {
if (!m_master.rois()) {
throw std::runtime_error(LOCATION +
"No ROIs defined in the master file.");
}
const size_t num_rois = m_ROI_geometries.size();
std::vector<Frame> frames;
frames.reserve(num_rois);
for (size_t roi_idx = 0; roi_idx < num_rois; ++roi_idx) {
frames.push_back(get_frame(m_current_frame, roi_idx));
}
++m_current_frame;
return frames;
}
Frame RawFile::read_frame() {
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(LOCATION +
"Multiple ROIs defined in the master file. "
"Use read_ROIs() instead.");
}
return get_frame(m_current_frame++);
}
Frame RawFile::read_frame(size_t frame_number) { Frame RawFile::read_frame(size_t frame_number) {
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(
LOCATION + "Multiple ROIs defined in the master file. "
"Use read_ROIs(const size_t frame_number) instead.");
}
seek(frame_number); seek(frame_number);
return read_frame(); return read_frame();
} }
void RawFile::read_into(std::byte *image_buf, size_t n_frames) { void RawFile::read_into(std::byte *image_buf, size_t n_frames) {
// TODO: implement this in a more efficient way // TODO: implement this in a more efficient way
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(LOCATION +
"Cannot use read_into for multiple ROIs.");
}
for (size_t i = 0; i < n_frames; i++) { for (size_t i = 0; i < n_frames; i++) {
this->get_frame_into(m_current_frame++, image_buf); this->get_frame_into(m_current_frame++, image_buf);
@@ -47,33 +118,83 @@ void RawFile::read_into(std::byte *image_buf, size_t n_frames) {
} }
void RawFile::read_into(std::byte *image_buf) { void RawFile::read_into(std::byte *image_buf) {
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(LOCATION +
"Cannot use read_into for multiple ROIs. Use "
"read_roi_into() for a single ROI instead.");
}
return get_frame_into(m_current_frame++, image_buf); return get_frame_into(m_current_frame++, image_buf);
} }
void RawFile::read_into(std::byte *image_buf, DetectorHeader *header) { void RawFile::read_roi_into(std::byte *image_buf, const size_t roi_index,
const size_t frame_number, DetectorHeader *header) {
if (!m_master.rois().has_value()) {
throw std::runtime_error(LOCATION +
"No ROIs defined in the master file.");
}
if (roi_index >= num_rois()) {
throw std::runtime_error(LOCATION + "ROI index out of range.");
}
return get_frame_into(frame_number, image_buf, roi_index, header);
}
return get_frame_into(m_current_frame++, image_buf, header); void RawFile::read_into(std::byte *image_buf, DetectorHeader *header) {
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(LOCATION +
"Cannot use read_into for multiple ROIs. Use "
"read_roi_into() for a single ROI instead.");
}
return get_frame_into(m_current_frame++, image_buf, 0, header);
} }
void RawFile::read_into(std::byte *image_buf, size_t n_frames, void RawFile::read_into(std::byte *image_buf, size_t n_frames,
DetectorHeader *header) { DetectorHeader *header) {
// return get_frame_into(m_current_frame++, image_buf, header); // return get_frame_into(m_current_frame++, image_buf, header);
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(
LOCATION +
"Cannot use read_into for multiple ROIs."); // TODO: maybe pass
// roi_index so one can
// use read_into for a
// specific ROI
}
for (size_t i = 0; i < n_frames; i++) { for (size_t i = 0; i < n_frames; i++) {
this->get_frame_into(m_current_frame++, image_buf, header); this->get_frame_into(m_current_frame++, image_buf, 0, header);
image_buf += bytes_per_frame(); image_buf += bytes_per_frame();
if (header) if (header)
header += m_geometry.n_modules_in_roi(); header += m_ROI_geometries[0].num_modules_in_roi();
} }
} }
size_t RawFile::bytes_per_frame() { size_t RawFile::bytes_per_frame() {
return m_geometry.pixels_x() * m_geometry.pixels_y() * m_master.bitdepth() / if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(
LOCATION + "Pass the desired roi_index to bytes_per_frame to get "
"bytes_per_frame for the specific ROI. ");
}
return bytes_per_frame(0);
}
size_t RawFile::bytes_per_frame(const size_t roi_index) {
return m_ROI_geometries.at(roi_index).pixels_x() *
m_ROI_geometries.at(roi_index).pixels_y() * m_master.bitdepth() /
bits_per_byte; bits_per_byte;
} }
size_t RawFile::pixels_per_frame() { size_t RawFile::pixels_per_frame() {
// return m_rows * m_cols; if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
return m_geometry.pixels_x() * m_geometry.pixels_y(); throw std::runtime_error(
LOCATION + "Pass the desired roi_index to pixels_per_frame to get "
"pixels_per_frame for the specific ROI. ");
}
return pixels_per_frame(0);
}
size_t RawFile::pixels_per_frame(const size_t roi_index) {
return m_ROI_geometries.at(roi_index).pixels_x() *
m_ROI_geometries.at(roi_index).pixels_y();
} }
DetectorType RawFile::detector_type() const { return m_master.detector_type(); } DetectorType RawFile::detector_type() const { return m_master.detector_type(); }
@@ -93,8 +214,29 @@ void RawFile::seek(size_t frame_index) {
size_t RawFile::tell() { return m_current_frame; } size_t RawFile::tell() { return m_current_frame; }
size_t RawFile::total_frames() const { return m_master.frames_in_file(); } size_t RawFile::total_frames() const { return m_master.frames_in_file(); }
size_t RawFile::rows() const { return m_geometry.pixels_y(); }
size_t RawFile::cols() const { return m_geometry.pixels_x(); } size_t RawFile::rows() const {
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(LOCATION +
"Pass the desired roi_index to rows to get "
"rows for the specific ROI. ");
}
return rows(0);
}
size_t RawFile::rows(const size_t roi_index) const {
return m_ROI_geometries.at(roi_index).pixels_y();
}
size_t RawFile::cols() const {
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
throw std::runtime_error(LOCATION +
"Pass the desired roi_index to cols to get "
"cols for the specific ROI. ");
}
return cols(0);
}
size_t RawFile::cols(const size_t roi_index) const {
return m_ROI_geometries.at(roi_index).pixels_x();
}
size_t RawFile::bitdepth() const { return m_master.bitdepth(); } size_t RawFile::bitdepth() const { return m_master.bitdepth(); }
xy RawFile::geometry() const { xy RawFile::geometry() const {
return xy{static_cast<uint32_t>(m_geometry.modules_y()), return xy{static_cast<uint32_t>(m_geometry.modules_y()),
@@ -102,19 +244,46 @@ xy RawFile::geometry() const {
} }
size_t RawFile::n_modules() const { return m_geometry.n_modules(); }; size_t RawFile::n_modules() const { return m_geometry.n_modules(); };
size_t RawFile::n_modules_in_roi() const {
return m_geometry.n_modules_in_roi(); size_t RawFile::num_rois() const {
if (m_master.rois().has_value()) {
return m_master.rois()->size();
} else {
return 0;
}
}; };
void RawFile::open_subfiles() { const ROIGeometry &RawFile::roi_geometries(size_t roi_index) const {
if (m_mode == "r") return m_ROI_geometries[roi_index];
for (size_t i : m_geometry.get_modules_in_roi()) { }
auto pos = m_geometry.get_module_geometries(i);
m_subfiles.emplace_back(std::make_unique<RawSubFile>( std::vector<size_t> RawFile::n_modules_in_roi() const {
std::vector<size_t> results(m_ROI_geometries.size());
std::transform(
m_ROI_geometries.begin(), m_ROI_geometries.end(), results.begin(),
[](const ROIGeometry &roi) { return roi.num_modules_in_roi(); });
return results;
};
void RawFile::open_subfiles(const size_t roi_index) {
if (m_mode == "r") {
m_subfiles[roi_index].reserve(
m_ROI_geometries[roi_index].num_modules_in_roi());
auto module_indices =
m_ROI_geometries[roi_index].module_indices_in_roi();
for (const size_t i :
m_ROI_geometries[roi_index].module_indices_in_roi()) {
const auto pos = m_geometry.get_module_geometries(i);
m_subfiles[roi_index].emplace_back(std::make_unique<RawSubFile>(
m_master.data_fname(i, 0), m_master.detector_type(), pos.height, m_master.data_fname(i, 0), m_master.detector_type(), pos.height,
pos.width, m_master.bitdepth(), pos.row_index, pos.col_index)); pos.width, m_master.bitdepth(), pos.row_index, pos.col_index));
} }
else { } else {
throw std::runtime_error(LOCATION + throw std::runtime_error(LOCATION +
"Unsupported mode. Can only read RawFiles."); "Unsupported mode. Can only read RawFiles.");
} }
@@ -139,33 +308,37 @@ DetectorHeader RawFile::read_header(const std::filesystem::path &fname) {
RawMasterFile RawFile::master() const { return m_master; } RawMasterFile RawFile::master() const { return m_master; }
Frame RawFile::get_frame(size_t frame_index) { Frame RawFile::get_frame(size_t frame_index, const size_t roi_index) {
auto f = Frame(m_geometry.pixels_y(), m_geometry.pixels_x(), auto f = Frame(m_ROI_geometries[roi_index].pixels_y(),
m_ROI_geometries[roi_index].pixels_x(),
Dtype::from_bitdepth(m_master.bitdepth())); Dtype::from_bitdepth(m_master.bitdepth()));
std::byte *frame_buffer = f.data(); std::byte *frame_buffer = f.data();
get_frame_into(frame_index, frame_buffer); get_frame_into(frame_index, frame_buffer, roi_index);
return f; return f;
} }
size_t RawFile::bytes_per_pixel() const { return m_master.bitdepth() / 8; } size_t RawFile::bytes_per_pixel() const { return m_master.bitdepth() / 8; }
void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer, void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer,
DetectorHeader *header) { const size_t roi_index, DetectorHeader *header) {
LOG(logDEBUG) << "RawFile::get_frame_into(" << frame_index << ")"; LOG(logDEBUG) << "RawFile::get_frame_into(" << frame_index << ")";
if (frame_index >= total_frames()) { if (frame_index >= total_frames()) {
throw std::runtime_error(LOCATION + "Frame number out of range"); throw std::runtime_error(LOCATION + "Frame number out of range");
} }
std::vector<size_t> frame_numbers(m_geometry.n_modules_in_roi()); std::vector<size_t> frame_numbers(
std::vector<size_t> frame_indices(m_geometry.n_modules_in_roi(), m_ROI_geometries[roi_index].num_modules_in_roi());
frame_index); std::vector<size_t> frame_indices(
m_ROI_geometries[roi_index].num_modules_in_roi(), frame_index);
// sync the frame numbers // sync the frame numbers
if (m_geometry.n_modules() != 1) { // if we have more than one module if (m_ROI_geometries[roi_index].num_modules_in_roi() !=
for (size_t part_idx = 0; part_idx != m_geometry.n_modules_in_roi(); 1) { // if we have more than one module
for (size_t part_idx = 0;
part_idx != m_ROI_geometries[roi_index].num_modules_in_roi();
++part_idx) { ++part_idx) {
frame_numbers[part_idx] = frame_numbers[part_idx] =
m_subfiles[part_idx]->frame_number(frame_index); m_subfiles[roi_index][part_idx]->frame_number(frame_index);
} }
// 1. if frame number vector is the same break // 1. if frame number vector is the same break
@@ -176,7 +349,8 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer,
frame_numbers.begin(), frame_numbers.begin(),
std::min_element(frame_numbers.begin(), frame_numbers.end())); std::min_element(frame_numbers.begin(), frame_numbers.end()));
// 3. increase its index and update its respective frame number // 3. increase its index and update its respective frame
// number
frame_indices[min_frame_idx]++; frame_indices[min_frame_idx]++;
// 4. if we can't increase its index => throw error // 4. if we can't increase its index => throw error
@@ -186,42 +360,50 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer,
} }
frame_numbers[min_frame_idx] = frame_numbers[min_frame_idx] =
m_subfiles[min_frame_idx]->frame_number( m_subfiles[roi_index][min_frame_idx]->frame_number(
frame_indices[min_frame_idx]); frame_indices[min_frame_idx]);
} }
} }
if (m_master.geometry().col == 1) { if (m_master.geometry().col == 1) {
// get the part from each subfile and copy it to the frame // get the part from each subfile and copy it to the frame
for (size_t part_idx = 0; part_idx != m_geometry.n_modules_in_roi(); for (size_t part_idx = 0;
part_idx != m_ROI_geometries[roi_index].num_modules_in_roi();
++part_idx) { ++part_idx) {
auto corrected_idx = frame_indices[part_idx]; auto corrected_idx = frame_indices[part_idx];
// This is where we start writing // This is where we start writing
auto offset = (m_geometry auto offset =
.get_module_geometries( (m_geometry
m_geometry.get_modules_in_roi(part_idx)) .get_module_geometries(
.origin_y * m_ROI_geometries[roi_index].module_indices_in_roi(
m_geometry.pixels_x() + part_idx))
m_geometry .origin_y *
.get_module_geometries( m_ROI_geometries[roi_index].pixels_x() +
m_geometry.get_modules_in_roi(part_idx)) m_geometry
.origin_x) * .get_module_geometries(
m_master.bitdepth() / 8; m_ROI_geometries[roi_index].module_indices_in_roi(
part_idx))
.origin_x) *
m_master.bitdepth() / 8;
if (m_geometry if (m_geometry
.get_module_geometries( .get_module_geometries(
m_geometry.get_modules_in_roi(part_idx)) m_ROI_geometries[roi_index].module_indices_in_roi(
part_idx))
.origin_x != 0) .origin_x != 0)
throw std::runtime_error( throw std::runtime_error(
LOCATION + LOCATION +
" Implementation error. x pos not 0."); // TODO: origin " Implementation error. x pos not 0."); // TODO:
// origin
// can still // can still
// change if roi // change if
// roi
// changes // changes
// TODO! What if the files don't match? // TODO! What if the files don't match?
m_subfiles[part_idx]->seek(corrected_idx); m_subfiles[roi_index][part_idx]->seek(corrected_idx);
m_subfiles[part_idx]->read_into(frame_buffer + offset, header); m_subfiles[roi_index][part_idx]->read_into(frame_buffer + offset,
header);
if (header) if (header)
++header; ++header;
} }
@@ -230,23 +412,25 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer,
// TODO! should we read row by row? // TODO! should we read row by row?
// create a buffer large enough to hold a full module // create a buffer large enough to hold a full module
auto bytes_per_part = m_master.pixels_y() * m_master.pixels_x() * auto bytes_per_part =
m_master.bitdepth() / m_master.pixels_y() * m_master.pixels_x() * m_master.bitdepth() /
8; // TODO! replace with image_size_in_bytes 8; // TODO! replace with image_size_in_bytes // TODO
// shouldnt it only be the module size? - check
auto *part_buffer = new std::byte[bytes_per_part]; auto *part_buffer = new std::byte[bytes_per_part];
// TODO! if we have many submodules we should reorder them on the // TODO! if we have many submodules we should reorder them on
// module level // the module level
for (size_t part_idx = 0; part_idx != m_geometry.n_modules_in_roi(); for (size_t part_idx = 0;
part_idx != m_ROI_geometries[roi_index].num_modules_in_roi();
++part_idx) { ++part_idx) {
auto pos = m_geometry.get_module_geometries( auto pos = m_geometry.get_module_geometries(
m_geometry.get_modules_in_roi(part_idx)); m_ROI_geometries[roi_index].module_indices_in_roi(part_idx));
auto corrected_idx = frame_indices[part_idx]; auto corrected_idx = frame_indices[part_idx];
m_subfiles[part_idx]->seek(corrected_idx); m_subfiles[roi_index][part_idx]->seek(corrected_idx);
m_subfiles[part_idx]->read_into(part_buffer, header); m_subfiles[roi_index][part_idx]->read_into(part_buffer, header);
if (header) if (header)
++header; ++header;
@@ -255,7 +439,8 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer,
auto irow = (pos.origin_y + cur_row); auto irow = (pos.origin_y + cur_row);
auto icol = pos.origin_x; auto icol = pos.origin_x;
auto dest = (irow * this->m_geometry.pixels_x() + icol); auto dest =
(irow * m_ROI_geometries[roi_index].pixels_x() + icol);
dest = dest * m_master.bitdepth() / 8; dest = dest * m_master.bitdepth() / 8;
memcpy(frame_buffer + dest, memcpy(frame_buffer + dest,
part_buffer + part_buffer +
@@ -269,7 +454,17 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer,
std::vector<Frame> RawFile::read_n(size_t n_frames) { std::vector<Frame> RawFile::read_n(size_t n_frames) {
// TODO: implement this in a more efficient way // TODO: implement this in a more efficient way
if (num_rois() > 1) {
throw std::runtime_error(LOCATION +
"Multiple ROIs defined in the master "
"file. Use "
"read_num_rois for a specific ROI or use "
"read_ROIs to read one frame after "
"the other.");
}
std::vector<Frame> frames; std::vector<Frame> frames;
frames.reserve(n_frames);
for (size_t i = 0; i < n_frames; i++) { for (size_t i = 0; i < n_frames; i++) {
frames.push_back(this->get_frame(m_current_frame)); frames.push_back(this->get_frame(m_current_frame));
m_current_frame++; m_current_frame++;
@@ -277,11 +472,26 @@ std::vector<Frame> RawFile::read_n(size_t n_frames) {
return frames; return frames;
} }
std::vector<Frame> RawFile::read_n_with_roi(const size_t n_frames,
const size_t roi_index) {
if (roi_index >= num_rois()) {
throw std::runtime_error(LOCATION + "ROI index out of range.");
}
std::vector<Frame> frames;
frames.reserve(n_frames);
for (size_t i = 0; i < n_frames; i++) {
frames.push_back(this->get_frame(m_current_frame, roi_index));
m_current_frame++;
}
return frames;
}
size_t RawFile::frame_number(size_t frame_index) { size_t RawFile::frame_number(size_t frame_index) {
if (frame_index >= m_master.frames_in_file()) { if (frame_index >= m_master.frames_in_file()) {
throw std::runtime_error(LOCATION + " Frame number out of range"); throw std::runtime_error(LOCATION + " Frame number out of range");
} }
return m_subfiles[0]->frame_number(frame_index); return m_subfiles[0][0]->frame_number(frame_index);
} }
} // namespace aare } // namespace aare

View File

@@ -14,7 +14,8 @@ using aare::File;
using aare::RawFile; using aare::RawFile;
using namespace aare; using namespace aare;
TEST_CASE("Read number of frames from a jungfrau raw file", "[.with-data]") { TEST_CASE("Read number of frames from a jungfrau raw file",
"[.with-data][RawFile]") {
auto fpath = auto fpath =
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json"; test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
@@ -24,7 +25,8 @@ TEST_CASE("Read number of frames from a jungfrau raw file", "[.with-data]") {
REQUIRE(f.total_frames() == 10); REQUIRE(f.total_frames() == 10);
} }
TEST_CASE("Read frame numbers from a jungfrau raw file", "[.with-data]") { TEST_CASE("Read frame numbers from a jungfrau raw file",
"[.with-data][RawFile]") {
auto fpath = auto fpath =
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json"; test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@@ -41,7 +43,7 @@ TEST_CASE("Read frame numbers from a jungfrau raw file", "[.with-data]") {
} }
} }
TEST_CASE("Read a frame number too high throws", "[.with-data]") { TEST_CASE("Read a frame number too high throws", "[.with-data][RawFile]") {
auto fpath = auto fpath =
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json"; test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@@ -57,7 +59,7 @@ TEST_CASE("Read a frame number too high throws", "[.with-data]") {
} }
TEST_CASE("Read a frame numbers where the subfile is missing throws", TEST_CASE("Read a frame numbers where the subfile is missing throws",
"[.with-data]") { "[.with-data][RawFile]") {
auto fpath = test_data_path() / "raw/jungfrau" / auto fpath = test_data_path() / "raw/jungfrau" /
"jungfrau_missing_subfile_master_0.json"; "jungfrau_missing_subfile_master_0.json";
@@ -80,7 +82,7 @@ TEST_CASE("Read a frame numbers where the subfile is missing throws",
} }
TEST_CASE("Read data from a jungfrau 500k single port raw file", TEST_CASE("Read data from a jungfrau 500k single port raw file",
"[.with-data]") { "[.with-data][RawFile]") {
auto fpath = auto fpath =
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json"; test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@@ -99,7 +101,7 @@ TEST_CASE("Read data from a jungfrau 500k single port raw file",
} }
} }
TEST_CASE("Read frame numbers from a raw file", "[.with-data]") { TEST_CASE("Read frame numbers from a raw file", "[.with-data][RawFile]") {
auto fpath = auto fpath =
test_data_path() / "raw/eiger" / "eiger_500k_16bit_master_0.json"; test_data_path() / "raw/eiger" / "eiger_500k_16bit_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@@ -113,7 +115,8 @@ TEST_CASE("Read frame numbers from a raw file", "[.with-data]") {
} }
} }
TEST_CASE("Compare reading from a numpy file with a raw file", "[.with-data]") { TEST_CASE("Compare reading from a numpy file with a raw file",
"[.with-data][RawFile]") {
SECTION("jungfrau data") { SECTION("jungfrau data") {
auto fpath_raw = auto fpath_raw =
@@ -155,6 +158,11 @@ TEST_CASE("Compare reading from a numpy file with a raw file", "[.with-data]") {
auto raw_frame = raw.read_frame(); auto raw_frame = raw.read_frame();
auto npy_frame = npy.read_frame(); auto npy_frame = npy.read_frame();
CHECK(raw_frame.rows() == 512);
CHECK(raw_frame.cols() == 512);
CHECK(npy_frame.rows() == 512);
CHECK(npy_frame.cols() == 512);
CHECK((raw_frame.view<uint32_t>() == npy_frame.view<uint32_t>())); CHECK((raw_frame.view<uint32_t>() == npy_frame.view<uint32_t>()));
} }
SECTION("eiger data") { SECTION("eiger data") {
@@ -176,7 +184,7 @@ TEST_CASE("Compare reading from a numpy file with a raw file", "[.with-data]") {
} }
} }
TEST_CASE("Read multipart files", "[.with-data]") { TEST_CASE("Read multipart files", "[.with-data][RawFile]") {
auto fpath = auto fpath =
test_data_path() / "raw/jungfrau" / "jungfrau_double_master_0.json"; test_data_path() / "raw/jungfrau" / "jungfrau_double_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@@ -217,7 +225,7 @@ struct TestParameters {
std::vector<ModuleGeometry> module_geometries{}; std::vector<ModuleGeometry> module_geometries{};
}; };
TEST_CASE("check find_geometry", "[.with-data]") { TEST_CASE("check find_geometry", "[.with-data][RawFile]") {
auto test_parameters = GENERATE( auto test_parameters = GENERATE(
TestParameters{"raw/jungfrau_2modules_version6.1.2/run_master_0.raw", 2, TestParameters{"raw/jungfrau_2modules_version6.1.2/run_master_0.raw", 2,
@@ -290,7 +298,8 @@ TEST_CASE("check find_geometry", "[.with-data]") {
} }
} }
TEST_CASE("Open multi module file with ROI", "[.with-data]") { TEST_CASE("Open multi module file with ROI",
"[.with-data][read_rois][RawFile]") {
auto fpath = auto fpath =
test_data_path() / "raw/ROITestData/SingleChipROI/Data_master_0.json"; test_data_path() / "raw/ROITestData/SingleChipROI/Data_master_0.json";
@@ -304,7 +313,8 @@ TEST_CASE("Open multi module file with ROI", "[.with-data]") {
CHECK(f.n_modules() == 2); CHECK(f.n_modules() == 2);
CHECK(f.n_modules_in_roi() == 1); REQUIRE(f.n_modules_in_roi().size() == 1);
CHECK(f.n_modules_in_roi()[0] == 1);
auto frames = f.read_n(2); auto frames = f.read_n(2);
@@ -315,7 +325,70 @@ TEST_CASE("Open multi module file with ROI", "[.with-data]") {
} }
} }
TEST_CASE("Read file with unordered frames", "[.with-data]") { TEST_CASE("Open multi module file with ROI spanning over multiple modules",
"[.width-data][read_rois][RawFile]") {
auto fpath =
test_data_path() / "raw/ROITestData/MultipleChipROI/run_master_2.json";
REQUIRE(std::filesystem::exists(fpath));
RawFile f(fpath, "r");
CHECK(f.n_modules() == 2);
CHECK(f.n_modules_in_roi().size() == 1);
CHECK(f.n_modules_in_roi()[0] == 2);
auto frame = f.read_frame();
CHECK(frame.rows() == 301);
CHECK(frame.cols() == 101);
CHECK(f.rows() == 301);
CHECK(f.cols() == 101);
}
TEST_CASE("Open multi module file with two ROIs",
"[.width-data][read_rois][RawFile]") {
auto fpath =
test_data_path() / "raw/ROITestData/MultipleROIs/run_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
RawFile f(fpath, "r");
CHECK(f.n_modules() == 2);
REQUIRE(f.n_modules_in_roi().size() == 2);
CHECK(f.n_modules_in_roi()[0] == 1);
CHECK(f.n_modules_in_roi()[1] == 1);
CHECK_THROWS(f.read_frame());
CHECK(f.tell() == 0);
auto frame = f.read_rois();
CHECK(f.tell() == 1);
REQUIRE(frame.size() == 2);
CHECK(frame[0].rows() == 301);
CHECK(frame[0].cols() == 101);
CHECK(frame[1].rows() == 101);
CHECK(frame[1].cols() == 101);
CHECK_THROWS(f.rows());
CHECK(f.rows(0) == 301);
auto frames = f.read_n_with_roi(2, 1);
REQUIRE(frames.size() == 2);
CHECK(f.tell() == 3);
for (const auto &frm : frames) {
CHECK(frm.rows() == 101);
CHECK(frm.cols() == 101);
}
}
TEST_CASE("Read file with unordered frames", "[.with-data][RawFile]") {
// TODO! Better explanation and error message // TODO! Better explanation and error message
auto fpath = test_data_path() / "raw/mythen/scan242_master_3.raw"; auto fpath = test_data_path() / "raw/mythen/scan242_master_3.raw";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@@ -323,7 +396,7 @@ TEST_CASE("Read file with unordered frames", "[.with-data]") {
REQUIRE_THROWS((f.read_frame())); REQUIRE_THROWS((f.read_frame()));
} }
TEST_CASE("Read Mythenframe", "[.with-data]") { TEST_CASE("Read Mythenframe", "[.with-data][RawFile]") {
auto fpath = test_data_path() / "raw/newmythen03/run_2_master_1.json"; auto fpath = test_data_path() / "raw/newmythen03/run_2_master_1.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
RawFile f(fpath); RawFile f(fpath);

View File

@@ -71,7 +71,7 @@ ScanParameters::ScanParameters(const bool enabled, const DACIndex dac,
const int start, const int stop, const int step, const int start, const int stop, const int step,
const int64_t settleTime) const int64_t settleTime)
: m_enabled(enabled), m_dac(dac), m_start(start), m_stop(stop), : m_enabled(enabled), m_dac(dac), m_start(start), m_stop(stop),
m_step(step), m_settleTime(settleTime) {}; m_step(step), m_settleTime(settleTime){};
// "[enabled\ndac dac 4\nstart 500\nstop 2200\nstep 5\nsettleTime 100us\n]" // "[enabled\ndac dac 4\nstart 500\nstop 2200\nstep 5\nsettleTime 100us\n]"
ScanParameters::ScanParameters(const std::string &par) { ScanParameters::ScanParameters(const std::string &par) {
@@ -193,7 +193,18 @@ ScanParameters RawMasterFile::scan_parameters() const {
return m_scan_parameters; return m_scan_parameters;
} }
std::optional<ROI> RawMasterFile::roi() const { return m_roi; } std::optional<ROI> RawMasterFile::roi() const {
if (m_rois.value().size() > 1) {
throw std::runtime_error(LOCATION +
"Multiple ROIs present, use rois() method.");
} else {
return m_rois.has_value()
? std::optional<ROI>(m_rois.value().at(0))
: std::nullopt; // TODO: maybe throw if no roi exists
}
}
std::optional<std::vector<ROI>> RawMasterFile::rois() const { return m_rois; }
void RawMasterFile::parse_json(std::istream &is) { void RawMasterFile::parse_json(std::istream &is) {
json j; json j;
@@ -255,7 +266,7 @@ void RawMasterFile::parse_json(std::istream &is) {
m_frame_discard_policy = string_to<FrameDiscardPolicy>( m_frame_discard_policy = string_to<FrameDiscardPolicy>(
j["Frame Discard Policy"].get<std::string>()); j["Frame Discard Policy"].get<std::string>());
if(j.contains("Number of rows") && j["Number of rows"].is_number()){ if (j.contains("Number of rows") && j["Number of rows"].is_number()) {
m_number_of_rows = j["Number of rows"]; m_number_of_rows = j["Number of rows"];
} }
@@ -338,34 +349,35 @@ void RawMasterFile::parse_json(std::istream &is) {
} }
} }
try { try {
ROI tmp_roi;
if (v < 8.0) { if (v < 8.0) {
auto obj = j.at("Receiver Roi"); auto obj = j.at("Receiver Roi");
tmp_roi.xmin = obj.at("xmin"); if (obj.at("xmin") != 4294967295 || obj.at("xmax") != 4294967295 ||
tmp_roi.xmax = obj.at("xmax"); obj.at("ymin") != 4294967295 || obj.at("ymax") != 4294967295) {
tmp_roi.ymin = obj.at("ymin"); // Handle Mythen
tmp_roi.ymax = obj.at("ymax"); if (obj.at("ymin") == -1 && obj.at("ymax") == -1) {
} else { obj.at("ymin") = 0;
// TODO: for now only handle single ROI obj.at("ymax") = 0;
auto obj = j.at("Receiver Rois"); }
tmp_roi.xmin = obj[0].at("xmin"); m_rois.emplace();
tmp_roi.xmax = obj[0].at("xmax"); m_rois.value().push_back(ROI{
tmp_roi.ymin = obj[0].at("ymin"); obj.at("xmin"), static_cast<ssize_t>(obj.at("xmax")) + 1,
tmp_roi.ymax = obj[0].at("ymax"); obj.at("ymin"), static_cast<ssize_t>(obj.at("ymax")) + 1});
} }
} else {
// if any of the values are set update the roi auto obj = j.at("Receiver Rois");
// TODO: doesnt it write garbage if one of them is not set m_rois.emplace();
if (tmp_roi.xmin != 4294967295 || tmp_roi.xmax != 4294967295 || for (auto &elem : obj) {
tmp_roi.ymin != 4294967295 || tmp_roi.ymax != 4294967295) { // handle Mythen
tmp_roi.xmax++; if (elem.at("ymin") == -1 && elem.at("ymax") == -1) {
// Handle Mythen elem.at("ymin") = 0;
if (tmp_roi.ymin == -1 && tmp_roi.ymax == -1) { elem.at("ymax") = 0;
tmp_roi.ymin = 0; }
tmp_roi.ymax = 0;
m_rois.value().push_back(ROI{
elem.at("xmin"), static_cast<ssize_t>(elem.at("xmax")) + 1,
elem.at("ymin"),
static_cast<ssize_t>(elem.at("ymax")) + 1});
} }
tmp_roi.ymax++;
m_roi = tmp_roi;
} }
} catch (const json::out_of_range &e) { } catch (const json::out_of_range &e) {
@@ -380,7 +392,6 @@ void RawMasterFile::parse_json(std::istream &is) {
std::stoi(j["Counter Mask"].get<std::string>(), nullptr, 16); std::stoi(j["Counter Mask"].get<std::string>(), nullptr, 16);
} }
// Update detector type for Moench // Update detector type for Moench
// TODO! How does this work with old .raw master files? // TODO! How does this work with old .raw master files?
if (m_type == DetectorType::Moench && !m_analog_samples && if (m_type == DetectorType::Moench && !m_analog_samples &&