This commit is contained in:
froejdh_e 2025-03-05 17:40:08 +01:00
parent 8ae6bb76f8
commit 5614cb4673
15 changed files with 523 additions and 39 deletions

View File

@ -346,6 +346,7 @@ set(SourceFiles
${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Interpolator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/PixelMap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.cpp

View File

@ -8,11 +8,17 @@
namespace aare {
//TODO! Template this?
struct Cluster3x3 {
int16_t x;
int16_t y;
int32_t data[9];
};
struct Cluster2x2 {
int16_t x;
int16_t y;
int32_t data[4];
};
typedef enum {
cBottomLeft = 0,
@ -37,6 +43,7 @@ struct Eta2 {
double x;
double y;
corner c;
int32_t sum;
};
struct ClusterAnalysis {
@ -97,6 +104,8 @@ class ClusterFile {
*/
ClusterVector<int32_t> read_clusters(size_t n_clusters);
ClusterVector<int32_t> read_clusters(size_t n_clusters, ROI roi);
/**
* @brief Read a single frame from the file and return the clusters. The
* cluster vector will have the frame number set.
@ -131,5 +140,6 @@ int analyze_cluster(Cluster3x3 &cl, int32_t *t2, int32_t *t3, char *quad,
NDArray<double, 2> calculate_eta2(ClusterVector<int> &clusters);
Eta2 calculate_eta2(Cluster3x3 &cl);
Eta2 calculate_eta2(Cluster2x2 &cl);
} // namespace aare

View File

@ -231,6 +231,10 @@ template <typename T, typename CoordType = int16_t> class ClusterVector {
return *reinterpret_cast<V *>(element_ptr(i));
}
template <typename V> const V &at(size_t i) const {
return *reinterpret_cast<const V *>(element_ptr(i));
}
const std::string_view fmt_base() const {
// TODO! how do we match on coord_t?
return m_fmt_base;

View File

@ -0,0 +1,29 @@
#pragma once
#include "aare/NDArray.hpp"
#include "aare/NDView.hpp"
#include "aare/ClusterVector.hpp"
#include "aare/ClusterFile.hpp" //Cluster_3x3
namespace aare{
struct Photon{
double x;
double y;
double energy;
};
class Interpolator{
NDArray<double, 3> m_ietax;
NDArray<double, 3> m_ietay;
NDArray<double, 1> m_etabinsx;
NDArray<double, 1> m_etabinsy;
NDArray<double, 1> m_energy_bins;
public:
Interpolator(NDView<double, 3> etacube, NDView<double, 1> xbins, NDView<double, 1> ybins, NDView<double, 1> ebins);
NDArray<double, 3> get_ietax(){return m_ietax;}
NDArray<double, 3> get_ietay(){return m_ietay;}
std::vector<Photon> interpolate(const ClusterVector<int32_t>& clusters);
};
} // namespace aare

View File

@ -7,6 +7,7 @@ from ._aare import Pedestal_d, Pedestal_f, ClusterFinder, VarClusterFinder
from ._aare import DetectorType
from ._aare import ClusterFile
from ._aare import hitmap
from ._aare import ROI
from ._aare import ClusterFinderMT, ClusterCollector, ClusterFileSink, ClusterVector_i

View File

@ -1,50 +1,40 @@
import sys
sys.path.append('/home/l_msdetect/erik/aare/build')
#Our normal python imports
from pathlib import Path
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
import boost_histogram as bh
import time
import tifffile
import aare
data = np.random.normal(10, 1, 1000)
hist = bh.Histogram(bh.axis.Regular(10, 0, 20))
hist.fill(data)
#Directly import what we need from aare
from aare import File, ClusterFile, hitmap
from aare._aare import calculate_eta2, ClusterFinderMT, ClusterCollector
x = hist.axes[0].centers
y = hist.values()
y_err = np.sqrt(y)+1
res = aare.fit_gaus(x, y, y_err, chi2 = True)
base = Path('/mnt/sls_det_storage/moench_data/tomcat_nanoscope_21042020/09_Moench_650um/')
# for f in base.glob('*'):
# print(f.name)
t_elapsed = time.perf_counter()-t0
print(f'Histogram filling took: {t_elapsed:.3f}s {total_clusters/t_elapsed/1e6:.3f}M clusters/s')
cluster_fname = base/'acq_interp_center_3.8Mfr_200V.clust'
flatfield_fname = base/'flatfield_center_200_d0_f000000000000_0.clust'
histogram_data = hist3d.counts()
x = hist3d.axes[2].edges[:-1]
y = histogram_data[100,100,:]
xx = np.linspace(x[0], x[-1])
# fig, ax = plt.subplots()
# ax.step(x, y, where = 'post')
y_err = np.sqrt(y)
y_err = np.zeros(y.size)
y_err += 1
# par = fit_gaus2(y,x, y_err)
# ax.plot(xx, gaus(xx,par))
# print(par)
res = fit_gaus(y,x)
res2 = fit_gaus(y,x, y_err)
print(res)
print(res2)
cluster_fname.stat().st_size/1e6/4
image = np.zeros((400,400))
with ClusterFile(cluster_fname, chunk_size = 1000000) as f:
for clusters in f:
test = hitmap(image.shape, clusters)
break
# image += hitmap(image.shape, clusters)
# break
print('We are back in python')
# fig, ax = plt.subplots(figsize = (7,7))
# im = ax.imshow(image)
# im.set_clim(0,1)

View File

@ -0,0 +1,98 @@
{
"folders": [
{
"path": "../../.."
},
{
"path": "../../../../slsDetectorPackage"
}
],
"settings": {
"files.associations": {
"compare": "cpp",
"cstdint": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"any": "cpp",
"array": "cpp",
"atomic": "cpp",
"strstream": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cfenv": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"codecvt": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"source_location": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"format": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"ranges": "cpp",
"semaphore": "cpp",
"shared_mutex": "cpp",
"span": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stdfloat": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"text_encoding": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp",
"regex": "cpp",
"*.ipp": "cpp"
}
}
}

View File

@ -20,7 +20,13 @@ template <typename T>
void define_cluster_vector(py::module &m, const std::string &typestr) {
auto class_name = fmt::format("ClusterVector_{}", typestr);
py::class_<ClusterVector<T>>(m, class_name.c_str(), py::buffer_protocol())
.def(py::init<int, int>())
.def(py::init<int, int>(),
py::arg("cluster_size_x") = 3, py::arg("cluster_size_y") = 3)
.def("push_back",
[](ClusterVector<T> &self, int x, int y, py::array_t<T> data) {
// auto view = make_view_2d(data);
self.push_back(x, y, reinterpret_cast<const std::byte*>(data.data()));
})
.def_property_readonly("size", &ClusterVector<T>::size)
.def("item_size", &ClusterVector<T>::item_size)
.def_property_readonly("fmt",
@ -38,6 +44,8 @@ void define_cluster_vector(py::module &m, const std::string &typestr) {
auto *vec = new std::vector<T>(self.sum_2x2());
return return_vector(vec);
})
.def_property_readonly("cluster_size_x", &ClusterVector<T>::cluster_size_x)
.def_property_readonly("cluster_size_y", &ClusterVector<T>::cluster_size_y)
.def_property_readonly("capacity", &ClusterVector<T>::capacity)
.def_property("frame_number", &ClusterVector<T>::frame_number,
&ClusterVector<T>::set_frame_number)

View File

@ -31,6 +31,11 @@ void define_cluster_file_io_bindings(py::module &m) {
auto v = new ClusterVector<int32_t>(self.read_clusters(n_clusters));
return v;
},py::return_value_policy::take_ownership)
.def("read_clusters",
[](ClusterFile &self, size_t n_clusters, ROI roi) {
auto v = new ClusterVector<int32_t>(self.read_clusters(n_clusters, roi));
return v;
},py::return_value_policy::take_ownership)
.def("read_frame",
[](ClusterFile &self) {
auto v = new ClusterVector<int32_t>(self.read_frame());

View File

@ -195,6 +195,8 @@ void define_file_io_bindings(py::module &m) {
py::class_<ROI>(m, "ROI")
.def(py::init<>())
.def(py::init<int64_t, int64_t, int64_t, int64_t>(), py::arg("xmin"),
py::arg("xmax"), py::arg("ymin"), py::arg("ymax"))
.def_readwrite("xmin", &ROI::xmin)
.def_readwrite("xmax", &ROI::xmax)
.def_readwrite("ymin", &ROI::ymin)

View File

@ -0,0 +1,58 @@
#include "aare/Interpolator.hpp"
#include "aare/NDArray.hpp"
#include "aare/NDView.hpp"
#include "np_helper.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
void define_interpolation_bindings(py::module &m) {
PYBIND11_NUMPY_DTYPE(aare::Photon, x,y,energy);
py::class_<aare::Interpolator>(m, "Interpolator")
.def(py::init([](py::array_t<double, py::array::c_style | py::array::forcecast> etacube, py::array_t<double> xbins,
py::array_t<double> ybins, py::array_t<double> ebins) {
return Interpolator(make_view_3d(etacube), make_view_1d(xbins),
make_view_1d(ybins), make_view_1d(ebins));
}))
.def("get_ietax", [](Interpolator& self){
auto*ptr = new NDArray<double,3>{};
*ptr = self.get_ietax();
return return_image_data(ptr);
})
.def("get_ietay", [](Interpolator& self){
auto*ptr = new NDArray<double,3>{};
*ptr = self.get_ietay();
return return_image_data(ptr);
})
.def("interpolate", [](Interpolator& self, const ClusterVector<int32_t>& clusters){
auto photons = self.interpolate(clusters);
auto* ptr = new std::vector<Photon>{photons};
return return_vector(ptr);
});
// TODO! Evaluate without converting to double
m.def(
"hej",
[]() {
// auto boost_histogram = py::module_::import("boost_histogram");
// py::object axis =
// boost_histogram.attr("axis").attr("Regular")(10, 0.0, 10.0);
// py::object histogram = boost_histogram.attr("Histogram")(axis);
// return histogram;
// return h;
},
R"(
Evaluate a 1D Gaussian function for all points in x using parameters par.
Parameters
----------
x : array_like
The points at which to evaluate the Gaussian function.
par : array_like
The parameters of the Gaussian function. The first element is the amplitude, the second element is the mean, and the third element is the standard deviation.
)");
}

View File

@ -9,6 +9,7 @@
#include "cluster.hpp"
#include "cluster_file.hpp"
#include "fit.hpp"
#include "interpolation.hpp"
//Pybind stuff
#include <pybind11/pybind11.h>
@ -31,5 +32,6 @@ PYBIND11_MODULE(_aare, m) {
define_cluster_collector_bindings(m);
define_cluster_file_sink_bindings(m);
define_fit_bindings(m);
define_interpolation_bindings(m);
}

View File

@ -108,6 +108,79 @@ ClusterVector<int32_t> ClusterFile::read_clusters(size_t n_clusters) {
return clusters;
}
ClusterVector<int32_t> ClusterFile::read_clusters(size_t n_clusters, ROI roi) {
if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
}
ClusterVector<int32_t> clusters(3,3);
clusters.reserve(n_clusters);
int32_t iframe = 0; // frame number needs to be 4 bytes!
size_t nph_read = 0;
uint32_t nn = m_num_left;
uint32_t nph = m_num_left; // number of clusters in frame needs to be 4
// auto buf = reinterpret_cast<Cluster3x3 *>(clusters.data());
// auto buf = clusters.data();
Cluster3x3 tmp; //this would break if the cluster size changes
// if there are photons left from previous frame read them first
if (nph) {
if (nph > n_clusters) {
// if we have more photons left in the frame then photons to read we
// read directly the requested number
nn = n_clusters;
} else {
nn = nph;
}
//Read one cluster, in the ROI push back
// nph_read += fread((buf + nph_read*clusters.item_size()),
// clusters.item_size(), nn, fp);
for(size_t i = 0; i < nn; i++){
fread(&tmp, sizeof(tmp), 1, fp);
if(tmp.x >= roi.xmin && tmp.x <= roi.xmax && tmp.y >= roi.ymin && tmp.y <= roi.ymax){
clusters.push_back(tmp.x, tmp.y, reinterpret_cast<std::byte*>(tmp.data));
nph_read++;
}
}
m_num_left = nph - nn; // write back the number of photons left
}
if (nph_read < n_clusters) {
// keep on reading frames and photons until reaching n_clusters
while (fread(&iframe, sizeof(iframe), 1, fp)) {
// read number of clusters in frame
if (fread(&nph, sizeof(nph), 1, fp)) {
if (nph > (n_clusters - nph_read))
nn = n_clusters - nph_read;
else
nn = nph;
// nph_read += fread((buf + nph_read*clusters.item_size()),
// clusters.item_size(), nn, fp);
for(size_t i = 0; i < nn; i++){
fread(&tmp, sizeof(tmp), 1, fp);
if(tmp.x >= roi.xmin && tmp.x <= roi.xmax && tmp.y >= roi.ymin && tmp.y <= roi.ymax){
clusters.push_back(tmp.x, tmp.y, reinterpret_cast<std::byte*>(tmp.data));
nph_read++;
}
}
m_num_left = nph - nn;
}
if (nph_read >= n_clusters)
break;
}
}
// Resize the vector to the number of clusters.
// No new allocation, only change bounds.
clusters.resize(nph_read);
return clusters;
}
ClusterVector<int32_t> ClusterFile::read_frame() {
if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
@ -268,11 +341,23 @@ ClusterVector<int32_t> ClusterFile::read_frame() {
NDArray<double, 2> calculate_eta2(ClusterVector<int> &clusters) {
//TOTO! make work with 2x2 clusters
NDArray<double, 2> eta2({static_cast<int64_t>(clusters.size()), 2});
for (size_t i = 0; i < clusters.size(); i++) {
auto e = calculate_eta2(clusters.at<Cluster3x3>(i));
eta2(i, 0) = e.x;
eta2(i, 1) = e.y;
if (clusters.cluster_size_x() == 3 || clusters.cluster_size_y() == 3) {
for (size_t i = 0; i < clusters.size(); i++) {
auto e = calculate_eta2(clusters.at<Cluster3x3>(i));
eta2(i, 0) = e.x;
eta2(i, 1) = e.y;
}
}else if(clusters.cluster_size_x() == 2 || clusters.cluster_size_y() == 2){
for (size_t i = 0; i < clusters.size(); i++) {
auto e = calculate_eta2(clusters.at<Cluster2x2>(i));
eta2(i, 0) = e.x;
eta2(i, 1) = e.y;
}
}else{
throw std::runtime_error("Only 3x3 and 2x2 clusters are supported");
}
return eta2;
}
@ -290,7 +375,7 @@ Eta2 calculate_eta2(Cluster3x3 &cl) {
tot2[3] = cl.data[4] + cl.data[5] + cl.data[7] + cl.data[8];
auto c = std::max_element(tot2.begin(), tot2.end()) - tot2.begin();
eta.sum = tot2[c];
switch (c) {
case cBottomLeft:
if ((cl.data[3] + cl.data[4]) != 0)
@ -333,6 +418,19 @@ Eta2 calculate_eta2(Cluster3x3 &cl) {
return eta;
}
Eta2 calculate_eta2(Cluster2x2 &cl) {
Eta2 eta{};
eta.x = static_cast<double>(cl.data[0]) / (cl.data[0] + cl.data[1]);
eta.y = static_cast<double>(cl.data[0]) / (cl.data[0] + cl.data[2]);
eta.sum = cl.data[0] + cl.data[1] + cl.data[2]+ cl.data[3];
eta.c = cBottomLeft; //TODO! This is not correct
return eta;
}
int analyze_cluster(Cluster3x3 &cl, int32_t *t2, int32_t *t3, char *quad,
double *eta2x, double *eta2y, double *eta3x,
double *eta3y) {

159
src/Interpolator.cpp Normal file
View File

@ -0,0 +1,159 @@
#include "aare/Interpolator.hpp"
namespace aare {
Interpolator::Interpolator(NDView<double, 3> etacube, NDView<double, 1> xbins,
NDView<double, 1> ybins, NDView<double, 1> ebins)
: m_ietax(etacube), m_ietay(etacube), m_etabinsx(xbins), m_etabinsy(ybins), m_energy_bins(ebins) {
if (etacube.shape(0) != xbins.size() || etacube.shape(1) != ybins.size() ||
etacube.shape(2) != ebins.size()) {
throw std::invalid_argument(
"The shape of the etacube does not match the shape of the bins");
}
// Cumulative sum in the x direction, can maybe be combined with a copy?
for (ssize_t k = 0; k < m_ietax.shape(2); k++) {
for (ssize_t j = 0; j < m_ietax.shape(1); j++) {
for (ssize_t i = 1; i < m_ietax.shape(0); i++) {
m_ietax(i, j, k) += m_ietax(i - 1, j, k);
}
}
}
// Normalize by the highest row, if norm less than 1 don't do anything
for (ssize_t k = 0; k < m_ietax.shape(2); k++) {
for (ssize_t j = 0; j < m_ietax.shape(1); j++) {
auto val = m_ietax(m_ietax.shape(0) - 1, j, k);
double norm = val < 1 ? 1 : val;
for (ssize_t i = 0; i < m_ietax.shape(0); i++) {
m_ietax(i, j, k) /= norm;
}
}
}
// Cumulative sum in the y direction
for (ssize_t k = 0; k < m_ietay.shape(2); k++) {
for (ssize_t i = 0; i < m_ietay.shape(0); i++) {
for (ssize_t j = 1; j < m_ietay.shape(1); j++) {
m_ietay(i, j, k) += m_ietay(i, j - 1, k);
}
}
}
// Normalize by the highest column, if norm less than 1 don't do anything
for (ssize_t k = 0; k < m_ietay.shape(2); k++) {
for (ssize_t i = 0; i < m_ietay.shape(0); i++) {
auto val = m_ietay(i, m_ietay.shape(1) - 1, k);
double norm = val < 1 ? 1 : val;
for (ssize_t j = 0; j < m_ietay.shape(1); j++) {
m_ietay(i, j, k) /= norm;
}
}
}
}
std::vector<Photon> Interpolator::interpolate(const ClusterVector<int32_t>& clusters) {
std::vector<Photon> photons;
photons.reserve(clusters.size());
if (clusters.cluster_size_x() == 3 || clusters.cluster_size_y() == 3) {
for (size_t i = 0; i<clusters.size(); i++){
auto cluster = clusters.at<Cluster3x3>(i);
Eta2 eta= calculate_eta2(cluster);
Photon photon;
photon.x = cluster.x;
photon.y = cluster.y;
photon.energy = eta.sum;
//Now do some actual interpolation.
//Find which energy bin the cluster is in
//TODO! Could we use boost-histogram Axis.index here?
ssize_t idx = std::lower_bound(m_energy_bins.begin(), m_energy_bins.end(), photon.energy)-m_energy_bins.begin();
auto ix = std::lower_bound(m_etabinsx.begin(), m_etabinsx.end(), eta.x)- m_etabinsx.begin();
auto iy = std::lower_bound(m_etabinsy.begin(), m_etabinsy.end(), eta.y)- m_etabinsy.begin();
// ibx=(np.abs(etabinsx - ex)).argmin() #Find out which bin the eta should land in
// iby=(np.abs(etabinsy - ey)).argmin()
double dX, dY;
int ex, ey;
// cBottomLeft = 0,
// cBottomRight = 1,
// cTopLeft = 2,
// cTopRight = 3
switch (eta.c) {
case cTopLeft:
dX = -1.;
dY = 0;
break;
case cTopRight:;
dX = 0;
dY = 0;
break;
case cBottomLeft:
dX = -1.;
dY = -1.;
break;
case cBottomRight:
dX = 0;
dY = -1.;
break;
}
photon.x += m_ietax(ix, iy, idx) + dX + 0.5;
photon.y += m_ietay(ix, iy, idx) + dY + 0.5;
// fmt::print("x: {}, y: {}, energy: {}\n", photon.x, photon.y, photon.energy);
photons.push_back(photon);
}
}else if(clusters.cluster_size_x() == 2 || clusters.cluster_size_y() == 2){
//TODO! Implement 2x2 interpolation
for (size_t i = 0; i<clusters.size(); i++){
auto cluster = clusters.at<Cluster2x2>(i);
Eta2 eta= calculate_eta2(cluster);
Photon photon;
photon.x = cluster.x;
photon.y = cluster.y;
photon.energy = eta.sum;
//Now do some actual interpolation.
//Find which energy bin the cluster is in
//TODO! Could we use boost-histogram Axis.index here?
ssize_t idx = std::lower_bound(m_energy_bins.begin(), m_energy_bins.end(), photon.energy)-m_energy_bins.begin();
// auto ix = std::lower_bound(m_etabinsx.begin(), m_etabinsx.end(), eta.x)- m_etabinsx.begin();
// auto iy = std::lower_bound(m_etabinsy.begin(), m_etabinsy.end(), eta.y)- m_etabinsy.begin();
// if(ix<0) ix=0;
// if(iy<0) iy=0;
auto find_index = [](NDArray<double, 1>& etabins, double val){
auto iter = std::min_element(etabins.begin(), etabins.end(),
[val,etabins](double a, double b) {
return std::abs(a - val) < std::abs(b - val);
});
return std::distance(etabins.begin(), iter);
};
auto ix = find_index(m_etabinsx, eta.x)-1;
auto iy = find_index(m_etabinsy, eta.y)-1;
photon.x += (1-m_ietax(ix, iy, idx))*2; //eta goes between 0 and 1 but we could move the hit anywhere in the 2x2
photon.y += (1-m_ietay(ix, iy, idx))*2;
// photon.x = ix;
// photon.y = iy;
photons.push_back(photon);
}
}else{
throw std::runtime_error("Only 3x3 and 2x2 clusters are supported for interpolation");
}
return photons;
}
} // namespace aare

View File

@ -2,6 +2,7 @@
#include <array>
#include <catch2/benchmark/catch_benchmark.hpp>
#include <catch2/catch_test_macros.hpp>
#include <numeric>
using aare::NDArray;
using aare::NDView;
@ -34,6 +35,24 @@ TEST_CASE("Construct from an NDView") {
}
}
TEST_CASE("3D NDArray from NDView"){
std::vector<int> data(27);
std::iota(data.begin(), data.end(), 0);
NDView<int, 3> view(data.data(), Shape<3>{3, 3, 3});
NDArray<int, 3> image(view);
REQUIRE(image.shape() == view.shape());
REQUIRE(image.size() == view.size());
REQUIRE(image.data() != view.data());
for(int64_t i=0; i<image.shape(0); i++){
for(int64_t j=0; j<image.shape(1); j++){
for(int64_t k=0; k<image.shape(2); k++){
REQUIRE(image(i, j, k) == view(i, j, k));
}
}
}
}
TEST_CASE("1D image") {
std::array<int64_t, 1> shape{{20}};
NDArray<short, 1> img(shape, 3);