- various fixes
This commit is contained in:
+2
-2
@@ -28,7 +28,7 @@ itertools = "0.15"
|
|||||||
indexmap = { version = "2", features = ["serde"] }
|
indexmap = { version = "2", features = ["serde"] }
|
||||||
indicatif = { version = "0.18", features = ["rayon"], optional = true }
|
indicatif = { version = "0.18", features = ["rayon"], optional = true }
|
||||||
j4rs = { version = "0.25", optional = true }
|
j4rs = { version = "0.25", optional = true }
|
||||||
libczirw-sys = { path = "../libczirw-sys", optional = true }
|
libczirw-sys = { version = "0.5", optional = true }
|
||||||
ndarray = { version = "0.17", features = ["serde"] }
|
ndarray = { version = "0.17", features = ["serde"] }
|
||||||
num = "0.4"
|
num = "0.4"
|
||||||
numpy = { version = "0.29", optional = true }
|
numpy = { version = "0.29", optional = true }
|
||||||
@@ -76,7 +76,7 @@ tiff = ["dep:tiff", "dep:thread_local"]
|
|||||||
movie = ["dep:ffmpeg-sidecar", "dep:tokio", "dep:ordered-float", "dep:indicatif", "dep:console"]
|
movie = ["dep:ffmpeg-sidecar", "dep:tokio", "dep:ordered-float", "dep:indicatif", "dep:console"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["bioformats_java", "gpl-formats", "czi", "tiff", "movie"]
|
features = ["bioformats_java", "czi", "tiff", "tiffseq", "movie", "tiffwrite", "movie"]
|
||||||
|
|
||||||
[profile.test]
|
[profile.test]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
# ndbioimage
|
# ndbioimage
|
||||||
|
|
||||||
[](https://github.com/pomppervova/ndbioimage/actions/workflows/pytest.yml)
|
[](https://github.com/pomppervova/ndbioimage/actions/workflows/pytest.yml)
|
||||||
|
|
||||||
## Work in progress
|
## Work in progress
|
||||||
|
|
||||||
Rust rewrite of python version. Read bio image formats using the bio-formats java package.
|
Rust rewrite of python version. Read bio image formats using the bio-formats java package.
|
||||||
[https://www.openmicroscopy.org/bio-formats/](https://www.openmicroscopy.org/bio-formats/)
|
[https://www.openmicroscopy.org/bio-formats/](https://www.openmicroscopy.org/bio-formats/)
|
||||||
|
|
||||||
Exposes (bio) images as a numpy ndarray-like object, but without loading the whole
|
Exposes (bio) images as a numpy ndarray-like object (Python) or a struct that can be sliced like an ndarray Array
|
||||||
image into memory, reading from the file only when needed. Some metadata is read
|
(Rust), but without loading the whole image into memory, reading from the file only when needed. Some metadata is read
|
||||||
and stored in an [ome](https://genomebiology.biomedcentral.com/articles/10.1186/gb-2005-6-5-r47) structure.
|
and stored in an [ome](https://genomebiology.biomedcentral.com/articles/10.1186/gb-2005-6-5-r47) structure.
|
||||||
Additionally, it can automatically calculate an affine transform that corrects for chromatic aberrations etc. and apply
|
Additionally, it can automatically calculate an affine transform that corrects for chromatic aberrations etc. and apply
|
||||||
it on the fly to the image.
|
it on the fly to the image.
|
||||||
@@ -23,6 +25,7 @@ pip install ndbioimage
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Installation with option to write mp4 or mkv:
|
### Installation with option to write mp4 or mkv:
|
||||||
|
|
||||||
Work in progress! Make sure ffmpeg is installed.
|
Work in progress! Make sure ffmpeg is installed.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -30,6 +33,7 @@ pip install ndbioimage[write]
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Python
|
### Python
|
||||||
|
|
||||||
- Reading an image file and plotting the frame at channel=2, time=1
|
- Reading an image file and plotting the frame at channel=2, time=1
|
||||||
@@ -60,7 +64,6 @@ with Imread('image_file.tif', axes='cztyx') as im:
|
|||||||
|
|
||||||
sliced_im is an instance of Imread which will load any image data from file only when needed
|
sliced_im is an instance of Imread which will load any image data from file only when needed
|
||||||
|
|
||||||
|
|
||||||
- Converting (part) of the image to a numpy ndarray
|
- Converting (part) of the image to a numpy ndarray
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -71,36 +74,45 @@ with Imread('image_file.tif', axes='cztyx') as im:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Rust
|
### Rust
|
||||||
```
|
|
||||||
use ndarray::Array2;
|
|
||||||
use ndbioimage::Reader;
|
|
||||||
|
|
||||||
let path = "/path/to/file";
|
```rust
|
||||||
let reader = Reader::new(&path, 0)?;
|
use ndarray::Array2;
|
||||||
println!("size: {}, {}", reader.size_y, reader.size_y);
|
use ndbioimage::{DynReader, Frame, Reader};
|
||||||
let frame = reader.get_frame(0, 0, 0).unwrap();
|
|
||||||
if let Ok(arr) = <Frame as TryInto<Array2<i8>>>::try_into(frame) {
|
fn main() -> Result<(), ndbioimage::error::Error> {
|
||||||
|
let path = "/path/to/file";
|
||||||
|
let reader = DynReader::new(&path, 0, 0)?;
|
||||||
|
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);
|
println!("{:?}", arr);
|
||||||
} else {
|
} else {
|
||||||
println!("could not convert Frame to Array<i8>");
|
println!("could not convert Frame to Array<i8>");
|
||||||
|
}
|
||||||
|
let xml = reader.metadata()?.to_xml()?;
|
||||||
|
println!("{}", xml);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
let xml = reader.get_ome_xml().unwrap();
|
|
||||||
println!("{}", xml);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```rust
|
||||||
use ndarray::Array2;
|
use ndbioimage::{DynReader, Reader};
|
||||||
use ndbioimage::Reader;
|
|
||||||
|
|
||||||
let path = "/path/to/file";
|
fn main() -> Result<(), ndbioimage::error::Error> {
|
||||||
let reader = Reader::new(&path, 0)?;
|
let path = "/path/to/file";
|
||||||
let view = reader.view();
|
let reader = DynReader::new(&path, 0, 0)?;
|
||||||
let view = view.max_proj(3)?;
|
let view = reader.view();
|
||||||
let array = view.as_array::<u16>()?
|
let view = view.max_proj(3)?;
|
||||||
|
let array = view.as_array::<u16>()?;
|
||||||
|
println!("{:?}", array.shape());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Command line
|
### Command line
|
||||||
|
|
||||||
```ndbioimage --help```: show help
|
```ndbioimage --help```: show help
|
||||||
```ndbioimage image```: show metadata about image
|
```ndbioimage info image```: show metadata about image
|
||||||
```ndbioimage image -w {name}.tif -r```: copy image into image.tif (replacing {name} with image), while registering channels
|
```ndbioimage tiff image image.tif -r```: copy image into image.tif, while registering channels
|
||||||
```ndbioimage image -w image.mp4 -C cyan lime red``` copy image into image.mp4 (z will be max projected), make channel colors cyan lime and red
|
```ndbioimage movie image image.mp4 -C cyan lime red``` copy image into image.mp4 (z will be max projected), make channel
|
||||||
|
colors cyan lime and red
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "ndbioimage"
|
name = "ndbioimage"
|
||||||
version = "2027.0.2"
|
version = "2027.0.3"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
|
|||||||
+47
-3
@@ -1,4 +1,47 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
//! The ndbioimage crate exposes (bio) images a struct that can be sliced like an ndarray Array
|
||||||
|
//! (Rust), but without loading the whole image into memory, reading from the file only when needed.
|
||||||
|
//! Some metadata is read
|
||||||
|
//! and stored in an [ome](https://genomebiology.biomedcentral.com/articles/10.1186/gb-2005-6-5-r47)
|
||||||
|
//! structure. Additionally, it can automatically calculate an affine transform that corrects for
|
||||||
|
//! chromatic aberrations etc. and apply it on the fly to the image.
|
||||||
|
//!
|
||||||
|
//! Currently, it supports imagej tif files, czi files, micromanager tif sequences and anything
|
||||||
|
//! [bioformats](https://www.openmicroscopy.org/bio-formats/) can handle.
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! use ndarray::Array2;
|
||||||
|
//! use ndbioimage::readers::{DynReader, Frame, Reader};
|
||||||
|
//!
|
||||||
|
//! # fn main() -> Result<(), ndbioimage::error::Error> {
|
||||||
|
//! let path = "/path/to/file";
|
||||||
|
//! let reader = DynReader::new(&path, 0, 0)?;
|
||||||
|
//! 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>");
|
||||||
|
//! }
|
||||||
|
//! let xml = reader.metadata()?.to_xml()?;
|
||||||
|
//! println!("{}", xml);
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! use ndbioimage::readers::{DynReader, Reader};
|
||||||
|
//!
|
||||||
|
//! # fn main() -> Result<(), ndbioimage::error::Error> {
|
||||||
|
//! let path = "/path/to/file";
|
||||||
|
//! let reader = DynReader::new(&path, 0, 0)?;
|
||||||
|
//! let view = reader.view();
|
||||||
|
//! let view = view.max_proj(3)?;
|
||||||
|
//! let array = view.as_array::<u16>()?;
|
||||||
|
//! println!("{:?}", array.shape());
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
|
||||||
pub mod axes;
|
pub mod axes;
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
@@ -14,7 +57,7 @@ pub mod movie;
|
|||||||
pub mod readers;
|
pub mod readers;
|
||||||
#[cfg(feature = "tiffwrite")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
pub mod tiffwrite;
|
pub mod tiffwrite;
|
||||||
// mod cache;
|
mod utils;
|
||||||
|
|
||||||
pub mod main {
|
pub mod main {
|
||||||
#[cfg(feature = "tiffwrite")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
@@ -30,7 +73,8 @@ pub mod main {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(arg_required_else_help = true, version, about, long_about = None, propagate_version = true)]
|
#[command(arg_required_else_help = true, version, about, long_about = None, propagate_version = true
|
||||||
|
)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Commands,
|
command: Commands,
|
||||||
@@ -162,7 +206,7 @@ pub mod main {
|
|||||||
output,
|
output,
|
||||||
} => {
|
} => {
|
||||||
let options = crate::tiffwrite::TiffOptions::new(
|
let options = crate::tiffwrite::TiffOptions::new(
|
||||||
Some(crate::tiffwrite::get_bar(
|
Some(crate::utils::progress::get_bar(
|
||||||
Some(0),
|
Some(0),
|
||||||
Some("writing tiff file".to_string()),
|
Some("writing tiff file".to_string()),
|
||||||
)),
|
)),
|
||||||
|
|||||||
+6
-19
@@ -2,18 +2,16 @@ use crate::axes::Axis;
|
|||||||
use crate::colors::Color;
|
use crate::colors::Color;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::readers::{PixelType, Reader};
|
use crate::readers::{PixelType, Reader};
|
||||||
|
use crate::utils::progress::get_bar;
|
||||||
use crate::view::View;
|
use crate::view::View;
|
||||||
use console::Term;
|
|
||||||
use ffmpeg_sidecar::command::FfmpegCommand;
|
use ffmpeg_sidecar::command::FfmpegCommand;
|
||||||
use ffmpeg_sidecar::download::auto_download;
|
use ffmpeg_sidecar::download::auto_download;
|
||||||
use ffmpeg_sidecar::event::{FfmpegEvent, LogLevel};
|
use ffmpeg_sidecar::event::{FfmpegEvent, LogLevel};
|
||||||
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ndarray::{Array2, Array3, Dimension, IxDyn, s, stack};
|
use ndarray::{Array2, Array3, Dimension, IxDyn, s, stack};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
pub struct MovieOptions {
|
pub struct MovieOptions {
|
||||||
velocity: f64,
|
velocity: f64,
|
||||||
@@ -138,20 +136,6 @@ fn cframe(frame: Array2<f64>, color: &[u8], a: f64, b: f64) -> Array3<f64> {
|
|||||||
stack(ndarray::Axis(2), &view).unwrap()
|
stack(ndarray::Axis(2), &view).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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>
|
impl<D, R> View<D, R>
|
||||||
where
|
where
|
||||||
D: Dimension,
|
D: Dimension,
|
||||||
@@ -252,7 +236,10 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
let rt = tokio::runtime::Runtime::new()?;
|
let rt = tokio::runtime::Runtime::new()?;
|
||||||
let bar = get_bar(Some(size_t));
|
let bar = get_bar(
|
||||||
|
Some(size_t),
|
||||||
|
Some("autoscaling movie brightness".to_string()),
|
||||||
|
);
|
||||||
let rt_bar = bar.clone();
|
let rt_bar = bar.clone();
|
||||||
let write_task = rt.spawn(async move {
|
let write_task = rt.spawn(async move {
|
||||||
for t in 0..size_t {
|
for t in 0..size_t {
|
||||||
@@ -275,7 +262,7 @@ where
|
|||||||
});
|
});
|
||||||
bar.finish();
|
bar.finish();
|
||||||
|
|
||||||
let bar = get_bar(Some(size_t));
|
let bar = get_bar(Some(size_t), Some("saving movie".to_string()));
|
||||||
let rt_bar = bar.clone();
|
let rt_bar = bar.clone();
|
||||||
let progress_task = rt.spawn(async move {
|
let progress_task = rt.spawn(async move {
|
||||||
for event in movie.iter().map_err(|e| Error::Ffmpeg(e.to_string()))? {
|
for event in movie.iter().map_err(|e| Error::Ffmpeg(e.to_string()))? {
|
||||||
|
|||||||
@@ -1741,7 +1741,7 @@ impl PyView {
|
|||||||
bar: bool,
|
bar: bool,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
let bar = if bar {
|
let bar = if bar {
|
||||||
Some(crate::tiffwrite::get_bar(
|
Some(crate::utils::progress::get_bar(
|
||||||
Some(0),
|
Some(0),
|
||||||
Some("writing tiff file".to_string()),
|
Some("writing tiff file".to_string()),
|
||||||
))
|
))
|
||||||
@@ -1991,7 +1991,16 @@ impl PyShape {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
} else if idx.is_instance_of::<PyInt>() {
|
} else if idx.is_instance_of::<PyInt>() {
|
||||||
(vec![idx.cast::<PyInt>()?.extract::<usize>()?], true)
|
let i = idx.cast::<PyInt>()?.extract::<isize>()?;
|
||||||
|
let len = self.inner.order.len() as isize;
|
||||||
|
let i = if i < 0 { i + len } else { i };
|
||||||
|
if i < 0 || i >= len {
|
||||||
|
return Err(PyIndexError::new_err(format!(
|
||||||
|
"index {} is out of bounds for size {}",
|
||||||
|
i, len
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
(vec![i as usize], true)
|
||||||
} else {
|
} else {
|
||||||
return Err(PyErr::new::<PyTypeError, _>(format!(
|
return Err(PyErr::new::<PyTypeError, _>(format!(
|
||||||
"Unknown type: {:?}",
|
"Unknown type: {:?}",
|
||||||
|
|||||||
+1
-1
@@ -136,7 +136,7 @@ pub enum ArrayT<D: Dimension> {
|
|||||||
F128(Array<f64, D>), // f128 is nightly
|
F128(Array<f64, D>), // f128 is nightly
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type Frame = ArrayT<Ix2>;
|
pub type Frame = ArrayT<Ix2>;
|
||||||
|
|
||||||
pub trait Reader: Clone + Sized + Debug + Send + Hash + Into<DynReader> {
|
pub trait Reader: Clone + Sized + Debug + Send + Hash + Into<DynReader> {
|
||||||
fn new<P>(path: P, series: usize, position: usize) -> Result<Self, Error>
|
fn new<P>(path: P, series: usize, position: usize) -> Result<Self, Error>
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub use crate::readers::{ArrayT, PixelType, Reader};
|
use crate::readers::{ArrayT, DynReader, Frame, PixelType, Reader, Shape};
|
||||||
use crate::readers::{DynReader, Frame, Shape};
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use j4rs::{Instance, InvocationArg, Jvm, JvmBuilder};
|
use j4rs::{Instance, InvocationArg, JavaOpt, Jvm, JvmBuilder};
|
||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
@@ -107,10 +106,18 @@ fn jvm() -> Rc<Jvm> {
|
|||||||
} else {
|
} else {
|
||||||
*jvm_built = true;
|
*jvm_built = true;
|
||||||
let j = JvmBuilder::new()
|
let j = JvmBuilder::new()
|
||||||
|
.skip_setting_native_lib()
|
||||||
|
.with_base_path(class_path.to_str().unwrap())
|
||||||
|
.java_opt(JavaOpt::new("--enable-native-access=ALL-UNNAMED"))
|
||||||
|
.build();
|
||||||
|
let j = match j {
|
||||||
|
Ok(j) => j,
|
||||||
|
Err(_) => JvmBuilder::new()
|
||||||
.skip_setting_native_lib()
|
.skip_setting_native_lib()
|
||||||
.with_base_path(class_path.to_str().unwrap())
|
.with_base_path(class_path.to_str().unwrap())
|
||||||
.build()
|
.build()
|
||||||
.expect("Failed to build JVM");
|
.expect("Failed to build JVM"),
|
||||||
|
};
|
||||||
if let Ok(e) = InvocationArg::try_from("ERROR") {
|
if let Ok(e) = InvocationArg::try_from("ERROR") {
|
||||||
let _ = j.invoke_static(
|
let _ = j.invoke_static(
|
||||||
"loci.common.DebugTools",
|
"loci.common.DebugTools",
|
||||||
|
|||||||
+1
-1
@@ -331,7 +331,7 @@ impl Reader for TiffReader {
|
|||||||
|
|
||||||
fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame, Error> {
|
fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame, Error> {
|
||||||
let (page_idx, offset, stride) = if self.p_ndim == 3 {
|
let (page_idx, offset, stride) = if self.p_ndim == 3 {
|
||||||
(z * self.shape.t + t, c, self.n_samples)
|
(t * self.shape.z + z, c, self.n_samples)
|
||||||
} else {
|
} else {
|
||||||
(c + z * self.shape.c + t * self.shape.c * self.shape.z, 0, 1)
|
(c + z * self.shape.c + t * self.shape.c * self.shape.z, 0, 1)
|
||||||
};
|
};
|
||||||
|
|||||||
+19
-26
@@ -4,15 +4,14 @@ use crate::error::Error;
|
|||||||
use crate::metadata::Metadata;
|
use crate::metadata::Metadata;
|
||||||
use crate::readers::{DynReader, PixelType, Reader};
|
use crate::readers::{DynReader, PixelType, Reader};
|
||||||
use crate::stats::MinMax;
|
use crate::stats::MinMax;
|
||||||
|
use crate::utils::progress::get_bar;
|
||||||
use crate::view::{Number, View};
|
use crate::view::{Number, View};
|
||||||
use console::Term;
|
use indicatif::ProgressBar;
|
||||||
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
|
|
||||||
use itertools::iproduct;
|
use itertools::iproduct;
|
||||||
use ndarray::{Array0, Array1, Array2, ArrayD, Dimension};
|
use ndarray::{Array0, Array1, Array2, ArrayD, Dimension};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{Arc, Condvar, Mutex};
|
use std::sync::{Arc, Condvar, Mutex};
|
||||||
use std::time::Duration;
|
|
||||||
use tiffwrite::{Bytes, Colors, Compression, IJTiffFile};
|
use tiffwrite::{Bytes, Colors, Compression, IJTiffFile};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -34,23 +33,6 @@ impl Default for TiffOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a progress bar with an ok style that when py::detach is used also works in jupyter
|
|
||||||
pub fn get_bar(count: Option<usize>, message: Option<String>) -> 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);
|
|
||||||
if let Some(message) = message {
|
|
||||||
bar.set_message(message);
|
|
||||||
}
|
|
||||||
bar.enable_steady_tick(Duration::from_millis(100));
|
|
||||||
bar
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TiffOptions {
|
impl TiffOptions {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
bar: Option<ProgressBar>,
|
bar: Option<ProgressBar>,
|
||||||
@@ -258,14 +240,25 @@ mod tests {
|
|||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "czi",
|
||||||
|
feature = "tiffseq",
|
||||||
|
feature = "tiff",
|
||||||
|
feature = "bioformats_java"
|
||||||
|
))]
|
||||||
#[test]
|
#[test]
|
||||||
fn tiff() -> Result<(), Error> {
|
fn tiff() -> Result<(), Error> {
|
||||||
#[cfg(any(feature = "czi", feature = "bioformats_java"))]
|
let file = if cfg!(any(feature = "czi", feature = "bioformats_java")) {
|
||||||
let file = "czi/1xp53-01-AP1.czi";
|
"czi/1xp53-01-AP1.czi"
|
||||||
#[cfg(feature = "tiff")]
|
} else if cfg!(feature = "tiff") {
|
||||||
let file = "tiff/20251014_20-Pos_000_000_loc_results_Cy3.tif";
|
"tiff/20251014_20-Pos_000_000_loc_results_Cy3.tif"
|
||||||
#[cfg(feature = "tiffseq")]
|
} else if cfg!(feature = "tiffseq") {
|
||||||
let file = "tiffseq/20-Pos_005_005";
|
"tiffseq/20-Pos_005_005"
|
||||||
|
} else {
|
||||||
|
unreachable!(
|
||||||
|
"need to enable one of these features: czi, bioformats_java, tiff, tiffseq"
|
||||||
|
);
|
||||||
|
};
|
||||||
let path = std::env::current_dir()?
|
let path = std::env::current_dir()?
|
||||||
.join("tests")
|
.join("tests")
|
||||||
.join("files")
|
.join("files")
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#[cfg(any(feature = "tiffwrite", feature = "movie"))]
|
||||||
|
pub(crate) mod progress {
|
||||||
|
use console::Term;
|
||||||
|
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressState, ProgressStyle};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// a progress bar with an ok style that when py::detach is used also works in jupyter
|
||||||
|
pub fn get_bar(count: Option<usize>, message: Option<String>) -> ProgressBar {
|
||||||
|
let style = ProgressStyle::with_template(
|
||||||
|
"{spinner:.green} {msg} {percent}% [{wide_bar:.green/lime}] {pos:>7}/{len:7} [{elapsed}/{eta}, {rate}]",
|
||||||
|
)
|
||||||
|
.expect("could not build progress bar style")
|
||||||
|
.with_key("rate", |state: &ProgressState, w: &mut dyn std::fmt::Write| {
|
||||||
|
if state.per_sec() < 1.0 {
|
||||||
|
write!(w, "{:>4.2} s", 1.0 / state.per_sec()).expect("could not write to progress bar");
|
||||||
|
} else {
|
||||||
|
write!(w, "{:>4.2}/s", state.per_sec()).expect("could not write to progress bar");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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);
|
||||||
|
if let Some(message) = message {
|
||||||
|
bar.set_message(message);
|
||||||
|
}
|
||||||
|
bar.enable_steady_tick(Duration::from_millis(100));
|
||||||
|
bar
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-6
@@ -1197,14 +1197,23 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
let _ = out.insert(a);
|
let _ = out.insert(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut n = 1;
|
let n = if let Some((&ax, op)) = op_czt.first()
|
||||||
for (ax, size) in self.shape().to_hashmap().into_iter() {
|
&& *op == Operation::Mean
|
||||||
if ((ax == Axis::C) || (ax == Axis::Z) || (ax == Axis::T))
|
|
||||||
&& let Some(Operation::Mean) = self.operations.get(&ax)
|
|
||||||
{
|
{
|
||||||
n *= size;
|
self.axes
|
||||||
}
|
.iter()
|
||||||
|
.zip(self.slice.iter())
|
||||||
|
.find(|(a, _)| **a == ax)
|
||||||
|
.and_then(|(_, s)| match s {
|
||||||
|
SliceInfoElem::Slice { start, end, step } => {
|
||||||
|
end.map(|e| (((e - start).max(0) / step) as usize).max(1))
|
||||||
}
|
}
|
||||||
|
_ => Some(1),
|
||||||
|
})
|
||||||
|
.unwrap_or(1)
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
let array = if n == 1 {
|
let array = if n == 1 {
|
||||||
out.take().unwrap()
|
out.take().unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ def array():
|
|||||||
return np.random.randint(0, 255, (64, 64, 2, 3, 4), "uint16")
|
return np.random.randint(0, 255, (64, 64, 2, 3, 4), "uint16")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def image(array):
|
def image(array):
|
||||||
with tempfile.TemporaryDirectory() as folder:
|
with tempfile.TemporaryDirectory() as folder:
|
||||||
file = Path(folder) / "tiff" / "test.tif"
|
file = Path(folder) / "tiff" / "test.tif"
|
||||||
|
file.parent.mkdir(parents=True)
|
||||||
tiffwrite(file, array, "yxczt")
|
tiffwrite(file, array, "yxczt")
|
||||||
with Imread(file, axes="yxczt") as im:
|
with Imread(file, axes="yxczt") as im:
|
||||||
yield im
|
yield im
|
||||||
|
|||||||
Reference in New Issue
Block a user