- no cloning if frame is owned already
PyTest / pytest (push) Successful in 1m26s
PyTest / pytest (3.10) (push) Successful in 1m48s
PyTest / pytest (3.12) (push) Successful in 1m59s
PyTest / pytest (3.14) (push) Successful in 2m6s

This commit is contained in:
Wim Pomp
2026-05-10 11:51:12 +02:00
parent 5a5bf9216f
commit 998b24e7af
4 changed files with 55 additions and 18 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tiffwrite" name = "tiffwrite"
version = "2026.5.0" version = "2026.5.1"
edition = "2024" edition = "2024"
rust-version = "1.88.0" rust-version = "1.88.0"
authors = ["Wim Pomp <w.pomp@nki.nl>"] authors = ["Wim Pomp <w.pomp@nki.nl>"]
+1 -1
View File
@@ -22,7 +22,7 @@ makes that very hard anyway.
or or
- install [rust](https://rustup.rs/) - install [rust](https://rustup.rs/)
- ``` pip install tiffwrite@git+https://github.com/wimpomp/tiffwrite ``` - ``` pip install tiffwrite@git+https://git.wimpomp.nl/wim/tiffwrite ```
## Usage ## Usage
### Write an image stack ### Write an image stack
-4
View File
@@ -29,10 +29,6 @@ dependencies = ["numpy", "tqdm"]
[project.optional-dependencies] [project.optional-dependencies]
test = ["pytest", "tifffile", "imagecodecs"] test = ["pytest", "tifffile", "imagecodecs"]
[project.urls]
homepage = "https://github.com/wimpomp/tiffwrite"
repository = "https://github.com/wimpomp/tiffwrite"
[project.scripts] [project.scripts]
tiffwrite_generate_stub = "tiffwrite:tiffwrite_generate_stub" tiffwrite_generate_stub = "tiffwrite:tiffwrite_generate_stub"
+53 -12
View File
@@ -9,7 +9,7 @@ use colorgrad::{Gradient, LinearGradient};
use css_color::Srgb; use css_color::Srgb;
use flate2::write::ZlibEncoder; use flate2::write::ZlibEncoder;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use ndarray::{ArcArray2, AsArray, Ix2, s}; use ndarray::{Array, Array2, ArrayView, ArrayView2, Dimension, Ix2, s};
use num::{Complex, FromPrimitive, Rational32, traits::ToBytes}; use num::{Complex, FromPrimitive, Rational32, traits::ToBytes};
use rayon::prelude::*; use rayon::prelude::*;
use std::collections::HashSet; use std::collections::HashSet;
@@ -380,7 +380,7 @@ impl Frame {
fn new<T>( fn new<T>(
hashes: Arc<Mutex<HashMap<u64, u64>>>, hashes: Arc<Mutex<HashMap<u64, u64>>>,
file: Arc<Mutex<BufWriter<File>>>, file: Arc<Mutex<BufWriter<File>>>,
frame: ArcArray2<T>, frame: Array2<T>,
compression: Compression, compression: Compression,
) -> Result<Frame, Error> ) -> Result<Frame, Error>
where where
@@ -434,14 +434,14 @@ impl Frame {
slices slices
.into_par_iter() .into_par_iter()
.map(|slice| { .map(|slice| {
Frame::compress_tile_deflate(frame.clone(), slice, tile_size, tile_size) Frame::compress_tile_deflate(frame.view(), slice, tile_size, tile_size)
}) })
.collect::<Result<Vec<_>, Error>>()? .collect::<Result<Vec<_>, Error>>()?
} else { } else {
slices slices
.into_iter() .into_iter()
.map(|slice| { .map(|slice| {
Frame::compress_tile_deflate(frame.clone(), slice, tile_size, tile_size) Frame::compress_tile_deflate(frame.view(), slice, tile_size, tile_size)
}) })
.collect::<Result<Vec<_>, Error>>()? .collect::<Result<Vec<_>, Error>>()?
} }
@@ -453,7 +453,7 @@ impl Frame {
.into_par_iter() .into_par_iter()
.map(|slice| { .map(|slice| {
Frame::compress_tile_zstd( Frame::compress_tile_zstd(
frame.clone(), frame.view(),
slice, slice,
tile_size, tile_size,
tile_size, tile_size,
@@ -466,7 +466,7 @@ impl Frame {
.into_iter() .into_iter()
.map(|slice| { .map(|slice| {
Frame::compress_tile_zstd( Frame::compress_tile_zstd(
frame.clone(), frame.view(),
slice, slice,
tile_size, tile_size,
tile_size, tile_size,
@@ -501,7 +501,7 @@ impl Frame {
fn encode<W, T>( fn encode<W, T>(
mut encoder: W, mut encoder: W,
frame: ArcArray2<T>, frame: ArrayView2<T>,
slice: (usize, usize, usize, usize), slice: (usize, usize, usize, usize),
tile_width: usize, tile_width: usize,
tile_length: usize, tile_length: usize,
@@ -532,7 +532,7 @@ impl Frame {
} }
fn compress_tile_deflate<T>( fn compress_tile_deflate<T>(
frame: ArcArray2<T>, frame: ArrayView2<T>,
slice: (usize, usize, usize, usize), slice: (usize, usize, usize, usize),
tile_width: usize, tile_width: usize,
tile_length: usize, tile_length: usize,
@@ -546,7 +546,7 @@ impl Frame {
} }
fn compress_tile_zstd<T>( fn compress_tile_zstd<T>(
frame: ArcArray2<T>, frame: ArrayView2<T>,
slice: (usize, usize, usize, usize), slice: (usize, usize, usize, usize),
tile_width: usize, tile_width: usize,
tile_length: usize, tile_length: usize,
@@ -621,6 +621,47 @@ pub enum Colors {
Colormap(Vec<Vec<u8>>), Colormap(Vec<Vec<u8>>),
} }
/// Clone if array is a view, do not clone if array is owned already.
pub trait IntoOwnedArray<T, D> {
fn into_owned(self) -> Array<T, D>;
}
impl<T, D> IntoOwnedArray<T, D> for Array<T, D> {
fn into_owned(self) -> Array<T, D> {
self
}
}
impl<T, D> IntoOwnedArray<T, D> for &Array<T, D>
where
T: Clone,
D: Dimension,
{
fn into_owned(self) -> Array<T, D> {
self.to_owned()
}
}
impl<T, D> IntoOwnedArray<T, D> for ArrayView<'_, T, D>
where
T: Clone,
D: Dimension,
{
fn into_owned(self) -> Array<T, D> {
self.to_owned()
}
}
impl<T, D> IntoOwnedArray<T, D> for &ArrayView<'_, T, D>
where
T: Clone,
D: Dimension,
{
fn into_owned(self) -> Array<T, D> {
self.to_owned()
}
}
/// save 2d arrays in a tif file compatible with Fiji/ImageJ /// save 2d arrays in a tif file compatible with Fiji/ImageJ
#[derive(Debug)] #[derive(Debug)]
pub struct IJTiffFile { pub struct IJTiffFile {
@@ -842,15 +883,15 @@ impl IJTiffFile {
} }
/// save a 2d array to the tiff file at channel c, slice z, and time t /// save a 2d array to the tiff file at channel c, slice z, and time t
pub fn save<'a, A, T>(&mut self, frame: A, c: usize, z: usize, t: usize) -> Result<(), Error> pub fn save<A, T>(&mut self, frame: A, c: usize, z: usize, t: usize) -> Result<(), Error>
where where
A: AsArray<'a, T, Ix2>, A: IntoOwnedArray<T, Ix2>,
T: Bytes + Clone + Send + Sync + 'static, T: Bytes + Clone + Send + Sync + 'static,
{ {
self.collect_threads(false, usize::from(available_parallelism()?))?; self.collect_threads(false, usize::from(available_parallelism()?))?;
let hashes = self.hashes.clone(); let hashes = self.hashes.clone();
let file = self.file.clone(); let file = self.file.clone();
let frame = frame.into().to_shared(); let frame = frame.into_owned();
let compression = self.compression; let compression = self.compression;
self.threads.insert( self.threads.insert(
(c, z, t), (c, z, t),