- implement shape in Rust
- implement more readers - fix downloading of bioformats jar - (mostly) compatible with python version
This commit is contained in:
1 parent
705ca16379
commit
ff7cd562af
36 files changed
+7946
-2843
No files matched your search
+418
-160
@@ -1,7 +1,7 @@
|
||||
use crate::axes::{Ax, Axis, Operation, Slice, SliceInfoElemDef, slice_info};
|
||||
use crate::axes::{Ax, Axis, Operation, Shape, Slice, SliceInfoElemDef, slice_info};
|
||||
use crate::error::Error;
|
||||
use crate::metadata::Metadata;
|
||||
use crate::reader::Reader;
|
||||
use crate::readers::{Dimensions, DynReader, Reader};
|
||||
use crate::stats::MinMax;
|
||||
use indexmap::IndexMap;
|
||||
use itertools::{Itertools, iproduct};
|
||||
@@ -14,13 +14,13 @@ use num::{Bounded, FromPrimitive, ToPrimitive, Zero};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
use std::any::type_name;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::Sum;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{AddAssign, Deref, Div};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::path::Path;
|
||||
|
||||
fn idx_bnd(idx: isize, bnd: isize) -> Result<isize, Error> {
|
||||
if idx < -bnd {
|
||||
@@ -64,9 +64,9 @@ impl<T> Number for T where
|
||||
|
||||
/// sliceable view on an image file
|
||||
#[serde_as]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct View<D: Dimension> {
|
||||
reader: Arc<Reader>,
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct View<D: Dimension, R: Reader = DynReader> {
|
||||
reader: R,
|
||||
/// same order as axes
|
||||
#[serde_as(as = "Vec<SliceInfoElemDef>")]
|
||||
slice: Vec<SliceInfoElem>,
|
||||
@@ -76,8 +76,24 @@ pub struct View<D: Dimension> {
|
||||
dimensionality: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D: Dimension> View<D> {
|
||||
pub(crate) fn new(reader: Arc<Reader>, slice: Vec<SliceInfoElem>, axes: Vec<Axis>) -> Self {
|
||||
impl<D, R> Hash for View<D, R>
|
||||
where
|
||||
D: Dimension,
|
||||
R: Reader,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.reader.hash(state);
|
||||
self.slice.hash(state);
|
||||
self.axes.hash(state);
|
||||
for (ax, op) in self.operations.iter() {
|
||||
ax.hash(state);
|
||||
op.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dimension, R: Reader> View<D, R> {
|
||||
pub(crate) fn new(reader: R, slice: Vec<SliceInfoElem>, axes: Vec<Axis>) -> Self {
|
||||
Self {
|
||||
reader,
|
||||
slice,
|
||||
@@ -88,33 +104,34 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new_with_axes(reader: Arc<Reader>, axes: Vec<Axis>) -> Result<Self, Error> {
|
||||
pub(crate) fn new_with_axes(reader: R, axes: Vec<Axis>) -> Result<Self, Error> {
|
||||
let mut slice = Vec::new();
|
||||
let shape = reader.shape();
|
||||
for axis in axes.iter() {
|
||||
match axis {
|
||||
Axis::C => slice.push(SliceInfoElem::Slice {
|
||||
start: 0,
|
||||
end: Some(reader.size_c as isize),
|
||||
end: Some(shape.c as isize),
|
||||
step: 1,
|
||||
}),
|
||||
Axis::Z => slice.push(SliceInfoElem::Slice {
|
||||
start: 0,
|
||||
end: Some(reader.size_z as isize),
|
||||
end: Some(shape.z as isize),
|
||||
step: 1,
|
||||
}),
|
||||
Axis::T => slice.push(SliceInfoElem::Slice {
|
||||
start: 0,
|
||||
end: Some(reader.size_t as isize),
|
||||
end: Some(shape.t as isize),
|
||||
step: 1,
|
||||
}),
|
||||
Axis::Y => slice.push(SliceInfoElem::Slice {
|
||||
start: 0,
|
||||
end: Some(reader.size_y as isize),
|
||||
end: Some(shape.y as isize),
|
||||
step: 1,
|
||||
}),
|
||||
Axis::X => slice.push(SliceInfoElem::Slice {
|
||||
start: 0,
|
||||
end: Some(reader.size_x as isize),
|
||||
end: Some(shape.x as isize),
|
||||
step: 1,
|
||||
}),
|
||||
Axis::New => {
|
||||
@@ -126,11 +143,11 @@ impl<D: Dimension> View<D> {
|
||||
for axis in [Axis::C, Axis::Z, Axis::T, Axis::Y, Axis::X] {
|
||||
if !axes.contains(&axis) {
|
||||
let size = match axis {
|
||||
Axis::C => reader.size_c,
|
||||
Axis::Z => reader.size_z,
|
||||
Axis::T => reader.size_t,
|
||||
Axis::Y => reader.size_y,
|
||||
Axis::X => reader.size_x,
|
||||
Axis::C => shape.c,
|
||||
Axis::Z => shape.z,
|
||||
Axis::T => shape.t,
|
||||
Axis::Y => shape.y,
|
||||
Axis::X => shape.x,
|
||||
Axis::New => 1,
|
||||
};
|
||||
if size > 1 {
|
||||
@@ -150,13 +167,13 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
|
||||
/// the file path
|
||||
pub fn path(&self) -> &PathBuf {
|
||||
&self.reader.path
|
||||
pub fn path(&self) -> &Path {
|
||||
self.reader.path()
|
||||
}
|
||||
|
||||
/// the series in the file
|
||||
pub fn series(&self) -> usize {
|
||||
self.reader.series
|
||||
self.reader.series()
|
||||
}
|
||||
|
||||
fn with_operations(mut self, operations: IndexMap<Axis, Operation>) -> Self {
|
||||
@@ -165,7 +182,7 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
|
||||
/// change the dimension into a dynamic dimension
|
||||
pub fn into_dyn(self) -> View<IxDyn> {
|
||||
pub fn into_dyn(self) -> View<IxDyn, R> {
|
||||
View {
|
||||
reader: self.reader,
|
||||
slice: self.slice,
|
||||
@@ -176,7 +193,7 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
|
||||
/// change the dimension into a concrete dimension
|
||||
pub fn into_dimensionality<D2: Dimension>(self) -> Result<View<D2>, Error> {
|
||||
pub fn into_dimensionality<D2: Dimension>(self) -> Result<View<D2, R>, Error> {
|
||||
if let Some(d) = D2::NDIM {
|
||||
if d == self.ndim() {
|
||||
Ok(View {
|
||||
@@ -231,7 +248,7 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
|
||||
/// remove axes of size 1
|
||||
pub fn squeeze(&self) -> Result<View<IxDyn>, Error> {
|
||||
pub fn squeeze(&self) -> Result<View<IxDyn, R>, Error> {
|
||||
let view = self.clone().into_dyn();
|
||||
let slice: Vec<_> = self
|
||||
.shape()
|
||||
@@ -286,14 +303,15 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
|
||||
/// the shape of the view
|
||||
pub fn shape(&self) -> Vec<usize> {
|
||||
let mut shape = Vec::<usize>::new();
|
||||
pub fn shape(&self) -> Shape {
|
||||
let mut shape = Shape::new();
|
||||
for (ax, s) in self.axes.iter().zip(self.slice.iter()) {
|
||||
match s {
|
||||
SliceInfoElem::Slice { start, end, step } => {
|
||||
if !self.operations.contains_key(ax) {
|
||||
if let Some(e) = end {
|
||||
shape.push(((e - start).max(0) / step) as usize);
|
||||
shape.order.push(*ax);
|
||||
shape.set_axis(ax, ((e - start).max(0) / step) as usize);
|
||||
} else {
|
||||
panic!("slice has no end")
|
||||
}
|
||||
@@ -302,7 +320,7 @@ impl<D: Dimension> View<D> {
|
||||
SliceInfoElem::Index(_) => {}
|
||||
SliceInfoElem::NewAxis => {
|
||||
if !self.operations.contains_key(ax) {
|
||||
shape.push(1);
|
||||
shape.order.push(*ax);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,60 +328,6 @@ impl<D: Dimension> View<D> {
|
||||
shape
|
||||
}
|
||||
|
||||
pub fn size_of(&self, axis: Axis) -> usize {
|
||||
if let Some(axis_position) = self.axes.iter().position(|a| *a == axis) {
|
||||
match self.slice[axis_position] {
|
||||
SliceInfoElem::Slice { start, end, step } => {
|
||||
if let Some(e) = end {
|
||||
((e - start).max(0) / step) as usize
|
||||
} else {
|
||||
panic!("slice has no end")
|
||||
}
|
||||
}
|
||||
_ => 1,
|
||||
}
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size_c(&self) -> usize {
|
||||
self.size_of(Axis::C)
|
||||
}
|
||||
|
||||
pub fn size_z(&self) -> usize {
|
||||
self.size_of(Axis::Z)
|
||||
}
|
||||
|
||||
pub fn size_t(&self) -> usize {
|
||||
self.size_of(Axis::T)
|
||||
}
|
||||
|
||||
pub fn size_y(&self) -> usize {
|
||||
self.size_of(Axis::Y)
|
||||
}
|
||||
|
||||
pub fn size_x(&self) -> usize {
|
||||
self.size_of(Axis::X)
|
||||
}
|
||||
|
||||
fn shape_all(&self) -> Vec<usize> {
|
||||
let mut shape = Vec::<usize>::new();
|
||||
for s in self.slice.iter() {
|
||||
match s {
|
||||
SliceInfoElem::Slice { start, end, step } => {
|
||||
if let Some(e) = end {
|
||||
shape.push(((e - start).max(0) / step) as usize);
|
||||
} else {
|
||||
panic!("slice has no end")
|
||||
}
|
||||
}
|
||||
_ => shape.push(1),
|
||||
}
|
||||
}
|
||||
shape
|
||||
}
|
||||
|
||||
/// swap two axes
|
||||
pub fn swap_axes<A: Ax>(&self, axis0: A, axis1: A) -> Result<Self, Error> {
|
||||
let idx0 = axis0.pos_op(&self.axes, &self.slice, &self.op_axes())?;
|
||||
@@ -392,6 +356,37 @@ impl<D: Dimension> View<D> {
|
||||
Ok(View::new(self.reader.clone(), slice, axes).with_operations(self.operations.clone()))
|
||||
}
|
||||
|
||||
/// subset of gives axes will be reordered in given order, only axes in axes will be returned
|
||||
pub fn permute_axes_dyn<A: Ax>(&self, axes: &[A]) -> Result<View<IxDyn, R>, Error> {
|
||||
let idx: Vec<usize> = axes
|
||||
.iter()
|
||||
.map(|a| a.pos_op(&self.axes, &self.slice, &self.op_axes()).unwrap())
|
||||
.collect();
|
||||
let mut jdx = idx.clone();
|
||||
jdx.sort();
|
||||
let mut new_slice = self.slice.to_vec();
|
||||
let mut new_axes = self.axes.clone();
|
||||
let axes = axes.iter().map(|a| a.n()).collect::<HashSet<_>>();
|
||||
for (&i, j) in idx.iter().zip(jdx) {
|
||||
new_slice[j] = self.slice[i];
|
||||
new_axes[j] = self.axes[i];
|
||||
}
|
||||
for (ax, s) in new_axes.iter().zip(new_slice.iter_mut()) {
|
||||
if let SliceInfoElem::Slice { start, end, step } = s
|
||||
&& !axes.contains(&ax.n())
|
||||
{
|
||||
let size = ((end.expect("slice has no end") - *start).max(0) / *step) as usize;
|
||||
if size != 1 {
|
||||
return Err(Error::SizeMismatch(ax.to_string(), size));
|
||||
}
|
||||
*s = SliceInfoElem::Index(*start)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(View::new(self.reader.clone(), new_slice, new_axes)
|
||||
.with_operations(self.operations.clone()))
|
||||
}
|
||||
|
||||
/// reverse the order of the axes
|
||||
pub fn transpose(&self) -> Result<Self, Error> {
|
||||
Ok(View::new(
|
||||
@@ -402,7 +397,11 @@ impl<D: Dimension> View<D> {
|
||||
.with_operations(self.operations.clone()))
|
||||
}
|
||||
|
||||
fn operate<A: Ax>(&self, axis: A, operation: Operation) -> Result<View<D::Smaller>, Error> {
|
||||
pub fn operate<A: Ax>(
|
||||
&self,
|
||||
axis: A,
|
||||
operation: Operation,
|
||||
) -> Result<View<D::Smaller, R>, Error> {
|
||||
let pos = axis.pos_op(&self.axes, &self.slice, &self.op_axes())?;
|
||||
let ax = self.axes[pos];
|
||||
let (axes, slice, operations) = if Axis::New == ax {
|
||||
@@ -430,27 +429,27 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
|
||||
/// maximum along axis
|
||||
pub fn max_proj<A: Ax>(&self, axis: A) -> Result<View<D::Smaller>, Error> {
|
||||
pub fn max_proj<A: Ax>(&self, axis: A) -> Result<View<D::Smaller, R>, Error> {
|
||||
self.operate(axis, Operation::Max)
|
||||
}
|
||||
|
||||
/// minimum along axis
|
||||
pub fn min_proj<A: Ax>(&self, axis: A) -> Result<View<D::Smaller>, Error> {
|
||||
pub fn min_proj<A: Ax>(&self, axis: A) -> Result<View<D::Smaller, R>, Error> {
|
||||
self.operate(axis, Operation::Min)
|
||||
}
|
||||
|
||||
/// sum along axis
|
||||
pub fn sum_proj<A: Ax>(&self, axis: A) -> Result<View<D::Smaller>, Error> {
|
||||
pub fn sum_proj<A: Ax>(&self, axis: A) -> Result<View<D::Smaller, R>, Error> {
|
||||
self.operate(axis, Operation::Sum)
|
||||
}
|
||||
|
||||
/// mean along axis
|
||||
pub fn mean_proj<A: Ax>(&self, axis: A) -> Result<View<D::Smaller>, Error> {
|
||||
pub fn mean_proj<A: Ax>(&self, axis: A) -> Result<View<D::Smaller, R>, Error> {
|
||||
self.operate(axis, Operation::Mean)
|
||||
}
|
||||
|
||||
/// created a new sliced view
|
||||
pub fn slice<I>(&self, info: I) -> Result<View<I::OutDim>, Error>
|
||||
pub fn slice<I>(&self, info: I) -> Result<View<I::OutDim, R>, Error>
|
||||
where
|
||||
I: SliceArg<D>,
|
||||
{
|
||||
@@ -554,11 +553,7 @@ impl<D: Dimension> View<D> {
|
||||
new_axes.push(*a.expect("axis should exist when slice exists"));
|
||||
r_idx += 1;
|
||||
}
|
||||
_ => {
|
||||
panic!("unreachable");
|
||||
// n_idx += 1;
|
||||
// r_idx += 1;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -575,7 +570,7 @@ impl<D: Dimension> View<D> {
|
||||
|
||||
/// resets axes to cztyx order, with all 5 axes present,
|
||||
/// inserts new axes in place of axes under operation (max_proj etc.)
|
||||
pub fn reset_axes(&self) -> Result<View<Ix5>, Error> {
|
||||
pub fn reset_axes(&self) -> Result<View<Ix5, R>, Error> {
|
||||
let mut axes = Vec::new();
|
||||
let mut slice = Vec::new();
|
||||
|
||||
@@ -603,7 +598,7 @@ impl<D: Dimension> View<D> {
|
||||
|
||||
/// slice, but slice elements are in cztyx order, all cztyx must be given,
|
||||
/// but axes not present in view will be ignored, view axes are reordered in cztyx order
|
||||
pub fn slice_cztyx<I>(&self, info: I) -> Result<View<I::OutDim>, Error>
|
||||
pub fn slice_cztyx<I>(&self, info: I) -> Result<View<I::OutDim, R>, Error>
|
||||
where
|
||||
I: SliceArg<Ix5>,
|
||||
{
|
||||
@@ -644,14 +639,14 @@ impl<D: Dimension> View<D> {
|
||||
Array2<T>: MinMax<Output = Array1<T>>,
|
||||
{
|
||||
let mut op_xy = IndexMap::new();
|
||||
if let Some((&ax, op)) = self.operations.first() {
|
||||
if (ax == Axis::X) || (ax == Axis::Y) {
|
||||
op_xy.insert(ax, op.clone());
|
||||
if let Some((&ax2, op2)) = self.operations.get_index(1) {
|
||||
if (ax2 == Axis::X) || (ax2 == Axis::Y) {
|
||||
op_xy.insert(ax2, op2.clone());
|
||||
}
|
||||
}
|
||||
if let Some((&ax, op)) = self.operations.first()
|
||||
&& ((ax == Axis::X) || (ax == Axis::Y))
|
||||
{
|
||||
op_xy.insert(ax, op.clone());
|
||||
if let Some((&ax2, op2)) = self.operations.get_index(1)
|
||||
&& ((ax2 == Axis::X) || (ax2 == Axis::Y))
|
||||
{
|
||||
op_xy.insert(ax2, op2.clone());
|
||||
}
|
||||
}
|
||||
let op_czt = if let Some((&ax, op)) = self.operations.get_index(op_xy.len()) {
|
||||
@@ -686,33 +681,27 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
let mut slice_reader = vec![Slice::empty(); 5];
|
||||
let mut xy_dim = 0usize;
|
||||
let shape = [
|
||||
self.size_c as isize,
|
||||
self.size_z as isize,
|
||||
self.size_t as isize,
|
||||
self.size_y as isize,
|
||||
self.size_x as isize,
|
||||
];
|
||||
let shape_reader = self.reader.shape();
|
||||
for (s, &axis) in self.slice.iter().zip(&self.axes) {
|
||||
match axis {
|
||||
Axis::New => {}
|
||||
_ => match s {
|
||||
SliceInfoElem::Slice { start, end, step } => {
|
||||
if let Axis::X | Axis::Y = axis {
|
||||
if !op_xy.contains_key(&axis) {
|
||||
xy_dim += 1;
|
||||
}
|
||||
if let Axis::X | Axis::Y = axis
|
||||
&& !op_xy.contains_key(&axis)
|
||||
{
|
||||
xy_dim += 1;
|
||||
}
|
||||
slice_reader[axis as usize] = Slice::new(
|
||||
idx_bnd(*start, shape[axis as usize])?,
|
||||
slc_bnd(end.unwrap(), shape[axis as usize])?,
|
||||
idx_bnd(*start, shape_reader[axis] as isize)?,
|
||||
slc_bnd(end.unwrap(), shape_reader[axis] as isize)?,
|
||||
*step,
|
||||
);
|
||||
}
|
||||
SliceInfoElem::Index(j) => {
|
||||
slice_reader[axis as usize] = Slice::new(
|
||||
idx_bnd(*j, shape[axis as usize])?,
|
||||
slc_bnd(*j + 1, shape[axis as usize])?,
|
||||
idx_bnd(*j, shape_reader[axis] as isize)?,
|
||||
slc_bnd(*j + 1, shape_reader[axis] as isize)?,
|
||||
1,
|
||||
);
|
||||
}
|
||||
@@ -737,10 +726,7 @@ impl<D: Dimension> View<D> {
|
||||
} else {
|
||||
ArrayD::<T>::zeros(shape_out.into_dimension())
|
||||
};
|
||||
let size_c = self.reader.size_c as isize;
|
||||
let size_z = self.reader.size_z as isize;
|
||||
let size_t = self.reader.size_t as isize;
|
||||
|
||||
let shape = self.reader.shape();
|
||||
let mut axes_out_idx = [None; 5];
|
||||
for (i, ax) in ax_out.iter().enumerate() {
|
||||
if *ax < Axis::New {
|
||||
@@ -759,9 +745,9 @@ impl<D: Dimension> View<D> {
|
||||
slice[i] = SliceInfoElem::Index(t)
|
||||
};
|
||||
let frame = self.reader.get_frame(
|
||||
(c % size_c) as usize,
|
||||
(z % size_z) as usize,
|
||||
(t % size_t) as usize,
|
||||
(c % shape.c as isize) as usize,
|
||||
(z % shape.z as isize) as usize,
|
||||
(t % shape.t as isize) as usize,
|
||||
)?;
|
||||
|
||||
let arr_frame: Array2<T> = frame.try_into()?;
|
||||
@@ -860,11 +846,11 @@ impl<D: Dimension> View<D> {
|
||||
}
|
||||
}
|
||||
let mut n = 1;
|
||||
for (&ax, size) in self.axes.iter().zip(self.shape_all().iter()) {
|
||||
if let Some(Operation::Mean) = self.operations.get(&ax) {
|
||||
if (ax == Axis::C) || (ax == Axis::Z) || (ax == Axis::T) {
|
||||
n *= size;
|
||||
}
|
||||
for (ax, size) in self.shape().to_hashmap().into_iter() {
|
||||
if ((ax == Axis::C) || (ax == Axis::Z) || (ax == Axis::T))
|
||||
&& let Some(Operation::Mean) = self.operations.get(&ax)
|
||||
{
|
||||
n *= size;
|
||||
}
|
||||
}
|
||||
let array = if n == 1 {
|
||||
@@ -999,9 +985,10 @@ impl<D: Dimension> View<D> {
|
||||
/// gives a helpful summary of the recorded experiment
|
||||
pub fn summary(&self) -> Result<String, Error> {
|
||||
let mut s = "".to_string();
|
||||
s.push_str(&format!("path/filename: {}\n", self.path.display()));
|
||||
s.push_str(&format!("series/pos: {}\n", self.series));
|
||||
s.push_str(&format!("dtype: {:?}\n", self.pixel_type));
|
||||
s.push_str(&format!("path/filename: {}\n", self.path().display()));
|
||||
s.push_str(&format!("series/pos: {}\n", self.series()));
|
||||
s.push_str(&format!("reader: {}\n", self.reader_name()));
|
||||
s.push_str(&format!("dtype: {:?}\n", self.pixel_type()));
|
||||
let axes = self
|
||||
.axes()
|
||||
.into_iter()
|
||||
@@ -1015,45 +1002,47 @@ impl<D: Dimension> View<D> {
|
||||
.join(" x ");
|
||||
let space = " ".repeat(6usize.saturating_sub(axes.len()));
|
||||
s.push_str(&format!("shape ({}):{}{}\n", axes, space, shape));
|
||||
s.push_str(&self.get_ome()?.summary()?);
|
||||
s.push_str(&self.metadata()?.summary()?);
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dimension> Deref for View<D> {
|
||||
type Target = Reader;
|
||||
impl<D: Dimension, R: Reader> Deref for View<D, R> {
|
||||
type Target = R;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.reader.as_ref()
|
||||
&self.reader
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> TryFrom<View<D>> for Array<T, D>
|
||||
impl<T, D, R> TryFrom<View<D, R>> for Array<T, D>
|
||||
where
|
||||
T: Number,
|
||||
D: Dimension,
|
||||
R: Reader,
|
||||
ArrayD<T>: MinMax<Output = ArrayD<T>>,
|
||||
Array1<T>: MinMax<Output = Array0<T>>,
|
||||
Array2<T>: MinMax<Output = Array1<T>>,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(view: View<D>) -> Result<Self, Self::Error> {
|
||||
fn try_from(view: View<D, R>) -> Result<Self, Self::Error> {
|
||||
view.as_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> TryFrom<&View<D>> for Array<T, D>
|
||||
impl<T, D, R> TryFrom<&View<D, R>> for Array<T, D>
|
||||
where
|
||||
T: Number,
|
||||
D: Dimension,
|
||||
R: Reader,
|
||||
ArrayD<T>: MinMax<Output = ArrayD<T>>,
|
||||
Array1<T>: MinMax<Output = Array0<T>>,
|
||||
Array2<T>: MinMax<Output = Array1<T>>,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(view: &View<D>) -> Result<Self, Self::Error> {
|
||||
fn try_from(view: &View<D, R>) -> Result<Self, Self::Error> {
|
||||
view.as_array()
|
||||
}
|
||||
}
|
||||
@@ -1068,26 +1057,17 @@ pub trait Item {
|
||||
Array2<T>: MinMax<Output = Array1<T>>;
|
||||
}
|
||||
|
||||
impl View<Ix5> {
|
||||
pub fn from_path<P>(path: P, series: usize) -> Result<Self, Error>
|
||||
impl<R: Reader> View<Ix5, R> {
|
||||
pub fn from_path<P>(path: P) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut path = path.as_ref().to_path_buf();
|
||||
if path.is_dir() {
|
||||
for file in path.read_dir()?.flatten() {
|
||||
let p = file.path();
|
||||
if file.path().is_file() && (p.extension() == Some("tif".as_ref())) {
|
||||
path = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Reader::new(path, series)?.view())
|
||||
let (path, dimensions) = Dimensions::parse_path(path)?;
|
||||
Ok(R::new(path, dimensions.s.unwrap_or(0), dimensions.p.unwrap_or(0))?.view())
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for View<Ix0> {
|
||||
impl<R: Reader> Item for View<Ix0, R> {
|
||||
fn item<T>(&self) -> Result<T, Error>
|
||||
where
|
||||
T: Number,
|
||||
@@ -1099,12 +1079,12 @@ impl Item for View<Ix0> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dimension> Display for View<D> {
|
||||
impl<D: Dimension, R: Reader> Display for View<D, R> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if let Ok(summary) = self.summary() {
|
||||
write!(f, "{}", summary)
|
||||
} else {
|
||||
write!(f, "{}", self.path.display())
|
||||
write!(f, "{}", self.path().display())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1131,3 +1111,281 @@ macro_rules! to_bytes_vec_impl {
|
||||
to_bytes_vec_impl!(
|
||||
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::axes::Axis;
|
||||
use crate::error::Error;
|
||||
use crate::readers::{DynReader, Reader};
|
||||
use crate::stats::MinMax;
|
||||
use crate::view::Item;
|
||||
use ndarray::{Array, Array4, Array5, NewAxis};
|
||||
use ndarray::{Array2, s};
|
||||
|
||||
fn open(file: &str) -> Result<DynReader, Error> {
|
||||
let path = std::env::current_dir()?
|
||||
.join("tests")
|
||||
.join("files")
|
||||
.join(file);
|
||||
DynReader::new(&path, 0, 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view() -> Result<(), Error> {
|
||||
let file = "tiffseq/YTL1841B2-2-1_1hr_DMSO_galinduction_1";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view();
|
||||
println!("view: {:?}", view);
|
||||
let a = view.slice(s![0, 5, 0, .., ..])?;
|
||||
println!("a: {:?}", a);
|
||||
let b = reader.get_frame(0, 5, 0)?;
|
||||
let c: Array2<isize> = a.try_into()?;
|
||||
println!("c {:?}", c);
|
||||
let d: Array2<isize> = b.try_into()?;
|
||||
println!("d {:?}", d);
|
||||
assert_eq!(c, d);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_shape() -> Result<(), Error> {
|
||||
let file = "tiffseq/YTL1841B2-2-1_1hr_DMSO_galinduction_1";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view();
|
||||
let a = view.slice(s![0, ..5, 0, .., 100..200])?;
|
||||
let shape = a.shape();
|
||||
assert_eq!(shape.to_vec(), vec![5, 1024, 100]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_new_axis() -> Result<(), Error> {
|
||||
let file = "tiffseq/YTL1841B2-2-1_1hr_DMSO_galinduction_1";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view();
|
||||
let a = Array5::<u8>::zeros((1, 9, 1, 1024, 1024));
|
||||
let a = a.slice(s![0, ..5, 0, NewAxis, 100..200, ..]);
|
||||
let v = view.slice(s![0, ..5, 0, NewAxis, 100..200, ..])?;
|
||||
assert_eq!(v.shape().to_vec(), a.shape());
|
||||
let a = a.slice(s![NewAxis, .., .., NewAxis, .., .., NewAxis]);
|
||||
let v = v.slice(s![NewAxis, .., .., NewAxis, .., .., NewAxis])?;
|
||||
assert_eq!(v.shape().to_vec(), a.shape());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_permute_axes() -> Result<(), Error> {
|
||||
let file = "tiffseq/YTL1841B2-2-1_1hr_DMSO_galinduction_1";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view();
|
||||
let s = view.shape();
|
||||
let mut a = Array5::<u8>::zeros((s[0], s[1], s[2], s[3], s[4]));
|
||||
assert_eq!(view.shape().to_vec(), a.shape());
|
||||
let b: Array5<usize> = view.clone().try_into()?;
|
||||
assert_eq!(b.shape(), a.shape());
|
||||
|
||||
let view = view.swap_axes(Axis::C, Axis::Z)?;
|
||||
a.swap_axes(0, 1);
|
||||
assert_eq!(view.shape().to_vec(), a.shape());
|
||||
let b: Array5<usize> = view.clone().try_into()?;
|
||||
assert_eq!(b.shape(), a.shape());
|
||||
let view = view.permute_axes(&[Axis::X, Axis::Z, Axis::Y])?;
|
||||
let a = a.permuted_axes([4, 1, 2, 0, 3]);
|
||||
assert_eq!(view.shape().to_vec(), a.shape());
|
||||
let b: Array5<usize> = view.clone().try_into()?;
|
||||
assert_eq!(b.shape(), a.shape());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! test_max {
|
||||
($($name:ident: $b:expr $(,)?)*) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() -> Result<(), Error> {
|
||||
let file = "tiffseq/YTL1841B2-2-1_1hr_DMSO_galinduction_1";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view();
|
||||
let array: Array5<usize> = view.clone().try_into()?;
|
||||
let view = view.max_proj($b)?;
|
||||
let a: Array4<usize> = view.clone().try_into()?;
|
||||
let b = array.max($b)?;
|
||||
assert_eq!(a.shape(), b.shape());
|
||||
assert_eq!(a, b);
|
||||
Ok(())
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
test_max! {
|
||||
max_c: 0
|
||||
max_z: 1
|
||||
max_t: 2
|
||||
max_y: 3
|
||||
max_x: 4
|
||||
}
|
||||
|
||||
macro_rules! test_index {
|
||||
($($name:ident: $b:expr $(,)?)*) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() -> Result<(), Error> {
|
||||
let file = "tiffseq/YTL1841B2-2-1_1hr_DMSO_galinduction_1";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view();
|
||||
let v4: Array<usize, _> = view.slice($b)?.try_into()?;
|
||||
let a5: Array5<usize> = reader.view().try_into()?;
|
||||
let a4 = a5.slice($b).to_owned();
|
||||
assert_eq!(a4, v4);
|
||||
Ok(())
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
test_index! {
|
||||
index_0: s![.., .., .., .., ..]
|
||||
index_1: s![0, .., .., .., ..]
|
||||
index_2: s![.., 0, .., .., ..]
|
||||
index_3: s![.., .., 0, .., ..]
|
||||
index_4: s![.., .., .., 0, ..]
|
||||
index_5: s![.., .., .., .., 0]
|
||||
index_6: s![0, 0, .., .., ..]
|
||||
index_7: s![0, .., 0, .., ..]
|
||||
index_8: s![0, .., .., 0, ..]
|
||||
index_9: s![0, .., .., .., 0]
|
||||
index_a: s![.., 0, 0, .., ..]
|
||||
index_b: s![.., 0, .., 0, ..]
|
||||
index_c: s![.., 0, .., .., 0]
|
||||
index_d: s![.., .., 0, 0, ..]
|
||||
index_e: s![.., .., 0, .., 0]
|
||||
index_f: s![.., .., .., 0, 0]
|
||||
index_g: s![0, 0, 0, .., ..]
|
||||
index_h: s![0, 0, .., 0, ..]
|
||||
index_i: s![0, 0, .., .., 0]
|
||||
index_j: s![0, .., 0, 0, ..]
|
||||
index_k: s![0, .., 0, .., 0]
|
||||
index_l: s![0, .., .., 0, 0]
|
||||
index_m: s![0, 0, 0, 0, ..]
|
||||
index_n: s![0, 0, 0, .., 0]
|
||||
index_o: s![0, 0, .., 0, 0]
|
||||
index_p: s![0, .., 0, 0, 0]
|
||||
index_q: s![.., 0, 0, 0, 0]
|
||||
index_r: s![0, 0, 0, 0, 0]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_view() -> Result<(), Error> {
|
||||
let file = "tiffseq/YTL1841B2-2-1_1hr_DMSO_galinduction_1";
|
||||
let reader = open(file)?;
|
||||
let a = reader.view().into_dyn();
|
||||
let b = a.max_proj(1)?;
|
||||
let c = b.slice(s![0, 0, .., ..])?;
|
||||
let d = c.as_array::<usize>()?;
|
||||
assert_eq!(d.shape(), [1024, 1024]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item() -> Result<(), Error> {
|
||||
let file = "czi/1xp53-01-AP1.czi";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view();
|
||||
let a = view.slice(s![.., 0, 0, 0, 0])?;
|
||||
let b = a.slice(s![0])?;
|
||||
let item = b.item::<usize>()?;
|
||||
assert_eq!(item, 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_cztyx() -> Result<(), Error> {
|
||||
let file = "czi/1xp53-01-AP1.czi";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view().max_proj(Axis::Z)?.into_dyn();
|
||||
println!("view.axes: {:?}", view.get_axes());
|
||||
println!("view.slice: {:?}", view.get_slice());
|
||||
let r = view.reset_axes()?;
|
||||
println!("r.axes: {:?}", r.get_axes());
|
||||
println!("r.slice: {:?}", r.get_slice());
|
||||
let a = view.slice_cztyx(s![0, 0, 0, .., ..])?;
|
||||
println!("a.axes: {:?}", a.get_axes());
|
||||
println!("a.slice: {:?}", a.get_slice());
|
||||
assert_eq!(a.axes(), [Axis::Y, Axis::X]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_axes() -> Result<(), Error> {
|
||||
let file = "czi/1xp53-01-AP1.czi";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view().max_proj(Axis::Z)?;
|
||||
let view = view.reset_axes()?;
|
||||
assert_eq!(view.axes(), [Axis::C, Axis::New, Axis::T, Axis::Y, Axis::X]);
|
||||
let a = view.as_array::<f64>()?;
|
||||
assert_eq!(a.ndim(), 5);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_axes2() -> Result<(), Error> {
|
||||
let file = "czi/Experiment-2029.czi";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view().squeeze()?;
|
||||
let a = view.reset_axes()?;
|
||||
assert_eq!(a.axes(), [Axis::C, Axis::Z, Axis::T, Axis::Y, Axis::X]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_axes3() -> Result<(), Error> {
|
||||
let file = "czi/Experiment-2029.czi";
|
||||
let reader = open(file)?;
|
||||
let view4 = reader.view().squeeze()?;
|
||||
let view = view4.max_proj(Axis::Z)?.into_dyn();
|
||||
let slice = view.slice_cztyx(s![0, .., .., .., ..])?.into_dyn();
|
||||
let a = slice.as_array::<u16>()?;
|
||||
assert_eq!(slice.shape().to_vec(), [1, 10, 1280, 1280]);
|
||||
assert_eq!(a.shape(), [1, 10, 1280, 1280]);
|
||||
let r = slice.reset_axes()?;
|
||||
let b = r.as_array::<u16>()?;
|
||||
assert_eq!(r.shape().to_vec(), [1, 1, 10, 1280, 1280]);
|
||||
assert_eq!(b.shape(), [1, 1, 10, 1280, 1280]);
|
||||
let q = slice.max_proj(Axis::C)?.max_proj(Axis::T)?;
|
||||
let c = q.as_array::<f64>()?;
|
||||
assert_eq!(q.shape().to_vec(), [1, 1280, 1280]);
|
||||
assert_eq!(c.shape().to_vec(), [1, 1280, 1280]);
|
||||
let p = q.reset_axes()?;
|
||||
let d = p.as_array::<u16>()?;
|
||||
println!("axes: {:?}", p.get_axes());
|
||||
println!("operations: {:?}", p.get_operations());
|
||||
println!("slice: {:?}", p.get_slice());
|
||||
assert_eq!(p.shape().to_vec(), [1, 1, 1, 1280, 1280]);
|
||||
assert_eq!(d.shape(), [1, 1, 1, 1280, 1280]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max() -> Result<(), Error> {
|
||||
let file = "czi/Experiment-2029.czi";
|
||||
let reader = open(file)?;
|
||||
let view = reader.view();
|
||||
let m = view.max_proj(Axis::T)?;
|
||||
let a = m.as_array::<u16>()?;
|
||||
assert_eq!(m.shape().to_vec(), [2, 1, 1280, 1280]);
|
||||
assert_eq!(a.shape(), [2, 1, 1280, 1280]);
|
||||
let mc = view.max_proj(Axis::C)?;
|
||||
let a = mc.as_array::<u16>()?;
|
||||
assert_eq!(mc.shape().to_vec(), [1, 10, 1280, 1280]);
|
||||
assert_eq!(a.shape(), [1, 10, 1280, 1280]);
|
||||
let mz = mc.max_proj(Axis::Z)?;
|
||||
let a = mz.as_array::<u16>()?;
|
||||
assert_eq!(mz.shape().to_vec(), [10, 1280, 1280]);
|
||||
assert_eq!(a.shape(), [10, 1280, 1280]);
|
||||
let mt = mz.max_proj(Axis::T)?;
|
||||
let a = mt.as_array::<u16>()?;
|
||||
assert_eq!(mt.shape().to_vec(), [1280, 1280]);
|
||||
assert_eq!(a.shape(), [1280, 1280]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user