- inital transforms code
This commit is contained in:
+6
-3
@@ -19,12 +19,14 @@ name = "ndbioimage"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
bioformats = { version = "0.1", optional = true }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
color-eyre = { version = "0.6", optional = true }
|
||||
console = { version = "0.16", optional = true }
|
||||
downloader = { version = "0.2", optional = true, default-features = false, features = ["rustls-tls"] }
|
||||
ffmpeg-sidecar = { version = "2", optional = true }
|
||||
itertools = "0.15"
|
||||
image-registration = { path = "../image-registration", optional = true }
|
||||
indexmap = { version = "2", features = ["serde"] }
|
||||
indicatif = { version = "0.18", features = ["rayon"], optional = true }
|
||||
j4rs = { version = "0.25", optional = true }
|
||||
@@ -50,7 +52,6 @@ tiffwrite = { version = "2026.6.0", optional = true }
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread"], optional = true }
|
||||
thread_local = { version = "1", optional = true }
|
||||
xmltree = { version = "0.12", optional = true }
|
||||
bioformats = { version = "0.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rayon = "1"
|
||||
@@ -64,7 +65,7 @@ toml = "1"
|
||||
|
||||
[features]
|
||||
default = ["bioformats_java", "gpl-formats", "czi", "tiff", "tiffseq", "movie", "tiffwrite"]
|
||||
all = ["bioformats_java", "bioformats_rust", "czi", "gpl-formats", "movie", "tiffseq", "tiffwrite", "tiff"]
|
||||
all = ["bioformats_java", "bioformats_rust", "czi", "gpl-formats", "movie", "tiffseq", "tiffwrite", "tiff", "transforms"]
|
||||
gpl-formats = []
|
||||
python = ["dep:pyo3", "dep:numpy", "dep:color-eyre", "dep:pyo3-stub-gen", "dep:postcard", "ome-metadata/python"]
|
||||
czi = ["dep:libczirw-sys", "dep:xmltree", "dep:thread_local"]
|
||||
@@ -74,9 +75,11 @@ tiffwrite = ["dep:tiffwrite", "dep:indicatif", "dep:console", "dep:rayon"]
|
||||
tiffseq = ["dep:tiff", "dep:serde_yaml"]
|
||||
tiff = ["dep:tiff", "dep:thread_local"]
|
||||
movie = ["dep:ffmpeg-sidecar", "dep:tokio", "dep:ordered-float", "dep:indicatif", "dep:console"]
|
||||
transforms = ["dep:image-registration"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["bioformats_java", "czi", "tiff", "tiffseq", "movie", "tiffwrite", "movie"]
|
||||
no-default-features = true
|
||||
features = ["bioformats_java", "bioformats_rust", "czi", "movie", "tiffseq", "tiffwrite", "tiff", "transforms"]
|
||||
|
||||
[profile.test]
|
||||
inherits = "release"
|
||||
|
||||
@@ -20,7 +20,7 @@ To transition to semver, versions before 0.1.0 were yanked from crates.io.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
```sh
|
||||
pip install ndbioimage
|
||||
```
|
||||
|
||||
@@ -28,7 +28,7 @@ pip install ndbioimage
|
||||
|
||||
Work in progress! Make sure ffmpeg is installed.
|
||||
|
||||
```
|
||||
```sh
|
||||
pip install ndbioimage[write]
|
||||
```
|
||||
|
||||
@@ -38,7 +38,7 @@ pip install ndbioimage[write]
|
||||
|
||||
- Reading an image file and plotting the frame at channel=2, time=1
|
||||
|
||||
```
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
from ndbioimage import Imread
|
||||
with Imread('image_file.tif', axes='ctyx', dtype=int) as im:
|
||||
@@ -47,7 +47,7 @@ with Imread('image_file.tif', axes='ctyx', dtype=int) as im:
|
||||
|
||||
- Showing some image metadata
|
||||
|
||||
```
|
||||
```python
|
||||
from ndbioimage import Imread
|
||||
from pprint import pprint
|
||||
with Imread('image_file.tif') as im:
|
||||
@@ -56,7 +56,7 @@ with Imread('image_file.tif') as im:
|
||||
|
||||
- Slicing the image without loading the image into memory
|
||||
|
||||
```
|
||||
```python
|
||||
from ndbioimage import Imread
|
||||
with Imread('image_file.tif', axes='cztyx') as im:
|
||||
sliced_im = im[1, :, :, 100:200, 100:200]
|
||||
@@ -66,7 +66,7 @@ sliced_im is an instance of Imread which will load any image data from file only
|
||||
|
||||
- Converting (part) of the image to a numpy ndarray
|
||||
|
||||
```
|
||||
```python
|
||||
from ndbioimage import Imread
|
||||
import numpy as np
|
||||
with Imread('image_file.tif', axes='cztyx') as im:
|
||||
|
||||
@@ -159,13 +159,13 @@ mod python_bioformats {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("cargo::rerun-if-changed=build.rs");
|
||||
|
||||
#[cfg(feature = "bioformats_java")]
|
||||
bioformats::build()?;
|
||||
|
||||
if std::env::var("DOCS_RS").is_err() {
|
||||
#[cfg(feature = "movie")]
|
||||
ffmpeg_sidecar::download::auto_download()?;
|
||||
|
||||
#[cfg(feature = "bioformats_java")]
|
||||
bioformats::build()?;
|
||||
|
||||
#[cfg(all(not(feature = "python"), feature = "bioformats_java"))]
|
||||
no_python_bioformats::build()?;
|
||||
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
//! The ndbioimage crate exposes (bio) images a struct that can be sliced like an ndarray Array
|
||||
//! The ndbioimage crate exposes (bio) images as 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)
|
||||
@@ -68,6 +68,9 @@ pub mod readers;
|
||||
#[cfg(feature = "tiffwrite")]
|
||||
/// saving views as tiff files
|
||||
pub mod tiffwrite;
|
||||
#[cfg(feature = "transforms")]
|
||||
/// image registration and warping images using affine transforms
|
||||
mod transforms;
|
||||
mod utils;
|
||||
|
||||
/// main entry point for the application
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::error::Error;
|
||||
use crate::readers::ArrayT;
|
||||
use crate::readers::{DynReader, Frame, PixelType, Reader, Shape};
|
||||
use bioformats::{DimensionOrder, ImageReader};
|
||||
use itertools::Itertools;
|
||||
use ndarray::Array2;
|
||||
use ome_metadata::Ome;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
@@ -200,18 +201,41 @@ impl BioFormatsRustReader {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_tiff(path: &Path) -> Result<Option<PathBuf>, Error> {
|
||||
if let Some(ext) = path.extension()
|
||||
&& path.is_file()
|
||||
&& (["tif", "tiff"].contains(&ext.to_string_lossy().to_lowercase().as_str()))
|
||||
{
|
||||
return Ok(Some(path.to_path_buf()));
|
||||
} else if path.is_dir() {
|
||||
for file in path.read_dir()?.flatten().sorted_by_key(|i| i.file_name()) {
|
||||
if let Ok(Some(file)) = find_tiff(file.path().as_path()) {
|
||||
return Ok(Some(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
impl Reader for BioFormatsRustReader {
|
||||
fn new<P>(path: P, series: usize, _position: usize) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut path = path.as_ref().to_path_buf();
|
||||
if path.is_dir() {
|
||||
let orig = path.clone();
|
||||
path = find_tiff(&path)?.ok_or_else(|| {
|
||||
Error::FileDoesNotExist(orig.join("**").join("*.tif").display().to_string())
|
||||
})?;
|
||||
}
|
||||
let mut new = Self {
|
||||
reader: ThreadLocal::default(),
|
||||
path: path.as_ref().to_path_buf(),
|
||||
path,
|
||||
series,
|
||||
shape: Shape::default(),
|
||||
pixel_type: PixelType::U16,
|
||||
little_endian: true,
|
||||
pixel_type: PixelType::I8,
|
||||
little_endian: false,
|
||||
};
|
||||
let reader = new.get_reader()?;
|
||||
let metadata = reader.metadata().clone();
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use crate::readers::Reader;
|
||||
use crate::view::View;
|
||||
pub use image_registration::transform::Transform;
|
||||
use ndarray::{Dimension, Ix2, Ix3};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub enum TransformD {
|
||||
YX(Transform<Ix2>),
|
||||
ZYX(Transform<Ix3>),
|
||||
}
|
||||
|
||||
impl<D: Dimension, R: Reader> View<D, R> {}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Transforms {
|
||||
channel: Vec<TransformD>,
|
||||
drift: Vec<TransformD>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::transforms::Transforms;
|
||||
|
||||
#[test]
|
||||
fn test_transforms() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let t = Transforms::default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+14
@@ -4,6 +4,8 @@ use crate::error::Error;
|
||||
use crate::metadata::Metadata;
|
||||
use crate::readers::{Dimensions, DynReader, Frame, Reader};
|
||||
use crate::stats::MinMax;
|
||||
#[cfg(feature = "transforms")]
|
||||
use crate::transforms::Transforms;
|
||||
use indexmap::IndexMap;
|
||||
use itertools::{Itertools, iproduct};
|
||||
use ndarray::{
|
||||
@@ -88,6 +90,8 @@ pub struct View<D: Dimension, R: Reader = DynReader> {
|
||||
axes: Vec<Axis>,
|
||||
operations: IndexMap<Axis, Operation>,
|
||||
dimensionality: PhantomData<D>,
|
||||
#[cfg(feature = "transforms")]
|
||||
transforms: Transforms,
|
||||
}
|
||||
|
||||
impl<D, R> Hash for View<D, R>
|
||||
@@ -114,6 +118,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
||||
axes,
|
||||
operations: IndexMap::new(),
|
||||
dimensionality: PhantomData,
|
||||
#[cfg(feature = "transforms")]
|
||||
transforms: Transforms::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +183,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
||||
axes,
|
||||
operations: IndexMap::new(),
|
||||
dimensionality: PhantomData,
|
||||
#[cfg(feature = "transforms")]
|
||||
transforms: Transforms::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -227,6 +235,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
||||
axes: self.axes,
|
||||
operations: self.operations,
|
||||
dimensionality: PhantomData,
|
||||
#[cfg(feature = "transforms")]
|
||||
transforms: Transforms::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +250,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
||||
axes: self.axes,
|
||||
operations: self.operations,
|
||||
dimensionality: PhantomData,
|
||||
#[cfg(feature = "transforms")]
|
||||
transforms: Transforms::default(),
|
||||
})
|
||||
} else {
|
||||
Err(Error::DimensionalityMismatch(d, self.ndim()))
|
||||
@@ -251,6 +263,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
||||
axes: self.axes,
|
||||
operations: self.operations,
|
||||
dimensionality: PhantomData,
|
||||
#[cfg(feature = "transforms")]
|
||||
transforms: Transforms::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user