1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! Interfaces and implementations specific to integration with `ndarray`.
//!
//! This module introduces the trait [`IntoNdArray`], which is implemented for
//! all NIfTI volume types and enable their mapping into an [`Array`] with a
//! dynamic number of dimensions and an abitrary element type. The affine
//! scaling of the values (from the `scl_slope` and `scl_inter` attributes) are
//! also considered in this transformation.
//!
//! A target [element type] needs to be provided at compile time, which is
//! usually the data type that the user wishes to work the array with. If the
//! source and target types do not match, each voxel is cast in a way as to
//! avoid loss of precision.
//!
//! #### Note on memory order
//!
//! NIfTI volumes are usually stored in disk in column major order (also called
//! Fortran order). As such, the array resulting from this operation will also
//! be in this memory order, rather than the usual row major order (AKA C
//! ordering). When accessing the array, one should consider any potential
//! bottlenecks emerging from this ordering in their data processing pipelines.
//! Namely, it might be faster to produce output arrays in column major order
//! as well.
//!
//! [`IntoNdArray`]: ./trait.IntoNdArray.html
//! [`Array`]: ../../../ndarray/type.Array.html
//! [element type]: ../element/trait.DataElement.html
//!
use crate::error::Result;
use crate::volume::element::DataElement;
use crate::volume::NiftiVolume;
use ndarray::{Array, Axis, Ix, IxDyn};
use num_traits::AsPrimitive;
use std::ops::{Add, Mul};

/// Trait for volumes which can be converted to an ndarray.
///
/// Please see the [module-level documentation](index.html) for more details.
pub trait IntoNdArray {

    /// Consume the volume into an ndarray with the same number of dimensions
    /// and the given target element type `T`.
    fn into_ndarray<T>(self) -> Result<Array<T, IxDyn>>
    where
        T: Mul<Output = T>,
        T: Add<Output = T>,
        T: DataElement,
        u8: AsPrimitive<T>,
        i8: AsPrimitive<T>,
        u16: AsPrimitive<T>,
        i16: AsPrimitive<T>,
        u32: AsPrimitive<T>,
        i32: AsPrimitive<T>,
        u64: AsPrimitive<T>,
        i64: AsPrimitive<T>,
        f32: AsPrimitive<T>,
        f64: AsPrimitive<T>;
}

impl<V> IntoNdArray for super::SliceView<V>
where
    V: NiftiVolume + IntoNdArray,
{
    fn into_ndarray<T>(self) -> Result<Array<T, IxDyn>>
    where
        T: Mul<Output = T>,
        T: Add<Output = T>,
        T: DataElement,
        u8: AsPrimitive<T>,
        i8: AsPrimitive<T>,
        u16: AsPrimitive<T>,
        i16: AsPrimitive<T>,
        u32: AsPrimitive<T>,
        i32: AsPrimitive<T>,
        u64: AsPrimitive<T>,
        i64: AsPrimitive<T>,
        f32: AsPrimitive<T>,
        f64: AsPrimitive<T>,
    {
        // TODO optimize this implementation (we don't need the whole volume)
        let volume = self.volume.into_ndarray()?;
        Ok(volume.into_subview(Axis(self.axis as Ix), self.index as usize))
    }
}