- change to MIT license

- thread local image reader instances
- add keywords and categories
- add python dependencies
- README
This commit is contained in:
Wim Pomp
2025-02-17 19:53:34 +01:00
parent e5c6361086
commit 2247a994be
8 changed files with 243 additions and 727 deletions

View File

@@ -71,6 +71,7 @@ macro_rules! method_arg {
macro_rules! method {
($name:ident, $method:expr $(,[$($n:tt: $t:ty$(|$p:tt)?),*])? $(=> $tt:ty$(|$c:tt)?)?) => {
#[allow(dead_code)]
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)?;
@@ -95,10 +96,12 @@ macro_rules! method {
};
}
pub(crate) struct DebugTools;
/// Wrapper around bioformats java class loci.common.DebugTools
pub struct DebugTools;
impl DebugTools {
pub(crate) fn set_root_level(level: &str) -> Result<()> {
/// set debug root level: ERROR, DEBUG, TRACE, INFO, OFF
pub fn set_root_level(level: &str) -> Result<()> {
jvm().invoke_static(
"loci.common.DebugTools",
"setRootLevel",
@@ -108,6 +111,7 @@ impl DebugTools {
}
}
/// Wrapper around bioformats java class loci.formats.ChannelSeparator
pub(crate) struct ChannelSeparator(Instance);
impl ChannelSeparator {
@@ -129,7 +133,8 @@ impl ChannelSeparator {
method!(get_index, "getIndex", [z: i32|p, c: i32|p, t: i32|p] => i32|c);
}
pub(crate) struct ImageReader(Instance);
/// Wrapper around bioformats java class loci.formats.ImageReader
pub struct ImageReader(Instance);
impl Drop for ImageReader {
fn drop(&mut self) {
@@ -179,6 +184,7 @@ impl ImageReader {
method!(close, "close");
}
/// Wrapper around bioformats java class loci.formats.MetadataTools
pub(crate) struct MetadataTools(Instance);
impl MetadataTools {

View File

@@ -9,7 +9,9 @@ use ndarray::Array2;
use num::{FromPrimitive, Zero};
use std::any::type_name;
use std::fmt::Debug;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use thread_local::ThreadLocal;
/// Pixel types (u)int(8/16/32) or float(32/64)
#[derive(Clone, Debug)]
@@ -141,7 +143,7 @@ where
/// Reader interface to file. Use get_frame to get data.
pub struct Reader {
image_reader: ImageReader,
image_reader: ThreadLocal<ImageReader>,
/// path to file
pub path: PathBuf,
/// which (if more than 1) of the series in the file to open
@@ -161,6 +163,22 @@ pub struct Reader {
little_endian: bool,
}
impl Deref for Reader {
type Target = ImageReader;
fn deref(&self) -> &Self::Target {
self.image_reader.get_or(|| {
let reader = ImageReader::new().unwrap();
let meta_data_tools = MetadataTools::new().unwrap();
let ome_meta = meta_data_tools.create_ome_xml_metadata().unwrap();
reader.set_metadata_store(ome_meta).unwrap();
reader.set_id(self.path.to_str().unwrap()).unwrap();
reader.set_series(self.series).unwrap();
reader
})
}
}
impl Clone for Reader {
fn clone(&self) -> Self {
Reader::new(&self.path, self.series).unwrap()
@@ -187,36 +205,31 @@ impl Reader {
/// Create new reader for image file at path.
pub fn new(path: &Path, series: i32) -> Result<Self> {
DebugTools::set_root_level("ERROR")?;
let reader = ImageReader::new()?;
let meta_data_tools = MetadataTools::new()?;
let ome_meta = meta_data_tools.create_ome_xml_metadata()?;
reader.set_metadata_store(ome_meta)?;
reader.set_id(path.to_str().unwrap())?;
reader.set_series(series)?;
let size_x = reader.get_size_x()?;
let size_y = reader.get_size_y()?;
let size_c = reader.get_size_c()?;
let size_z = reader.get_size_z()?;
let size_t = reader.get_size_t()?;
let pixel_type = PixelType::try_from(reader.get_pixel_type()?)?;
let little_endian = reader.is_little_endian()?;
Ok(Reader {
image_reader: reader,
let mut reader = Reader {
image_reader: ThreadLocal::new(),
path: PathBuf::from(path),
series,
size_x: size_x as usize,
size_y: size_y as usize,
size_c: size_c as usize,
size_z: size_z as usize,
size_t: size_t as usize,
pixel_type,
little_endian,
})
size_x: 0,
size_y: 0,
size_c: 0,
size_z: 0,
size_t: 0,
pixel_type: PixelType::INT8,
little_endian: false,
};
reader.size_x = reader.get_size_x()? as usize;
reader.size_y = reader.get_size_y()? as usize;
reader.size_c = reader.get_size_c()? as usize;
reader.size_z = reader.get_size_z()? as usize;
reader.size_t = reader.get_size_t()? as usize;
reader.pixel_type = PixelType::try_from(reader.get_pixel_type()?)?;
reader.little_endian = reader.is_little_endian()?;
Ok(reader)
}
/// Get ome metadata as xml string
pub fn get_ome_xml(&self) -> Result<String> {
self.image_reader.ome_xml()
self.ome_xml()
}
fn deinterleave(&self, bytes: Vec<u8>, channel: usize) -> Result<Vec<u8>> {
@@ -240,16 +253,16 @@ impl Reader {
/// Retrieve fame at channel c, slize z and time t.
pub fn get_frame(&self, c: usize, z: usize, t: usize) -> Result<Frame> {
let bytes = if self.image_reader.is_rgb()? & self.image_reader.is_interleaved()? {
let index = self.image_reader.get_index(z as i32, 0, t as i32)?;
self.deinterleave(self.image_reader.open_bytes(index)?, c)?
} else if self.image_reader.get_rgb_channel_count()? > 1 {
let channel_separator = bioformats::ChannelSeparator::new(&self.image_reader)?;
let bytes = if self.is_rgb()? & self.is_interleaved()? {
let index = self.get_index(z as i32, 0, t as i32)?;
self.deinterleave(self.open_bytes(index)?, c)?
} else if self.get_rgb_channel_count()? > 1 {
let channel_separator = bioformats::ChannelSeparator::new(self)?;
let index = channel_separator.get_index(z as i32, c as i32, t as i32)?;
channel_separator.open_bytes(index)?
} else if self.image_reader.is_indexed()? {
let index = self.image_reader.get_index(z as i32, 0, t as i32)?;
self.image_reader.open_bytes(index)?
} else if self.is_indexed()? {
let index = self.get_index(z as i32, 0, t as i32)?;
self.open_bytes(index)?
// TODO: apply LUT
// let _bytes_lut = match self.pixel_type {
// PixelType::INT8 | PixelType::UINT8 => {
@@ -261,8 +274,8 @@ impl Reader {
// _ => {}
// };
} else {
let index = self.image_reader.get_index(z as i32, c as i32, t as i32)?;
self.image_reader.open_bytes(index)?
let index = self.get_index(z as i32, c as i32, t as i32)?;
self.open_bytes(index)?
};
self.bytes_to_frame(bytes)
}

View File

@@ -8,17 +8,16 @@ use std::path::PathBuf;
#[pyo3(name = "Reader")]
#[derive(Debug)]
struct PyReader {
path: PathBuf,
series: i32,
reader: Reader,
}
#[pymethods]
impl PyReader {
#[new]
fn new(path: &str, series: usize) -> PyResult<Self> {
let path = PathBuf::from(path);
Ok(PyReader {
path: PathBuf::from(path),
series: series as i32,
reader: Reader::new(&path, series as i32)?,
})
}
@@ -29,8 +28,7 @@ impl PyReader {
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)? {
Ok(match self.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(),
@@ -43,8 +41,7 @@ impl PyReader {
}
fn get_ome_xml(&self) -> PyResult<String> {
let reader = Reader::new(&self.path, self.series)?; // TODO: prevent making a new Reader each time
Ok(reader.get_ome_xml()?)
Ok(self.reader.get_ome_xml()?)
}
}