- implement shape in Rust

- implement more readers
- fix downloading of bioformats jar
- (mostly) compatible with python version
This commit is contained in:
w.pomp
2026-07-13 13:40:34 +02:00
parent 705ca16379
commit ff7cd562af
36 changed files with 7946 additions and 2843 deletions
+59 -22
View File
@@ -1,17 +1,19 @@
use crate::axes::Axis;
use crate::colors::Color;
use crate::error::Error;
use crate::reader::PixelType;
use crate::readers::{PixelType, Reader};
use crate::view::View;
use console::Term;
use ffmpeg_sidecar::command::FfmpegCommand;
use ffmpeg_sidecar::download::auto_download;
use ffmpeg_sidecar::event::{FfmpegEvent, LogLevel};
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use itertools::Itertools;
use ndarray::{Array2, Array3, Dimension, IxDyn, s, stack};
use ordered_float::OrderedFloat;
use std::io::Write;
use std::path::Path;
use std::thread;
use std::time::Duration;
pub struct MovieOptions {
velocity: f64,
@@ -93,7 +95,7 @@ impl MovieOptions {
}
}
fn get_ab(tyx: View<IxDyn>) -> Result<(f64, f64), Error> {
fn get_ab<R: Reader>(tyx: View<IxDyn, R>) -> Result<(f64, f64), Error> {
let s = tyx
.as_array::<f64>()?
.iter()
@@ -136,9 +138,25 @@ fn cframe(frame: Array2<f64>, color: &[u8], a: f64, b: f64) -> Array3<f64> {
stack(ndarray::Axis(2), &view).unwrap()
}
impl<D> View<D>
/// a progress bar with an ok style that when py::detach is used also works in jupyter
pub fn get_bar(count: Option<usize>) -> ProgressBar {
let style = ProgressStyle::with_template(
"{spinner:.green} {percent}% [{wide_bar:.green/lime}] {pos:>7}/{len:7} [{elapsed}/{eta}, {per_sec:<5}]",
).expect("template should be working").progress_chars("#>-");
let bar = ProgressBar::with_draw_target(
count.map(|i| i as u64),
ProgressDrawTarget::term_like_with_hz(Box::new(Term::buffered_stdout()), 20),
)
.with_style(style);
bar.enable_steady_tick(Duration::from_millis(100));
bar
}
impl<D, R> View<D, R>
where
D: Dimension,
R: Reader,
Self: 'static,
{
pub fn save_as_movie<P>(&self, path: P, options: &MovieOptions) -> Result<(), Error>
where
@@ -211,7 +229,7 @@ where
let ab = if options.no_scaling {
vec![
match view.pixel_type {
match view.pixel_type() {
PixelType::I8 => (i8::MIN as f64, i8::MAX as f64),
PixelType::U8 => (u8::MIN as f64, u8::MAX as f64),
PixelType::I16 => (i16::MIN as f64, i16::MAX as f64),
@@ -222,7 +240,7 @@ where
PixelType::U64 => (u64::MIN as f64, u64::MAX as f64),
_ => (0.0, 1.0),
};
view.size_c
view.shape().c
]
} else {
(0..size_c)
@@ -233,13 +251,16 @@ where
.collect::<Result<Vec<_>, Error>>()?
};
thread::spawn(move || {
let rt = tokio::runtime::Runtime::new()?;
let bar = get_bar(Some(size_t));
let rt_bar = bar.clone();
let write_task = rt.spawn(async move {
for t in 0..size_t {
let mut frame = Array3::<f64>::zeros((size_y, size_x, 3));
for c in 0..size_c {
frame = frame
+ cframe(
view.get_frame(c, 0, t).unwrap(),
view.get_frame(c, 0, t)?,
&colors[c],
ab[c].0,
ab[c].1 / brightness[c],
@@ -247,18 +268,30 @@ where
}
let frame = (frame.clamp(0.0, 1.0) * 255.0).round().mapv(|i| i as u8);
let bytes: Vec<_> = frame.flatten().into_iter().collect();
stdin.write_all(&bytes).unwrap();
stdin.write_all(&bytes)?;
rt_bar.inc(1);
}
Ok::<(), Error>(())
});
bar.finish();
let bar = get_bar(Some(size_t));
let rt_bar = bar.clone();
let progress_task = rt.spawn(async move {
for event in movie.iter().map_err(|e| Error::Ffmpeg(e.to_string()))? {
match event {
FfmpegEvent::Log(LogLevel::Error, e) => Err(Error::Ffmpeg(e))?,
FfmpegEvent::Progress(p) => rt_bar.set_position(p.frame as u64),
_ => {}
}
}
Ok::<(), Error>(())
});
movie
.iter()
.map_err(|e| Error::Ffmpeg(e.to_string()))?
.for_each(|e| match e {
FfmpegEvent::Log(LogLevel::Error, e) => println!("Error: {}", e),
FfmpegEvent::Progress(p) => println!("Progress: {} / 00:00:15", p.time),
_ => {}
});
rt.block_on(progress_task)??;
rt.block_on(write_task)??;
bar.finish();
Ok(())
}
}
@@ -266,20 +299,24 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::reader::Reader;
use crate::readers::DynReader;
use crate::view::View;
#[cfg(any(feature = "czi", feature = "bioformats_java"))]
#[test]
fn movie() -> Result<(), Error> {
let file = "1xp53-01-AP1.czi";
let file = "czi/1xp53-01-AP1.czi";
let path = std::env::current_dir()?
.join("tests")
.join("files")
.join(file);
let reader = Reader::new(&path, 0)?;
let view = reader.view();
let view: View<_, DynReader> = View::from_path(&path)?;
let mut options = MovieOptions::default();
options.set_overwrite(true);
view.save_as_movie("/home/wim/tmp/movie.mp4", &options)?;
view.save_as_movie(
std::env::home_dir().unwrap().join("tmp/movie.mp4"),
&options,
)?;
Ok(())
}
}