mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-04-21 04:04:35 +02:00
Dev/multiple rois in aare (#263)
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:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user