Commit 52a406b8 authored by David Frank's avatar David Frank
Browse files

#105 Add slicing capabilities to DataContainer

Access to a portion of the DataContainer in the form of slices.
Currently, only slicing in the direction of the last dimension are
possible, but this should still be useful for many cases.

The one suprising thing in this implementation, is that the returned
DataContainer, has a descriptor which is of the same dimension as the
original DataContainer but with a "thickness" of 1 in the last
dimension, instead of actually being lower dimensional.
parent e7fece2f
Pipeline #648180 passed with stages
in 21 minutes and 24 seconds
......@@ -3,6 +3,7 @@
#include "DataHandlerMapCPU.h"
#include "BlockDescriptor.h"
#include "RandomBlocksDescriptor.h"
#include "PartitionDescriptor.h"
#include "Error.h"
#include "TypeCasts.hpp"
......@@ -362,6 +363,40 @@ namespace elsa
newHandlerType};
}
template <typename data_t>
const DataContainer<data_t> DataContainer<data_t>::slice(index_t i) const
{
auto& desc = getDataDescriptor();
auto dim = desc.getNumberOfDimensions();
auto sizeOfLastDim = desc.getNumberOfCoefficientsPerDimension()[dim - 1];
if (i >= sizeOfLastDim) {
throw LogicError("Trying to access out of bound slice");
}
auto sliceDesc = PartitionDescriptor(desc, sizeOfLastDim);
// Now set the slice
return viewAs(sliceDesc).getBlock(i);
}
template <typename data_t>
DataContainer<data_t> DataContainer<data_t>::slice(index_t i)
{
auto& desc = getDataDescriptor();
auto dim = desc.getNumberOfDimensions();
auto sizeOfLastDim = desc.getNumberOfCoefficientsPerDimension()[dim - 1];
if (i >= sizeOfLastDim) {
throw LogicError("Trying to access out of bound slice");
}
auto sliceDesc = PartitionDescriptor(desc, sizeOfLastDim);
// Now set the slice
return viewAs(sliceDesc).getBlock(i);
}
template <typename data_t>
typename DataContainer<data_t>::iterator DataContainer<data_t>::begin()
{
......
......@@ -317,6 +317,19 @@ namespace elsa
/// return a const view of this DataContainer with a different descriptor
const DataContainer<data_t> viewAs(const DataDescriptor& dataDescriptor) const;
/// @brief Slice the container in the last dimension
///
/// Access a portion of the container via a slice. The slicing always is in the last
/// dimension. So for a 3D volume, the slice would be an sliced in the z direction and would
/// be a part of the x-y plane.
///
/// A slice is always the same dimension as the original DataContainer, but with a thickness
/// of 1 in the last dimension (i.e. the coefficient of the last dimension is 1)
const DataContainer<data_t> slice(index_t i) const;
/// @overload non-canst/read-write overload
DataContainer<data_t> slice(index_t i);
/// iterator for DataContainer (random access and continuous)
using iterator = DataContainerIterator<DataContainer<data_t>>;
......
......@@ -51,6 +51,9 @@ TYPE_TO_STRING(DataContainer<index_t>);
TYPE_TO_STRING(DataContainer<std::complex<float>>);
TYPE_TO_STRING(DataContainer<std::complex<double>>);
TYPE_TO_STRING(std::complex<float>);
TYPE_TO_STRING(std::complex<double>);
#ifdef ELSA_CUDA_VECTOR
using GPUTypeTuple =
std::tuple<TestHelperGPU<float>, TestHelperGPU<double>, TestHelperGPU<std::complex<float>>,
......@@ -973,6 +976,228 @@ TEST_CASE_TEMPLATE("DataContainer: Concatenate two DataContainers", data_t, floa
}
}
TEST_CASE_TEMPLATE("DataContainer: Slice a DataContainer", data_t, float, double,
std::complex<float>, std::complex<double>)
{
// Set seed for Eigen Matrices!
srand((unsigned int) 666);
GIVEN("A non 3D DataContainer")
{
constexpr index_t size = 20;
IndexVector_t numCoeff2D(2);
numCoeff2D << size, size;
const VolumeDescriptor desc(numCoeff2D);
const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size);
const DataContainer<data_t> dc(desc, randVec);
THEN("Accessing an out of bounds slice throws")
{
REQUIRE_THROWS_AS(dc.slice(20), LogicError);
}
WHEN("Accessing all the slices")
{
for (int i = 0; i < size; ++i) {
auto slice = dc.slice(i);
THEN("The the slice is a 2D slice of \"thickness\" 1")
{
REQUIRE_EQ(slice.getDataDescriptor().getNumberOfDimensions(), 2);
auto coeffs = slice.getDataDescriptor().getNumberOfCoefficientsPerDimension();
auto expectedCoeffs = IndexVector_t(2);
expectedCoeffs << size, 1;
REQUIRE_EQ(coeffs, expectedCoeffs);
}
THEN("All values are the same as of the original DataContainer")
{
// Check that it's read correctly
auto vecSlice = randVec.segment(i * size, size);
for (int j = 0; j < size; ++j) {
REQUIRE_UNARY(checkApproxEq(slice(j, 0), vecSlice[j]));
}
}
}
}
}
GIVEN("A const 3D DataContainer")
{
constexpr index_t size = 20;
const VolumeDescriptor desc({size, size, size});
const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size * size);
const DataContainer<data_t> dc(desc, randVec);
THEN("Accessing an out of bounds slice throws")
{
REQUIRE_THROWS_AS(dc.slice(20), LogicError);
}
WHEN("Accessing all the slices")
{
for (int i = 0; i < size; ++i) {
auto slice = dc.slice(i);
THEN("The the slice is a 3D slice of \"thickness\" 1")
{
REQUIRE_EQ(slice.getDataDescriptor().getNumberOfDimensions(), 3);
auto coeffs = slice.getDataDescriptor().getNumberOfCoefficientsPerDimension();
auto expectedCoeffs = IndexVector_t(3);
expectedCoeffs << size, size, 1;
REQUIRE_EQ(coeffs, expectedCoeffs);
}
THEN("All values are the same as of the original DataContainer")
{
// Check that it's read correctly
auto vecSlice = randVec.segment(i * size * size, size * size);
for (int j = 0; j < size; ++j) {
for (int k = 0; k < size; ++k) {
REQUIRE_UNARY(checkApproxEq(slice(k, j, 0), vecSlice[k + j * size]));
}
}
}
}
}
}
GIVEN("A non-const 3D DataContainer")
{
constexpr index_t size = 20;
IndexVector_t numCoeff(3);
numCoeff << size, size, size;
const VolumeDescriptor desc(numCoeff);
DataContainer<data_t> dc(desc);
dc = 0;
THEN("Accessing an out of bounds slice throws")
{
REQUIRE_THROWS_AS(dc.slice(20), LogicError);
}
WHEN("Setting the first slice to 1")
{
dc.slice(0) = 1;
THEN("Only the first slice is set to 1")
{
for (int j = 0; j < size; ++j) {
for (int i = 0; i < size; ++i) {
data_t val = dc(i, j, 0);
INFO("Expected slice 0 to be ", data_t{1}, " but it's ", val, " (at (", i,
", ", j, ", 0))");
REQUIRE_UNARY(checkApproxEq(val, 1));
}
}
}
THEN("The other slices are still set to 0")
{
for (int k = 1; k < size; ++k) {
for (int j = 0; j < size; ++j) {
for (int i = 0; i < size; ++i) {
data_t val = dc(i, j, k);
INFO("Expected all slices but the first to be ", data_t{0},
" but it's ", val, " (at (", i, ", ", j, ", 0))");
REQUIRE_UNARY(checkApproxEq(val, 0));
}
}
}
}
}
WHEN("Setting the fifth slice to some random data using a 3D DataContainer")
{
Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size * 1);
const DataContainer slice(VolumeDescriptor({size, size, 1}), randVec);
dc.slice(5) = slice;
THEN("The first 4 slices are still zero")
{
for (int k = 0; k < 5; ++k) {
for (int j = 0; j < size; ++j) {
for (int i = 0; i < size; ++i) {
data_t val = dc(i, j, k);
INFO("Expected all slices but the first to be ", data_t{0},
" but it's ", val, " (at (", i, ", ", j, ", 0))");
REQUIRE_UNARY(checkApproxEq(val, 0));
}
}
}
}
THEN("The fifth slices set correctly")
{
for (int j = 0; j < size; ++j) {
for (int i = 0; i < size; ++i) {
data_t val = dc(i, j, 5);
auto expected = randVec[i + j * size];
INFO("Expected slice 0 to be ", expected, " but it's ", val, " (at (", i,
", ", j, ", 0))");
REQUIRE_UNARY(checkApproxEq(val, expected));
}
}
}
THEN("The last 14 slices are still zero")
{
// Check last slices
for (int k = 6; k < size; ++k) {
for (int j = 0; j < size; ++j) {
for (int i = 0; i < size; ++i) {
data_t val = dc(i, j, k);
INFO("Expected all slices but the first to be ", data_t{0},
" but it's ", val, " (at (", i, ", ", j, ", 0))");
REQUIRE_UNARY(checkApproxEq(val, 0));
}
}
}
}
}
WHEN("Setting the first slice to some random data using a 2D DataContainer")
{
Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size);
const DataContainer slice(VolumeDescriptor({size, size}), randVec);
dc.slice(0) = slice;
THEN("The fifth slices set correctly")
{
for (int j = 0; j < size; ++j) {
for (int i = 0; i < size; ++i) {
data_t val = dc(i, j, 0);
auto expected = randVec[i + j * size];
INFO("Expected slice 0 to be ", expected, " but it's ", val, " (at (", i,
", ", j, ", 0))");
REQUIRE_UNARY(checkApproxEq(val, expected));
}
}
}
THEN("The other slices are still zero")
{
for (int k = 1; k < size; ++k) {
for (int j = 0; j < size; ++j) {
for (int i = 0; i < size; ++i) {
data_t val = dc(i, j, k);
INFO("Expected all slices but the first to be ", data_t{0},
" but it's ", val, " (at (", i, ", ", j, ", 0))");
REQUIRE_UNARY(checkApproxEq(val, 0));
}
}
}
}
}
}
}
// "instantiate" the test templates for CPU types
TEST_CASE_TEMPLATE_APPLY(datacontainer_construction, CPUTypeTuple);
TEST_CASE_TEMPLATE_APPLY(datacontainer_reduction, CPUTypeTuple);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment