- implement custom error types

- less restrictive dependency versions
- some extra features and bugfixes for movie writing
- make python tests work again
This commit is contained in:
Wim Pomp
2026-01-04 13:59:57 +01:00
parent 3dc8e6af04
commit 3c14168878
19 changed files with 655 additions and 333 deletions

View File

@@ -1,6 +1,8 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
#[cfg(feature = "movie")]
use ndarray::SliceInfoElem;
use ndbioimage::error::Error;
#[cfg(feature = "movie")]
use ndbioimage::movie::MovieOptions;
use ndbioimage::reader::split_path_and_series;
#[cfg(feature = "tiff")]
@@ -37,16 +39,26 @@ enum Commands {
Movie {
#[arg(value_name = "FILE", num_args(1..))]
file: Vec<PathBuf>,
#[arg(short, long, value_name = "Velocity", default_value = "3.6")]
#[arg(short, long, value_name = "VELOCITY", default_value = "3.6")]
velocity: f64,
#[arg(short, long, value_name = "BRIGHTNESS")]
#[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, long, value_name = "COLOR", num_args(1..))]
#[arg(short = 'C', long, value_name = "COLOR", num_args(1..))]
colors: Vec<String>,
#[arg(short, 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,
},
/// Download the BioFormats jar into the correct folder
DownloadBioFormats {
@@ -55,7 +67,43 @@ enum Commands {
},
}
pub(crate) fn main() -> Result<()> {
#[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(crate) fn main() -> Result<(), Error> {
let cli = Cli::parse();
match &cli.command {
Commands::Info { file } => {
@@ -87,6 +135,11 @@ pub(crate) fn main() -> Result<()> {
scale,
colors,
overwrite,
register,
channel,
zslice,
time,
no_scaling,
} => {
let options = MovieOptions::new(
*speed,
@@ -94,11 +147,29 @@ pub(crate) fn main() -> Result<()> {
*scale,
colors.to_vec(),
*overwrite,
*register,
*no_scaling,
)?;
for f in file {
let (path, series) = split_path_and_series(f)?;
let view = View::from_path(path, series.unwrap_or(0))?;
view.save_as_movie(f.with_extension("mp4"), &options)?;
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)?;
}
view.into_dyn()
.slice(s.as_slice())?
.save_as_movie(f.with_extension("mp4"), &options)?;
}
}
Commands::DownloadBioFormats { gpl_formats } => {