- inital transforms code
This commit is contained in:
+6
-3
@@ -19,12 +19,14 @@ name = "ndbioimage"
|
|||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bioformats = { version = "0.1", optional = true }
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
color-eyre = { version = "0.6", optional = true }
|
color-eyre = { version = "0.6", optional = true }
|
||||||
console = { version = "0.16", optional = true }
|
console = { version = "0.16", optional = true }
|
||||||
downloader = { version = "0.2", optional = true, default-features = false, features = ["rustls-tls"] }
|
downloader = { version = "0.2", optional = true, default-features = false, features = ["rustls-tls"] }
|
||||||
ffmpeg-sidecar = { version = "2", optional = true }
|
ffmpeg-sidecar = { version = "2", optional = true }
|
||||||
itertools = "0.15"
|
itertools = "0.15"
|
||||||
|
image-registration = { path = "../image-registration", optional = true }
|
||||||
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 }
|
||||||
@@ -50,7 +52,6 @@ tiffwrite = { version = "2026.6.0", optional = true }
|
|||||||
tokio = { version = "1", features = ["rt", "rt-multi-thread"], optional = true }
|
tokio = { version = "1", features = ["rt", "rt-multi-thread"], optional = true }
|
||||||
thread_local = { version = "1", optional = true }
|
thread_local = { version = "1", optional = true }
|
||||||
xmltree = { version = "0.12", optional = true }
|
xmltree = { version = "0.12", optional = true }
|
||||||
bioformats = { version = "0.1", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rayon = "1"
|
rayon = "1"
|
||||||
@@ -64,7 +65,7 @@ toml = "1"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["bioformats_java", "gpl-formats", "czi", "tiff", "tiffseq", "movie", "tiffwrite"]
|
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 = []
|
gpl-formats = []
|
||||||
python = ["dep:pyo3", "dep:numpy", "dep:color-eyre", "dep:pyo3-stub-gen", "dep:postcard", "ome-metadata/python"]
|
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"]
|
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"]
|
tiffseq = ["dep:tiff", "dep:serde_yaml"]
|
||||||
tiff = ["dep:tiff", "dep:thread_local"]
|
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"]
|
||||||
|
transforms = ["dep:image-registration"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[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]
|
[profile.test]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ To transition to semver, versions before 0.1.0 were yanked from crates.io.
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```
|
```sh
|
||||||
pip install ndbioimage
|
pip install ndbioimage
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ pip install ndbioimage
|
|||||||
|
|
||||||
Work in progress! Make sure ffmpeg is installed.
|
Work in progress! Make sure ffmpeg is installed.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
pip install ndbioimage[write]
|
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
|
- Reading an image file and plotting the frame at channel=2, time=1
|
||||||
|
|
||||||
```
|
```python
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from ndbioimage import Imread
|
from ndbioimage import Imread
|
||||||
with Imread('image_file.tif', axes='ctyx', dtype=int) as im:
|
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
|
- Showing some image metadata
|
||||||
|
|
||||||
```
|
```python
|
||||||
from ndbioimage import Imread
|
from ndbioimage import Imread
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
with Imread('image_file.tif') as im:
|
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
|
- Slicing the image without loading the image into memory
|
||||||
|
|
||||||
```
|
```python
|
||||||
from ndbioimage import Imread
|
from ndbioimage import Imread
|
||||||
with Imread('image_file.tif', axes='cztyx') as im:
|
with Imread('image_file.tif', axes='cztyx') as im:
|
||||||
sliced_im = im[1, :, :, 100:200, 100:200]
|
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
|
- Converting (part) of the image to a numpy ndarray
|
||||||
|
|
||||||
```
|
```python
|
||||||
from ndbioimage import Imread
|
from ndbioimage import Imread
|
||||||
import numpy as np
|
import numpy as np
|
||||||
with Imread('image_file.tif', axes='cztyx') as im:
|
with Imread('image_file.tif', axes='cztyx') as im:
|
||||||
|
|||||||
@@ -159,13 +159,13 @@ mod python_bioformats {
|
|||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("cargo::rerun-if-changed=build.rs");
|
println!("cargo::rerun-if-changed=build.rs");
|
||||||
|
|
||||||
|
#[cfg(feature = "bioformats_java")]
|
||||||
|
bioformats::build()?;
|
||||||
|
|
||||||
if std::env::var("DOCS_RS").is_err() {
|
if std::env::var("DOCS_RS").is_err() {
|
||||||
#[cfg(feature = "movie")]
|
#[cfg(feature = "movie")]
|
||||||
ffmpeg_sidecar::download::auto_download()?;
|
ffmpeg_sidecar::download::auto_download()?;
|
||||||
|
|
||||||
#[cfg(feature = "bioformats_java")]
|
|
||||||
bioformats::build()?;
|
|
||||||
|
|
||||||
#[cfg(all(not(feature = "python"), feature = "bioformats_java"))]
|
#[cfg(all(not(feature = "python"), feature = "bioformats_java"))]
|
||||||
no_python_bioformats::build()?;
|
no_python_bioformats::build()?;
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -1,5 +1,5 @@
|
|||||||
#![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
|
//! 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.
|
//! (Rust), but without loading the whole image into memory, reading from the file only when needed.
|
||||||
//! Some metadata is read
|
//! Some metadata is read
|
||||||
//! and stored in an [ome](https://genomebiology.biomedcentral.com/articles/10.1186/gb-2005-6-5-r47)
|
//! 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")]
|
#[cfg(feature = "tiffwrite")]
|
||||||
/// saving views as tiff files
|
/// saving views as tiff files
|
||||||
pub mod tiffwrite;
|
pub mod tiffwrite;
|
||||||
|
#[cfg(feature = "transforms")]
|
||||||
|
/// image registration and warping images using affine transforms
|
||||||
|
mod transforms;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
/// main entry point for the application
|
/// main entry point for the application
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use crate::error::Error;
|
|||||||
use crate::readers::ArrayT;
|
use crate::readers::ArrayT;
|
||||||
use crate::readers::{DynReader, Frame, PixelType, Reader, Shape};
|
use crate::readers::{DynReader, Frame, PixelType, Reader, Shape};
|
||||||
use bioformats::{DimensionOrder, ImageReader};
|
use bioformats::{DimensionOrder, ImageReader};
|
||||||
|
use itertools::Itertools;
|
||||||
use ndarray::Array2;
|
use ndarray::Array2;
|
||||||
use ome_metadata::Ome;
|
use ome_metadata::Ome;
|
||||||
use std::cell::{RefCell, RefMut};
|
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 {
|
impl Reader for BioFormatsRustReader {
|
||||||
fn new<P>(path: P, series: usize, _position: usize) -> Result<Self, Error>
|
fn new<P>(path: P, series: usize, _position: usize) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
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 {
|
let mut new = Self {
|
||||||
reader: ThreadLocal::default(),
|
reader: ThreadLocal::default(),
|
||||||
path: path.as_ref().to_path_buf(),
|
path,
|
||||||
series,
|
series,
|
||||||
shape: Shape::default(),
|
shape: Shape::default(),
|
||||||
pixel_type: PixelType::U16,
|
pixel_type: PixelType::I8,
|
||||||
little_endian: true,
|
little_endian: false,
|
||||||
};
|
};
|
||||||
let reader = new.get_reader()?;
|
let reader = new.get_reader()?;
|
||||||
let metadata = reader.metadata().clone();
|
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::metadata::Metadata;
|
||||||
use crate::readers::{Dimensions, DynReader, Frame, Reader};
|
use crate::readers::{Dimensions, DynReader, Frame, Reader};
|
||||||
use crate::stats::MinMax;
|
use crate::stats::MinMax;
|
||||||
|
#[cfg(feature = "transforms")]
|
||||||
|
use crate::transforms::Transforms;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use itertools::{Itertools, iproduct};
|
use itertools::{Itertools, iproduct};
|
||||||
use ndarray::{
|
use ndarray::{
|
||||||
@@ -88,6 +90,8 @@ pub struct View<D: Dimension, R: Reader = DynReader> {
|
|||||||
axes: Vec<Axis>,
|
axes: Vec<Axis>,
|
||||||
operations: IndexMap<Axis, Operation>,
|
operations: IndexMap<Axis, Operation>,
|
||||||
dimensionality: PhantomData<D>,
|
dimensionality: PhantomData<D>,
|
||||||
|
#[cfg(feature = "transforms")]
|
||||||
|
transforms: Transforms,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D, R> Hash for View<D, R>
|
impl<D, R> Hash for View<D, R>
|
||||||
@@ -114,6 +118,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
axes,
|
axes,
|
||||||
operations: IndexMap::new(),
|
operations: IndexMap::new(),
|
||||||
dimensionality: PhantomData,
|
dimensionality: PhantomData,
|
||||||
|
#[cfg(feature = "transforms")]
|
||||||
|
transforms: Transforms::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +183,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
axes,
|
axes,
|
||||||
operations: IndexMap::new(),
|
operations: IndexMap::new(),
|
||||||
dimensionality: PhantomData,
|
dimensionality: PhantomData,
|
||||||
|
#[cfg(feature = "transforms")]
|
||||||
|
transforms: Transforms::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,6 +235,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
axes: self.axes,
|
axes: self.axes,
|
||||||
operations: self.operations,
|
operations: self.operations,
|
||||||
dimensionality: PhantomData,
|
dimensionality: PhantomData,
|
||||||
|
#[cfg(feature = "transforms")]
|
||||||
|
transforms: Transforms::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +250,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
axes: self.axes,
|
axes: self.axes,
|
||||||
operations: self.operations,
|
operations: self.operations,
|
||||||
dimensionality: PhantomData,
|
dimensionality: PhantomData,
|
||||||
|
#[cfg(feature = "transforms")]
|
||||||
|
transforms: Transforms::default(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::DimensionalityMismatch(d, self.ndim()))
|
Err(Error::DimensionalityMismatch(d, self.ndim()))
|
||||||
@@ -251,6 +263,8 @@ impl<D: Dimension, R: Reader> View<D, R> {
|
|||||||
axes: self.axes,
|
axes: self.axes,
|
||||||
operations: self.operations,
|
operations: self.operations,
|
||||||
dimensionality: PhantomData,
|
dimensionality: PhantomData,
|
||||||
|
#[cfg(feature = "transforms")]
|
||||||
|
transforms: Transforms::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user