-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor volume stuff + add Sliceable trait
- Loading branch information
Showing
6 changed files
with
150 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
target | ||
Cargo.lock | ||
/nifti1.h | ||
/nifti1.h | ||
/.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
//! This module defines the voxel volume API, as well as data | ||
//! types for reading volumes from files. | ||
//! An integration with `ndarray` allows for more elegant and | ||
//! efficient approaches, and should be preferred when possible. | ||
//! In order to do so, you must add the `ndarray_volumes` feature | ||
//! to this crate. | ||
pub mod inmem; | ||
pub use self::inmem::*; | ||
mod util; | ||
use error::Result; | ||
use typedef::NiftiType; | ||
|
||
#[cfg(feature = "ndarray_volumes")] use ndarray::{Array, Ix, IxDyn, ShapeBuilder}; | ||
#[cfg(feature = "ndarray_volumes")] use std::ops::{Add, Mul}; | ||
#[cfg(feature = "ndarray_volumes")] use num::Num; | ||
|
||
/// Public API for NIFTI volume data, exposed as a multi-dimensional | ||
/// voxel array. | ||
/// | ||
/// This API is currently experimental and will likely be subjected to | ||
/// various changes and additions in future versions. | ||
pub trait NiftiVolume { | ||
/// Get the dimensions of the volume. Unlike how NIFTI-1 | ||
/// stores dimensions, the returned slice does not include | ||
/// `dim[0]` and is clipped to the effective number of dimensions. | ||
fn dim(&self) -> &[u16]; | ||
|
||
/// Get the volume's number of dimensions. In a fully compliant file, | ||
/// this is equivalent to the corresponding header's `dim[0]` field | ||
/// (with byte swapping already applied). | ||
fn dimensionality(&self) -> usize { | ||
self.dim().len() | ||
} | ||
|
||
/// Fetch a single voxel's value in the given voxel index coordinates | ||
/// as a double precision floating point value. | ||
/// All necessary conversions and transformations are made | ||
/// when reading the voxel, including scaling. Note that using this | ||
/// function continuously to traverse the volume is inefficient. | ||
/// Prefer using iterators or the `ndarray` API for volume traversal. | ||
/// | ||
/// # Errors | ||
/// | ||
/// - `NiftiError::OutOfBounds` if the given coordinates surpass this | ||
/// volume's boundaries. | ||
fn get_f64(&self, coords: &[u16]) -> Result<f64>; | ||
|
||
/// Get this volume's data type. | ||
fn data_type(&self) -> NiftiType; | ||
|
||
/// Fetch a single voxel's value in the given voxel index coordinates | ||
/// as a single precision floating point value. | ||
/// All necessary conversions and transformations are made | ||
/// when reading the voxel, including scaling. Note that using this | ||
/// function continuously to traverse the volume is inefficient. | ||
/// Prefer using iterators or the `ndarray` API for volume traversal. | ||
/// | ||
/// # Errors | ||
/// | ||
/// - `NiftiError::OutOfBounds` if the given coordinates surpass this | ||
/// volume's boundaries. | ||
fn get_f32(&self, coords: &[u16]) -> Result<f32> { | ||
let v = self.get_f64(coords)?; | ||
Ok(v as f32) | ||
} | ||
} | ||
|
||
/// Interface for a volume that can be sliced. | ||
pub trait Sliceable { | ||
/// The type of the resulting slice, which is also a volume. | ||
type Slice: NiftiVolume; | ||
|
||
/// Obtain a slice of the volume over a certain axis, yielding a | ||
/// volume of N-1 dimensions. | ||
fn get_slice(&self, axis: u16, index: u16) -> Result<Self::Slice>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
//! Miscellaneous volume-related functions | ||
use error::{Result, NiftiError}; | ||
|
||
pub fn coords_to_index(coords: &[u16], dim: &[u16]) -> Result<usize> { | ||
if coords.len() != dim.len() || coords.is_empty() { | ||
return Err(NiftiError::IncorrectVolumeDimensionality( | ||
dim.len() as u16, | ||
coords.len() as u16 | ||
)) | ||
} | ||
|
||
if !coords.iter().zip(dim).all(|(i, d)| { | ||
*i < (*d) as u16 | ||
}) { | ||
return Err(NiftiError::OutOfBounds(Vec::from(coords))); | ||
} | ||
|
||
let mut crds = coords.into_iter(); | ||
let start = *crds.next_back().unwrap() as usize; | ||
let index = crds.zip(dim).rev() | ||
.fold(start, |a, b| { | ||
a * *b.1 as usize + *b.0 as usize | ||
}); | ||
|
||
Ok(index) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::coords_to_index; | ||
|
||
#[test] | ||
fn test_coords_to_index() { | ||
assert!(coords_to_index(&[0, 0], &[10, 10, 5]).is_err()); | ||
assert!(coords_to_index(&[0, 0, 0, 0], &[10, 10, 5]).is_err()); | ||
assert_eq!( | ||
coords_to_index(&[0, 0, 0], &[10, 10, 5]).unwrap(), | ||
0 | ||
); | ||
|
||
assert_eq!( | ||
coords_to_index(&[1, 0, 0], &[16, 16, 3]).unwrap(), | ||
1 | ||
); | ||
assert_eq!( | ||
coords_to_index(&[0, 1, 0], &[16, 16, 3]).unwrap(), | ||
16 | ||
); | ||
assert_eq!( | ||
coords_to_index(&[0, 0, 1], &[16, 16, 3]).unwrap(), | ||
256 | ||
); | ||
assert_eq!( | ||
coords_to_index(&[1, 1, 1], &[16, 16, 3]).unwrap(), | ||
273 | ||
); | ||
|
||
assert_eq!( | ||
coords_to_index(&[15, 15, 2], &[16, 16, 3]).unwrap(), | ||
16 * 16 * 3 - 1 | ||
); | ||
|
||
assert!(coords_to_index(&[16, 15, 2], &[16, 16, 3]).is_err()); | ||
} | ||
} |