#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod axes;
mod cache;
#[cfg(feature = "python")]
mod py;
pub mod stats;
pub mod view;
pub mod colors;
pub mod error;
pub mod metadata;
#[cfg(feature = "movie")]
pub mod movie;
pub mod readers;
#[cfg(feature = "tiffwrite")]
pub mod tiffwrite;
mod utils;
pub mod main {
#[cfg(feature = "tiffwrite")]
use crate::axes::{Axis, Operation};
use crate::error::Error;
#[cfg(feature = "movie")]
use crate::movie::MovieOptions;
use crate::readers::{Dimensions, DynReader, Reader};
use crate::view::View;
use clap::{Parser, Subcommand};
#[cfg(feature = "movie")]
use ndarray::SliceInfoElem;
use std::path::PathBuf;
#[derive(Parser)]
#[command(arg_required_else_help = true, version, about, long_about = None, propagate_version = true
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Info {
#[arg(value_name = "FILE", num_args(1..))]
file: Vec<PathBuf>,
},
ExtractOME {
#[arg(value_name = "FILE", num_args(1..))]
file: Vec<PathBuf>,
},
#[cfg(feature = "tiffwrite")]
Tiff {
#[arg(value_name = "FILE", num_args(1..))]
file: Vec<PathBuf>,
#[arg(short = 'C', long, value_name = "COLOR", num_args(1..))]
colors: Vec<String>,
#[arg(short = 'f', long, value_name = "OVERWRITE")]
overwrite: bool,
#[arg(short = 'q', long, value_name = "OPERATIONS", num_args(1..))]
operations: Vec<String>,
#[arg(short = 'o', long, value_name = "OUTPUT")]
output: Option<PathBuf>,
},
#[cfg(feature = "movie")]
Movie {
#[arg(value_name = "FILE", num_args(1..))]
file: Vec<PathBuf>,
#[arg(short, long, value_name = "VELOCITY", default_value = "3.6")]
velocity: f64,
#[arg(short, long, value_name = "BRIGHTNESS", num_args(1..))]
brightness: Vec<f64>,
#[arg(short, long, value_name = "SCALE", default_value = "1.0")]
scale: f64,
#[arg(short = 'C', long, value_name = "COLOR", num_args(1..))]
colors: Vec<String>,
#[arg(short = 'f', long, value_name = "OVERWRITE")]
overwrite: bool,
#[arg(short, long, value_name = "REGISTER")]
register: bool,
#[arg(short, long, value_name = "CHANNEL")]
channel: Option<isize>,
#[arg(short, long, value_name = "ZSLICE")]
zslice: Option<String>,
#[arg(short, long, value_name = "TIME")]
time: Option<String>,
#[arg(short, long, value_name = "NO-SCALE-BRIGHTNESS")]
no_scaling: bool,
#[arg(short = 'o', long, value_name = "OUTPUT")]
output: Option<PathBuf>,
},
}
#[cfg(feature = "movie")]
fn parse_slice(s: &str) -> Result<SliceInfoElem, Error> {
let mut t = s
.trim()
.replace("..", ":")
.split(":")
.map(|i| i.parse().ok())
.collect::<Vec<Option<isize>>>();
if t.len() > 3 {
return Err(Error::Parse(s.to_string()));
}
while t.len() < 3 {
t.push(None);
}
match t[..] {
[Some(start), None, None] => Ok(SliceInfoElem::Index(start)),
[Some(start), end, None] => Ok(SliceInfoElem::Slice {
start,
end,
step: 1,
}),
[Some(start), end, Some(step)] => Ok(SliceInfoElem::Slice { start, end, step }),
[None, end, None] => Ok(SliceInfoElem::Slice {
start: 0,
end,
step: 1,
}),
[None, end, Some(step)] => Ok(SliceInfoElem::Slice {
start: 0,
end,
step,
}),
_ => Err(Error::Parse(s.to_string())),
}
}
pub fn main(args: Option<Vec<String>>) -> Result<(), Error> {
let cli = if let Some(args) = args {
Cli::parse_from(args)
} else {
Cli::parse()
};
match &cli.command {
Commands::Info { file } => {
for f in file {
let view = View::<_, DynReader>::from_path(f)?.squeeze()?;
println!("{}", view.summary()?);
}
}
Commands::ExtractOME { file } => {
for f in file {
let (path, dimensions) = Dimensions::parse_path(f)?;
let reader = DynReader::new(
&path,
dimensions.s.unwrap_or(0),
dimensions.p.unwrap_or(0),
)?;
let xml = reader.metadata()?.to_xml()?;
std::fs::write(path.with_extension("xml"), xml)?;
}
}
#[cfg(feature = "tiffwrite")]
Commands::Tiff {
file,
colors,
overwrite,
operations,
output,
} => {
let options = crate::tiffwrite::TiffOptions::new(
Some(crate::utils::progress::get_bar(
Some(0),
Some("writing tiff file".to_string()),
)),
None,
colors.clone(),
*overwrite,
)?;
for f in file {
let mut view: View<_, DynReader> =
View::<_, DynReader>::from_path(f)?.into_dyn();
for operation in operations {
let a = operation.split(":").collect::<Vec<_>>();
if let Some(ax) = a.first()
&& let Some(op) = a.get(1)
{
view = view.operate(ax.parse::<Axis>()?, op.parse::<Operation>()?)?;
}
}
let out = output
.as_ref()
.filter(|_| file.len() == 1)
.cloned()
.unwrap_or_else(|| f.with_extension("tiff"));
view.save_as_tiff(&out, &options)?;
}
}
#[cfg(feature = "movie")]
Commands::Movie {
file,
velocity: speed,
brightness,
scale,
colors,
overwrite,
register,
channel,
zslice,
time,
no_scaling,
output,
} => {
let options = MovieOptions::new(
*speed,
brightness.to_vec(),
*scale,
colors.to_vec(),
*overwrite,
*register,
*no_scaling,
)?;
for f in file {
let view: View<_, DynReader> = View::from_path(f)?;
let mut s = [SliceInfoElem::Slice {
start: 0,
end: None,
step: 1,
}; 5];
if let Some(channel) = channel {
s[0] = SliceInfoElem::Index(*channel);
};
if let Some(zslice) = zslice {
s[1] = parse_slice(zslice)?;
}
if let Some(time) = time {
s[2] = parse_slice(time)?;
}
let out = output
.as_ref()
.filter(|_| file.len() == 1)
.cloned()
.unwrap_or_else(|| f.with_extension("mp4"));
view.into_dyn()
.slice(s.as_slice())?
.save_as_movie(&out, &options)?;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::error::Error;
use crate::readers::{DynReader, Frame, Reader};
use ndarray::Array2;
use rayon::prelude::*;
fn open(file: &str) -> Result<DynReader, Error> {
let path = std::env::current_dir()?
.join("tests")
.join("files")
.join(file);
DynReader::new(&path, 0, 0)
}
fn get_pixel_type(file: &str) -> Result<String, Error> {
let reader = open(file)?;
Ok(format!(
"file: {}, pixel type: {:?}",
file,
reader.pixel_type()
))
}
fn get_frame(file: &str) -> Result<Frame, Error> {
let reader = open(file)?;
reader.get_frame(0, 0, 0)
}
#[test]
fn read_ser() -> Result<(), Error> {
let file = "czi/Experiment-2029.czi";
let reader = open(file)?;
println!("shape: {}", reader.shape());
let frame = reader.get_frame(0, 0, 0)?;
if let Ok(arr) = <Frame as TryInto<Array2<i8>>>::try_into(frame) {
println!("{:?}", arr);
} else {
println!("could not convert Frame to Array<i8>");
}
Ok(())
}
#[test]
fn read_par() -> Result<(), Error> {
let files = vec!["czi/Experiment-2029.czi", "tiff/test.tif"];
let pixel_type = files
.into_par_iter()
.map(|file| get_pixel_type(file).unwrap())
.collect::<Vec<_>>();
println!("{:?}", pixel_type);
Ok(())
}
#[test]
fn read_frame_par() -> Result<(), Error> {
let files = vec!["czi/Experiment-2029.czi", "tiff/test.tif"];
let frames = files
.into_par_iter()
.map(|file| get_frame(file).unwrap())
.collect::<Vec<_>>();
println!("{:?}", frames);
Ok(())
}
}