- added ome_xml method

- some pyo3 methods
This commit is contained in:
Wim Pomp
2025-02-08 20:22:45 +01:00
parent a3dfc075a8
commit fefdd6448b
4 changed files with 106 additions and 22 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "ndbioimage"
version = "2025.1.2"
version = "2025.2.0"
edition = "2021"
authors = ["Wim Pomp <w.pomp@nki.nl>"]
license = "GPL-3.0-or-later"
@@ -31,9 +31,9 @@ optional = true
rayon = "1.10.0"
[build-dependencies]
anyhow = { version = "1.0.95"}
j4rs = { version = "0.22", features = [] }
retry = { version = "2.0.0"}
anyhow = { version = "1.0.95"}
[features]
python = ["dep:pyo3", "dep:numpy"]

View File

@@ -31,7 +31,7 @@ macro_rules! method_arg {
macro_rules! method {
($name:ident, $method:expr $(,[$($n:tt: $t:ty$(|$p:tt)?),*])? $(=> $tt:ty$(|$c:tt)?)?) => {
pub fn $name(&self, $($($n: $t),*)?) -> method_return!($($tt)?) {
pub(crate) fn $name(&self, $($($n: $t),*)?) -> method_return!($($tt)?) {
let args: Vec<InvocationArg> = vec![$($( method_arg!($n:$t$(|$p)?) ),*)?];
let _result = jvm().invoke(&self.0, $method, &args)?;
@@ -55,10 +55,10 @@ macro_rules! method {
};
}
pub struct DebugTools;
pub(crate) struct DebugTools;
impl DebugTools {
pub fn set_root_level(level: &str) -> Result<()> {
pub(crate) fn set_root_level(level: &str) -> Result<()> {
jvm().invoke_static(
"loci.common.DebugTools",
"setRootLevel",
@@ -68,10 +68,10 @@ impl DebugTools {
}
}
pub struct ChannelSeparator(Instance);
pub(crate) struct ChannelSeparator(Instance);
impl ChannelSeparator {
pub fn new(image_reader: &ImageReader) -> Result<Self> {
pub(crate) fn new(image_reader: &ImageReader) -> Result<Self> {
let jvm = jvm();
let channel_separator = jvm.create_instance(
"loci.formats.ChannelSeparator",
@@ -80,7 +80,7 @@ impl ChannelSeparator {
Ok(ChannelSeparator(channel_separator))
}
pub fn open_bytes(&self, index: i32) -> Result<Vec<u8>> {
pub(crate) fn open_bytes(&self, index: i32) -> Result<Vec<u8>> {
let bi8 = self.open_bi8(index)?;
Ok(unsafe { std::mem::transmute::<Vec<i8>, Vec<u8>>(bi8) })
}
@@ -89,7 +89,7 @@ impl ChannelSeparator {
method!(get_index, "getIndex", [z: i32|p, c: i32|p, t: i32|p] => i32|c);
}
pub struct ImageReader(Instance);
pub(crate) struct ImageReader(Instance);
impl Drop for ImageReader {
fn drop(&mut self) {
@@ -98,17 +98,27 @@ impl Drop for ImageReader {
}
impl ImageReader {
pub fn new() -> Result<Self> {
pub(crate) fn new() -> Result<Self> {
let reader = jvm().create_instance("loci.formats.ImageReader", InvocationArg::empty())?;
Ok(ImageReader(reader))
}
pub fn open_bytes(&self, index: i32) -> Result<Vec<u8>> {
pub(crate) fn open_bytes(&self, index: i32) -> Result<Vec<u8>> {
let bi8 = self.open_bi8(index)?;
Ok(unsafe { std::mem::transmute::<Vec<i8>, Vec<u8>>(bi8) })
}
pub(crate) fn ome_xml(&self) -> Result<String> {
let mds = self.get_metadata_store()?;
Ok(jvm()
.chain(&mds)?
.cast("loci.formats.ome.OMEPyramidStore")?
.invoke("dumpXML", &[])?
.to_rust()?)
}
method!(set_metadata_store, "setMetadataStore", [ome_data: Instance]);
method!(get_metadata_store, "getMetadataStore" => Instance);
method!(set_id, "setId", [id: &str]);
method!(set_series, "setSeries", [series: i32|p]);
method!(open_bi8, "openBytes", [index: i32|p] => Vec<i8>|c);
@@ -129,10 +139,10 @@ impl ImageReader {
method!(close, "close");
}
pub struct MetadataTools(Instance);
pub(crate) struct MetadataTools(Instance);
impl MetadataTools {
pub fn new() -> Result<Self> {
pub(crate) fn new() -> Result<Self> {
let meta_data_tools =
jvm().create_instance("loci.formats.MetadataTools", InvocationArg::empty())?;
Ok(MetadataTools(meta_data_tools))

View File

@@ -144,7 +144,7 @@ pub struct Reader {
image_reader: ImageReader,
/// path to file
pub path: PathBuf,
/// which (if more) than 1 of the series in the file to open
/// which (if more than 1) of the series in the file to open
pub series: i32,
/// size x (horizontal)
pub size_x: usize,
@@ -154,7 +154,7 @@ pub struct Reader {
pub size_c: usize,
/// size z (# slices)
pub size_z: usize,
/// size t (time/frames)
/// size t (# time/frames)
pub size_t: usize,
/// pixel type ((u)int(8/16/32) or float(32/64))
pub pixel_type: PixelType,
@@ -214,6 +214,11 @@ impl Reader {
})
}
/// Get ome metadata as xml string
pub fn ome_xml(&self) -> Result<String> {
self.image_reader.ome_xml()
}
fn deinterleave(&self, bytes: Vec<u8>, channel: usize) -> Result<Vec<u8>> {
let chunk_size = match self.pixel_type {
PixelType::INT8 => 1,
@@ -311,7 +316,10 @@ mod tests {
use rayon::prelude::*;
fn open(file: &str) -> Result<Reader> {
let path = std::env::current_dir()?.join("tests").join("files").join(file);
let path = std::env::current_dir()?
.join("tests")
.join("files")
.join(file);
Reader::new(&path, 0)
}
@@ -363,4 +371,33 @@ mod tests {
println!("{:?}", frames);
Ok(())
}
#[test]
fn read_sequence() -> Result<()> {
let file = "YTL1841B2-2-1_1hr_DMSO_galinduction_1/Pos0/img_000000000_mScarlet_GFP-mSc-filter_004.tif";
let reader = open(file)?;
println!("reader: {:?}", reader);
let frame = reader.get_frame(0, 4, 0)?;
println!("frame: {:?}", frame);
let frame = reader.get_frame(0, 2, 0)?;
println!("frame: {:?}", frame);
Ok(())
}
#[test]
fn read_sequence1() -> Result<()> {
let file = "4-Pos_001_002/img_000000000_Cy3-Cy3_filter_000.tif";
let reader = open(file)?;
println!("reader: {:?}", reader);
Ok(())
}
#[test]
fn ome_xml() -> Result<()> {
let file = "Experiment-2029.czi";
let reader = open(file)?;
let xml = reader.ome_xml()?;
println!("{}", xml);
Ok(())
}
}

View File

@@ -1,14 +1,51 @@
use crate::{Frame, Reader};
use numpy::{IntoPyArray, PyArrayMethods, ToPyArray};
use pyo3::prelude::*;
use pyo3::BoundObject;
use std::path::PathBuf;
/// Formats the sum of two numbers as string.
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
#[pyclass(subclass)]
#[pyo3(name = "Reader")]
#[derive(Debug)]
struct PyReader {
path: PathBuf,
series: i32,
}
#[pymethods]
impl PyReader {
#[new]
fn new(path: &str, series: usize) -> PyResult<Self> {
Ok(PyReader {
path: PathBuf::from(path),
series: series as i32,
})
}
fn get_frame<'py>(
&self,
py: Python<'py>,
c: usize,
z: usize,
t: usize,
) -> PyResult<Bound<'py, PyAny>> {
let reader = Reader::new(&self.path, self.series)?; // TODO: prevent making a new Reader each time
Ok(match reader.get_frame(c, z, t)? {
Frame::INT8(arr) => arr.to_pyarray(py).into_any(),
Frame::UINT8(arr) => arr.to_pyarray(py).into_any(),
Frame::INT16(arr) => arr.to_pyarray(py).into_any(),
Frame::UINT16(arr) => arr.to_pyarray(py).into_any(),
Frame::INT32(arr) => arr.to_pyarray(py).into_any(),
Frame::UINT32(arr) => arr.to_pyarray(py).into_any(),
Frame::FLOAT(arr) => arr.to_pyarray(py).into_any(),
Frame::DOUBLE(arr) => arr.to_pyarray(py).into_any(),
})
}
}
/// A Python module implemented in Rust.
#[pymodule]
#[pyo3(name = "ndbioimage_rs")]
fn ndbioimage_rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
m.add_class::<PyReader>()?;
Ok(())
}