744 lines
25 KiB
Rust
744 lines
25 KiB
Rust
use crate::axes::{Axis, Shape};
|
|
use crate::error::Error;
|
|
use crate::view::View;
|
|
use ndarray::{Array, Dimension, Ix2, Ix5, s};
|
|
use num::{FromPrimitive, Zero};
|
|
use ome_metadata::Ome;
|
|
use regex::Regex;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::any::type_name;
|
|
use std::collections::HashSet;
|
|
use std::fmt::Debug;
|
|
use std::hash::Hash;
|
|
use std::path::{Path, PathBuf};
|
|
use std::str::FromStr;
|
|
use std::sync::LazyLock;
|
|
|
|
#[cfg(feature = "czi")]
|
|
pub mod czi;
|
|
|
|
#[cfg(feature = "bioformats_rust")]
|
|
pub mod bioformats_rust;
|
|
|
|
#[cfg(feature = "bioformats_java")]
|
|
pub mod bioformats_java;
|
|
|
|
#[cfg(feature = "tiffseq")]
|
|
pub mod tiffseq;
|
|
|
|
#[cfg(feature = "tiff")]
|
|
pub mod tiff;
|
|
|
|
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^([CZTSP])\D+(\d+)$").unwrap());
|
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
pub struct Dimensions {
|
|
pub c: Option<usize>,
|
|
pub z: Option<usize>,
|
|
pub t: Option<usize>,
|
|
pub s: Option<usize>,
|
|
pub p: Option<usize>,
|
|
}
|
|
|
|
impl Dimensions {
|
|
pub fn new(series: usize, position: usize) -> Self {
|
|
Self {
|
|
c: None,
|
|
z: None,
|
|
t: None,
|
|
s: Some(series),
|
|
p: Some(position),
|
|
}
|
|
}
|
|
|
|
pub fn parse_path<P>(path: P) -> Result<(PathBuf, Self), Error>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
let mut path = path.as_ref();
|
|
let mut new = Self::default();
|
|
while !path.exists() {
|
|
let last = path
|
|
.file_name()
|
|
.ok_or(Error::InvalidFileName)?
|
|
.to_string_lossy()
|
|
.to_string();
|
|
path = path.parent().ok_or(Error::NoParent)?;
|
|
let last_upper = last.to_uppercase();
|
|
let caps = RE.captures(&last_upper).ok_or(Error::FileDoesNotExist(
|
|
path.join(&last).display().to_string(),
|
|
))?;
|
|
let p = caps[2].parse()?;
|
|
match &caps[1] {
|
|
"C" => new.c = Some(p),
|
|
"Z" => new.z = Some(p),
|
|
"T" => new.t = Some(p),
|
|
"S" => new.s = Some(p),
|
|
"P" => new.p = Some(p),
|
|
_ => {
|
|
return Err(Error::FileDoesNotExist(
|
|
path.join(last).display().to_string(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
Ok((path.to_path_buf(), new))
|
|
}
|
|
}
|
|
|
|
/// Pixel types (u)int(8/16/32) or float(32/64), (u/i)(64/128) are not included in bioformats
|
|
#[allow(clippy::upper_case_acronyms)]
|
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
|
pub enum PixelType {
|
|
I8,
|
|
U8,
|
|
I16,
|
|
U16,
|
|
I32,
|
|
U32,
|
|
F32,
|
|
F64,
|
|
I64,
|
|
U64,
|
|
I128,
|
|
U128,
|
|
F128,
|
|
}
|
|
|
|
impl PixelType {
|
|
pub fn bytes_per_pixel(&self) -> usize {
|
|
match self {
|
|
PixelType::I8 | PixelType::U8 => 1,
|
|
PixelType::I16 | PixelType::U16 => 2,
|
|
PixelType::I32 | PixelType::U32 | PixelType::F32 => 4,
|
|
PixelType::I64 | PixelType::U64 | PixelType::F64 => 8,
|
|
PixelType::I128 | PixelType::U128 | PixelType::F128 => 16,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Struct containing frame data in one of eight pixel types. Cast to `Array2<T>` using try_into.
|
|
#[allow(clippy::upper_case_acronyms)]
|
|
#[derive(Clone, Debug)]
|
|
pub enum ArrayT<D: Dimension> {
|
|
I8(Array<i8, D>),
|
|
U8(Array<u8, D>),
|
|
I16(Array<i16, D>),
|
|
U16(Array<u16, D>),
|
|
I32(Array<i32, D>),
|
|
U32(Array<u32, D>),
|
|
F32(Array<f32, D>),
|
|
F64(Array<f64, D>),
|
|
I64(Array<i64, D>),
|
|
U64(Array<u64, D>),
|
|
I128(Array<i128, D>),
|
|
U128(Array<u128, D>),
|
|
F128(Array<f64, D>), // f128 is nightly
|
|
}
|
|
|
|
pub type Frame = ArrayT<Ix2>;
|
|
|
|
pub trait Reader: Clone + Sized + Debug + Send + Hash + Into<DynReader> {
|
|
fn new<P>(path: P, series: usize, position: usize) -> Result<Self, Error>
|
|
where
|
|
P: AsRef<Path>;
|
|
|
|
fn reader_name(&self) -> &'static str {
|
|
type_name::<Self>()
|
|
}
|
|
|
|
// TODO: read from file if present
|
|
fn metadata(&self) -> Result<Ome, Error>;
|
|
|
|
/// get a sliceable view on the image file
|
|
fn view(&self) -> View<Ix5, Self> {
|
|
let shape = self.shape();
|
|
let slice = s![0..shape.c, 0..shape.z, 0..shape.t, 0..shape.y, 0..shape.x,];
|
|
View::new(
|
|
self.clone(),
|
|
slice.as_ref().to_vec(),
|
|
vec![Axis::C, Axis::Z, Axis::T, Axis::Y, Axis::X],
|
|
)
|
|
}
|
|
|
|
/// Retrieve fame at channel c, slize z and time t.
|
|
#[allow(clippy::if_same_then_else)]
|
|
fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame, Error>;
|
|
|
|
fn path(&self) -> &Path;
|
|
fn series(&self) -> usize;
|
|
fn position(&self) -> usize;
|
|
fn shape(&self) -> &Shape;
|
|
fn pixel_type(&self) -> &PixelType;
|
|
fn get_available_positions<P>(path: P, series: usize) -> Result<HashSet<usize>, Error>
|
|
where
|
|
P: AsRef<Path>;
|
|
fn get_available_series<P>(path: P) -> Result<HashSet<usize>, Error>
|
|
where
|
|
P: AsRef<Path>;
|
|
}
|
|
|
|
impl TryFrom<i32> for PixelType {
|
|
type Error = Error;
|
|
|
|
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
|
match value {
|
|
0 => Ok(PixelType::I8),
|
|
1 => Ok(PixelType::U8),
|
|
2 => Ok(PixelType::I16),
|
|
3 => Ok(PixelType::U16),
|
|
4 => Ok(PixelType::I32),
|
|
5 => Ok(PixelType::U32),
|
|
6 => Ok(PixelType::F32),
|
|
7 => Ok(PixelType::F64),
|
|
8 => Ok(PixelType::I64),
|
|
9 => Ok(PixelType::U64),
|
|
10 => Ok(PixelType::I128),
|
|
11 => Ok(PixelType::U128),
|
|
12 => Ok(PixelType::F128),
|
|
_ => Err(Error::UnknownPixelType(value.to_string())),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromStr for PixelType {
|
|
type Err = Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s.to_lowercase().as_str() {
|
|
"int8" | "i8" => Ok(PixelType::I8),
|
|
"uint8" | "u8" => Ok(PixelType::U8),
|
|
"int16" | "i16" => Ok(PixelType::I16),
|
|
"uint16" | "u16" => Ok(PixelType::U16),
|
|
"int32" | "i32" => Ok(PixelType::I32),
|
|
"uint32" | "u32" => Ok(PixelType::U32),
|
|
"float" | "f32" | "float32" => Ok(PixelType::F32),
|
|
"double" | "f64" | "float64" => Ok(PixelType::F64),
|
|
"int64" | "i64" => Ok(PixelType::I64),
|
|
"uint64" | "u64" => Ok(PixelType::U64),
|
|
"int128" | "i128" => Ok(PixelType::I128),
|
|
"uint128" | "u128" => Ok(PixelType::U128),
|
|
"extended" | "f128" => Ok(PixelType::F128),
|
|
_ => Err(Error::UnknownPixelType(s.to_string())),
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_frame_cast {
|
|
($($t:tt: $s:ident $(,)?)*) => {
|
|
$(
|
|
impl<D: Dimension> From<Array<$t, D>> for ArrayT<D> {
|
|
fn from(value: Array<$t, D>) -> Self {
|
|
ArrayT::$s(value)
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
impl_frame_cast! {
|
|
u8: U8
|
|
i8: I8
|
|
i16: I16
|
|
u16: U16
|
|
i32: I32
|
|
u32: U32
|
|
f32: F32
|
|
f64: F64
|
|
i64: I64
|
|
u64: U64
|
|
i128: I128
|
|
u128: U128
|
|
}
|
|
|
|
#[cfg(target_pointer_width = "32")]
|
|
impl_frame_cast! {
|
|
usize: UINT32
|
|
isize: INT32
|
|
}
|
|
|
|
macro_rules! try_into_array_body {
|
|
($self:expr, $map:ident) => {{
|
|
let mut err = Ok(());
|
|
let arr = match $self {
|
|
ArrayT::I8(v) => v.$map(|x| {
|
|
T::from_i8(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::U8(v) => v.$map(|x| {
|
|
T::from_u8(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::I16(v) => v.$map(|x| {
|
|
T::from_i16(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::U16(v) => v.$map(|x| {
|
|
T::from_u16(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::I32(v) => v.$map(|x| {
|
|
T::from_i32(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::U32(v) => v.$map(|x| {
|
|
T::from_u32(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::F32(v) => v.$map(|x| {
|
|
T::from_f32(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::F64(v) | ArrayT::F128(v) => v.$map(|x| {
|
|
T::from_f64(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::I64(v) => v.$map(|x| {
|
|
T::from_i64(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::U64(v) => v.$map(|x| {
|
|
T::from_u64(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::I128(v) => v.$map(|x| {
|
|
T::from_i128(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
ArrayT::U128(v) => v.$map(|x| {
|
|
T::from_u128(x).unwrap_or_else(|| {
|
|
err = Err(Error::Cast(x.to_string(), type_name::<T>().to_string()));
|
|
T::zero()
|
|
})
|
|
}),
|
|
};
|
|
match err {
|
|
Err(err) => Err(err),
|
|
Ok(()) => Ok(arr),
|
|
}
|
|
}};
|
|
}
|
|
|
|
impl<D, T> TryInto<Array<T, D>> for ArrayT<D>
|
|
where
|
|
D: Dimension,
|
|
T: FromPrimitive + Zero + 'static,
|
|
{
|
|
type Error = Error;
|
|
|
|
fn try_into(self) -> Result<Array<T, D>, Self::Error> {
|
|
try_into_array_body!(self, mapv_into_any)
|
|
}
|
|
}
|
|
|
|
impl<D, T> TryInto<Array<T, D>> for &ArrayT<D>
|
|
where
|
|
D: Dimension,
|
|
T: Clone + FromPrimitive + Zero + 'static,
|
|
{
|
|
type Error = Error;
|
|
|
|
fn try_into(self) -> Result<Array<T, D>, Self::Error> {
|
|
try_into_array_body!(self, mapv)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
|
|
pub enum DynReader {
|
|
#[cfg(feature = "tiff")]
|
|
Tiff(tiff::TiffReader),
|
|
#[cfg(feature = "tiffseq")]
|
|
TiffSeq(tiffseq::TiffSeqReader),
|
|
#[cfg(feature = "czi")]
|
|
Czi(czi::CziReader),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
BioFormatsRust(bioformats_rust::BioFormatsRustReader),
|
|
#[cfg(feature = "bioformats_java")]
|
|
BioFormatsJava(bioformats_java::BioFormatsJavaReader),
|
|
}
|
|
|
|
impl Reader for DynReader {
|
|
fn new<P>(path: P, series: usize, position: usize) -> Result<Self, Error>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
let mut errors = Vec::<String>::new();
|
|
#[cfg(feature = "tiff")]
|
|
match tiff::TiffReader::new(&path, series, position) {
|
|
Ok(reader) => return Ok(DynReader::Tiff(reader)),
|
|
Err(err) => errors.push(format!("TiffReader: {}", err)),
|
|
}
|
|
#[cfg(feature = "tiffseq")]
|
|
match tiffseq::TiffSeqReader::new(&path, series, position) {
|
|
Ok(reader) => return Ok(DynReader::TiffSeq(reader)),
|
|
Err(err) => errors.push(format!("TiffseqReader: {}", err)),
|
|
}
|
|
#[cfg(feature = "czi")]
|
|
match czi::CziReader::new(&path, series, position) {
|
|
Ok(reader) => return Ok(DynReader::Czi(reader)),
|
|
Err(err) => errors.push(format!("CziReader: {}", err)),
|
|
}
|
|
#[cfg(feature = "bioformats_rust")]
|
|
match bioformats_rust::BioFormatsRustReader::new(&path, series, position) {
|
|
Ok(reader) => return Ok(DynReader::BioFormatsRust(reader)),
|
|
Err(err) => errors.push(format!("BioformatsRustReader: {}", err)),
|
|
}
|
|
#[cfg(feature = "bioformats_java")]
|
|
match bioformats_java::BioFormatsJavaReader::new(&path, series, position) {
|
|
Ok(reader) => return Ok(DynReader::BioFormatsJava(reader)),
|
|
Err(err) => errors.push(format!("BioformatsReader: {}", err)),
|
|
}
|
|
Err(Error::NoReader(
|
|
path.as_ref().display().to_string(),
|
|
errors.join("\n"),
|
|
))
|
|
}
|
|
|
|
fn reader_name(&self) -> &'static str {
|
|
match self {
|
|
#[cfg(feature = "tiff")]
|
|
DynReader::Tiff(r) => r.reader_name(),
|
|
#[cfg(feature = "tiffseq")]
|
|
DynReader::TiffSeq(r) => r.reader_name(),
|
|
#[cfg(feature = "czi")]
|
|
DynReader::Czi(r) => r.reader_name(),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
DynReader::BioFormatsRust(r) => r.reader_name(),
|
|
#[cfg(feature = "bioformats_java")]
|
|
DynReader::BioFormatsJava(r) => r.reader_name(),
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn metadata(&self) -> Result<Ome, Error> {
|
|
Ok(match self {
|
|
#[cfg(feature = "tiff")]
|
|
DynReader::Tiff(r) => r.metadata()?,
|
|
#[cfg(feature = "tiffseq")]
|
|
DynReader::TiffSeq(r) => r.metadata()?,
|
|
#[cfg(feature = "czi")]
|
|
DynReader::Czi(r) => r.metadata()?,
|
|
#[cfg(feature = "bioformats_rust")]
|
|
DynReader::BioFormatsRust(r) => r.metadata()?,
|
|
#[cfg(feature = "bioformats_java")]
|
|
DynReader::BioFormatsJava(r) => r.metadata()?,
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
})
|
|
}
|
|
|
|
fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame, Error> {
|
|
match self {
|
|
#[cfg(feature = "tiff")]
|
|
DynReader::Tiff(r) => r.get_frame(c, z, t),
|
|
#[cfg(feature = "tiffseq")]
|
|
DynReader::TiffSeq(r) => r.get_frame(c, z, t),
|
|
#[cfg(feature = "czi")]
|
|
DynReader::Czi(r) => r.get_frame(c, z, t),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
DynReader::BioFormatsRust(r) => r.get_frame(c, z, t),
|
|
#[cfg(feature = "bioformats_java")]
|
|
DynReader::BioFormatsJava(r) => r.get_frame(c, z, t),
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn path(&self) -> &Path {
|
|
match self {
|
|
#[cfg(feature = "tiff")]
|
|
DynReader::Tiff(r) => r.path(),
|
|
#[cfg(feature = "tiffseq")]
|
|
DynReader::TiffSeq(r) => r.path(),
|
|
#[cfg(feature = "czi")]
|
|
DynReader::Czi(r) => r.path(),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
DynReader::BioFormatsRust(r) => r.path(),
|
|
#[cfg(feature = "bioformats_java")]
|
|
DynReader::BioFormatsJava(r) => r.path(),
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn series(&self) -> usize {
|
|
match self {
|
|
#[cfg(feature = "tiff")]
|
|
DynReader::Tiff(r) => r.series(),
|
|
#[cfg(feature = "tiffseq")]
|
|
DynReader::TiffSeq(r) => r.series(),
|
|
#[cfg(feature = "czi")]
|
|
DynReader::Czi(r) => r.series(),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
DynReader::BioFormatsRust(r) => r.series(),
|
|
#[cfg(feature = "bioformats_java")]
|
|
DynReader::BioFormatsJava(r) => r.series(),
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn position(&self) -> usize {
|
|
match self {
|
|
#[cfg(feature = "tiff")]
|
|
DynReader::Tiff(r) => r.position(),
|
|
#[cfg(feature = "tiffseq")]
|
|
DynReader::TiffSeq(r) => r.position(),
|
|
#[cfg(feature = "czi")]
|
|
DynReader::Czi(r) => r.position(),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
DynReader::BioFormatsRust(r) => r.position(),
|
|
#[cfg(feature = "bioformats_java")]
|
|
DynReader::BioFormatsJava(r) => r.position(),
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn shape(&self) -> &Shape {
|
|
match self {
|
|
#[cfg(feature = "tiff")]
|
|
DynReader::Tiff(r) => r.shape(),
|
|
#[cfg(feature = "tiffseq")]
|
|
DynReader::TiffSeq(r) => r.shape(),
|
|
#[cfg(feature = "czi")]
|
|
DynReader::Czi(r) => r.shape(),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
DynReader::BioFormatsRust(r) => r.shape(),
|
|
#[cfg(feature = "bioformats_java")]
|
|
DynReader::BioFormatsJava(r) => r.shape(),
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn pixel_type(&self) -> &PixelType {
|
|
match self {
|
|
#[cfg(feature = "tiff")]
|
|
DynReader::Tiff(r) => r.pixel_type(),
|
|
#[cfg(feature = "tiffseq")]
|
|
DynReader::TiffSeq(r) => r.pixel_type(),
|
|
#[cfg(feature = "czi")]
|
|
DynReader::Czi(r) => r.pixel_type(),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
DynReader::BioFormatsRust(r) => r.pixel_type(),
|
|
#[cfg(feature = "bioformats_java")]
|
|
DynReader::BioFormatsJava(r) => r.pixel_type(),
|
|
#[allow(unreachable_patterns)]
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn get_available_positions<P>(path: P, series: usize) -> Result<HashSet<usize>, Error>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
let mut errors = Vec::<String>::new();
|
|
#[cfg(feature = "tiff")]
|
|
match tiff::TiffReader::get_available_positions(&path, series) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("TiffReader: {}", e)),
|
|
}
|
|
#[cfg(feature = "tiffseq")]
|
|
match tiffseq::TiffSeqReader::get_available_positions(&path, series) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("TiffSeqReader: {}", e)),
|
|
}
|
|
#[cfg(feature = "czi")]
|
|
match czi::CziReader::get_available_positions(&path, series) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("CziReader: {}", e)),
|
|
}
|
|
#[cfg(feature = "bioformats_rust")]
|
|
match bioformats_rust::BioFormatsRustReader::get_available_positions(&path, series) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("BioFormatsRustReader: {}", e)),
|
|
}
|
|
#[cfg(feature = "bioformats_java")]
|
|
match bioformats_java::BioFormatsJavaReader::get_available_positions(&path, series) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("BioFormatsReader: {}", e)),
|
|
}
|
|
Err(Error::NoReader(
|
|
path.as_ref().display().to_string(),
|
|
errors.join("\n"),
|
|
))
|
|
}
|
|
|
|
fn get_available_series<P>(path: P) -> Result<HashSet<usize>, Error>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
let mut errors = Vec::<String>::new();
|
|
#[cfg(feature = "tiff")]
|
|
match tiff::TiffReader::get_available_series(&path) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("TiffReader: {}", e)),
|
|
}
|
|
#[cfg(feature = "tiffseq")]
|
|
match tiffseq::TiffSeqReader::get_available_series(&path) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("TiffSeqReader: {}", e)),
|
|
}
|
|
#[cfg(feature = "czi")]
|
|
match czi::CziReader::get_available_series(&path) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("CziReader: {}", e)),
|
|
}
|
|
#[cfg(feature = "bioformats_rust")]
|
|
match bioformats_rust::BioFormatsRustReader::get_available_series(&path) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("BioFormatsRustReader: {}", e)),
|
|
}
|
|
#[cfg(feature = "bioformats_java")]
|
|
match bioformats_java::BioFormatsJavaReader::get_available_series(&path) {
|
|
Ok(positions) => return Ok(positions),
|
|
Err(e) => errors.push(format!("BioFormatsReader: {}", e)),
|
|
}
|
|
Err(Error::NoReader(
|
|
path.as_ref().display().to_string(),
|
|
errors.join("\n"),
|
|
))
|
|
}
|
|
}
|
|
|
|
impl DynReader {
|
|
pub fn from_path_select_reader<P, R>(path: P, reader: R) -> Result<DynReader, Error>
|
|
where
|
|
P: AsRef<Path>,
|
|
R: AsRef<str>,
|
|
{
|
|
let path = path.as_ref();
|
|
let (path, dimensions) = Dimensions::parse_path(path)?;
|
|
let reader = reader.as_ref();
|
|
let reader = match reader.to_lowercase().as_str() {
|
|
#[cfg(feature = "tiff")]
|
|
"tif" => Ok(DynReader::Tiff(tiff::TiffReader::new(
|
|
path,
|
|
dimensions.s.unwrap_or(0),
|
|
dimensions.p.unwrap_or(0),
|
|
)?)),
|
|
#[cfg(feature = "tiffseq")]
|
|
"tiffseq" => Ok(DynReader::TiffSeq(tiffseq::TiffSeqReader::new(
|
|
path,
|
|
dimensions.s.unwrap_or(0),
|
|
dimensions.p.unwrap_or(0),
|
|
)?)),
|
|
#[cfg(feature = "czi")]
|
|
"czi" => Ok(DynReader::Czi(czi::CziReader::new(
|
|
path,
|
|
dimensions.s.unwrap_or(0),
|
|
dimensions.p.unwrap_or(0),
|
|
)?)),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
"bioformats_rust" => Ok(DynReader::BioFormatsRust(
|
|
bioformats_rust::BioFormatsRustReader::new(
|
|
path,
|
|
dimensions.s.unwrap_or(0),
|
|
dimensions.p.unwrap_or(0),
|
|
)?,
|
|
)),
|
|
#[cfg(feature = "bioformats_java")]
|
|
"bioformats_java" => Ok(DynReader::BioFormatsJava(
|
|
bioformats_java::BioFormatsJavaReader::new(
|
|
path,
|
|
dimensions.s.unwrap_or(0),
|
|
dimensions.p.unwrap_or(0),
|
|
)?,
|
|
)),
|
|
_ => Err(Error::Parse(reader.to_string())),
|
|
}?;
|
|
Ok(reader)
|
|
}
|
|
|
|
pub fn get_available_positions_select_reader<P, R>(
|
|
path: P,
|
|
series: usize,
|
|
reader: Option<R>,
|
|
) -> Result<HashSet<usize>, Error>
|
|
where
|
|
P: AsRef<Path>,
|
|
R: AsRef<str>,
|
|
{
|
|
Ok(if let Some(reader) = reader {
|
|
let reader = reader.as_ref();
|
|
match reader.to_lowercase().as_str() {
|
|
#[cfg(feature = "tiff")]
|
|
"tif" => Ok(tiff::TiffReader::get_available_positions(path, series)?),
|
|
#[cfg(feature = "tiffseq")]
|
|
"tiffseq" => Ok(tiffseq::TiffSeqReader::get_available_positions(
|
|
path, series,
|
|
)?),
|
|
#[cfg(feature = "czi")]
|
|
"czi" => Ok(czi::CziReader::get_available_positions(path, series)?),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
"bioformats_rust" => Ok(
|
|
bioformats_rust::BioFormatsRustReader::get_available_positions(path, series)?,
|
|
),
|
|
#[cfg(feature = "bioformats_java")]
|
|
"bioformats_java" => Ok(
|
|
bioformats_java::BioFormatsJavaReader::get_available_positions(path, series)?,
|
|
),
|
|
_ => Err(Error::Parse(reader.to_string())),
|
|
}?
|
|
} else {
|
|
DynReader::get_available_positions(&path, series)?
|
|
})
|
|
}
|
|
|
|
pub fn get_available_series_select_reader<P, R>(
|
|
path: P,
|
|
reader: Option<R>,
|
|
) -> Result<HashSet<usize>, Error>
|
|
where
|
|
P: AsRef<Path>,
|
|
R: AsRef<str>,
|
|
{
|
|
Ok(if let Some(reader) = reader {
|
|
let reader = reader.as_ref();
|
|
match reader.to_lowercase().as_str() {
|
|
#[cfg(feature = "tiff")]
|
|
"tif" => Ok(tiff::TiffReader::get_available_series(path)?),
|
|
#[cfg(feature = "tiffseq")]
|
|
"tiffseq" => Ok(tiffseq::TiffSeqReader::get_available_series(path)?),
|
|
#[cfg(feature = "czi")]
|
|
"czi" => Ok(czi::CziReader::get_available_series(path)?),
|
|
#[cfg(feature = "bioformats_rust")]
|
|
"bioformats_rust" => Ok(
|
|
bioformats_rust::BioFormatsRustReader::get_available_series(path)?,
|
|
),
|
|
#[cfg(feature = "bioformats_java")]
|
|
"bioformats_java" => Ok(
|
|
bioformats_java::BioFormatsJavaReader::get_available_series(path)?,
|
|
),
|
|
_ => Err(Error::Parse(reader.to_string())),
|
|
}?
|
|
} else {
|
|
DynReader::get_available_series(&path)?
|
|
})
|
|
}
|
|
}
|