use crate::{Colors, Compression, IJTiffFile, Tag}; use ndarray::s; use num::{Complex, FromPrimitive, Rational32}; use numpy::{PyArrayMethods, PyReadonlyArray2}; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; #[pyclass(subclass)] #[pyo3(name = "Tag")] #[derive(Clone, Debug)] struct PyTag { tag: Tag, } /// Tiff tag, use one of the constructors to get a tag of a specific type #[pymethods] impl PyTag { #[staticmethod] fn byte(code: u16, byte: Vec) -> Self { PyTag { tag: Tag::byte(code, &byte), } } #[staticmethod] fn ascii(code: u16, ascii: &str) -> Self { PyTag { tag: Tag::ascii(code, ascii), } } #[staticmethod] fn short(code: u16, short: Vec) -> Self { PyTag { tag: Tag::short(code, &short), } } #[staticmethod] fn long(code: u16, long: Vec) -> Self { PyTag { tag: Tag::long(code, &long), } } #[staticmethod] fn rational(code: u16, rational: Vec) -> Self { PyTag { tag: Tag::rational( code, &rational .into_iter() .map(|x| Rational32::from_f64(x).unwrap()) .collect::>(), ), } } #[staticmethod] fn sbyte(code: u16, sbyte: Vec) -> Self { PyTag { tag: Tag::sbyte(code, &sbyte), } } #[staticmethod] fn sshort(code: u16, sshort: Vec) -> Self { PyTag { tag: Tag::sshort(code, &sshort), } } #[staticmethod] fn slong(code: u16, slong: Vec) -> Self { PyTag { tag: Tag::slong(code, &slong), } } #[staticmethod] fn srational(code: u16, srational: Vec) -> Self { PyTag { tag: Tag::srational( code, &srational .into_iter() .map(|x| Rational32::from_f64(x).unwrap()) .collect::>(), ), } } #[staticmethod] fn float(code: u16, float: Vec) -> Self { PyTag { tag: Tag::float(code, &float), } } #[staticmethod] fn double(code: u16, double: Vec) -> Self { PyTag { tag: Tag::double(code, &double), } } #[staticmethod] fn ifd(code: u16, ifd: Vec) -> Self { PyTag { tag: Tag::ifd(code, &ifd), } } #[staticmethod] fn unicode(code: u16, unicode: &str) -> Self { PyTag { tag: Tag::unicode(code, unicode), } } #[staticmethod] fn complex(code: u16, complex: Vec<(f32, f32)>) -> Self { PyTag { tag: Tag::complex( code, &complex .into_iter() .map(|(x, y)| Complex { re: x, im: y }) .collect::>(), ), } } #[staticmethod] fn long8(code: u16, long8: Vec) -> Self { PyTag { tag: Tag::long8(code, &long8), } } #[staticmethod] fn slong8(code: u16, slong8: Vec) -> Self { PyTag { tag: Tag::slong8(code, &slong8), } } #[staticmethod] fn ifd8(code: u16, ifd8: Vec) -> Self { PyTag { tag: Tag::ifd8(code, &ifd8), } } /// get the number of values in the tag fn count(&self) -> u64 { self.tag.count() } } #[pyclass(subclass)] #[pyo3(name = "IJTiffFile")] #[derive(Debug)] struct PyIJTiffFile { ijtifffile: Option, } #[pymethods] impl PyIJTiffFile { #[new] fn new(path: &str) -> PyResult { Ok(PyIJTiffFile { ijtifffile: Some(IJTiffFile::new(path)?), }) } /// set zstd compression level: -7 ..= 22 fn set_compression(&mut self, compression: i32, level: i32) -> PyResult<()> { let c = match compression { 50000 => Compression::Zstd(level.clamp(-7, 22)), 8 => Compression::Deflate, _ => { return Err(PyValueError::new_err(format!( "Unknown compression {}", compression ))); } }; if let Some(ref mut ijtifffile) = self.ijtifffile { ijtifffile.set_compression(c) } Ok(()) } #[getter] fn get_colors(&self) -> PyResult>>> { if let Some(ijtifffile) = &self.ijtifffile { if let Colors::Colors(colors) = &ijtifffile.colors { return Ok(Some(colors.to_owned())); } } Ok(None) } #[setter] fn set_colors(&mut self, colors: Vec) -> PyResult<()> { if let Some(ijtifffile) = &mut self.ijtifffile { ijtifffile.set_colors(&colors)?; } Ok(()) } #[getter] fn get_colormap(&mut self) -> PyResult>>> { if let Some(ijtifffile) = &self.ijtifffile { if let Colors::Colormap(colormap) = &ijtifffile.colors { return Ok(Some(colormap.to_owned())); } } Ok(None) } #[setter] fn set_colormap(&mut self, colormap: PyReadonlyArray2) -> PyResult<()> { if let Some(ijtifffile) = &mut self.ijtifffile { let a = colormap.to_owned_array(); ijtifffile.colors = Colors::Colormap( (0..a.shape()[0]) .map(|i| Vec::from(a.slice(s![i, ..]).as_slice().unwrap())) .collect(), ); } Ok(()) } #[getter] fn get_px_size(&self) -> PyResult> { if let Some(ijtifffile) = &self.ijtifffile { Ok(ijtifffile.px_size) } else { Ok(None) } } #[setter] fn set_px_size(&mut self, px_size: f64) -> PyResult<()> { if let Some(ijtifffile) = &mut self.ijtifffile { ijtifffile.px_size = Some(px_size); } Ok(()) } #[getter] fn get_delta_z(&self) -> PyResult> { if let Some(ijtifffile) = &self.ijtifffile { Ok(ijtifffile.delta_z) } else { Ok(None) } } #[setter] fn set_delta_z(&mut self, delta_z: f64) -> PyResult<()> { if let Some(ijtifffile) = &mut self.ijtifffile { ijtifffile.delta_z = Some(delta_z); } Ok(()) } #[getter] fn get_time_interval(&self) -> PyResult> { if let Some(ijtifffile) = &self.ijtifffile { Ok(ijtifffile.time_interval) } else { Ok(None) } } #[setter] fn set_time_interval(&mut self, time_interval: f64) -> PyResult<()> { if let Some(ijtifffile) = &mut self.ijtifffile { ijtifffile.time_interval = Some(time_interval); } Ok(()) } #[getter] fn get_comment(&self) -> PyResult> { if let Some(ijtifffile) = &self.ijtifffile { Ok(ijtifffile.comment.clone()) } else { Ok(None) } } #[setter] fn set_comment(&mut self, comment: &str) -> PyResult<()> { if let Some(ijtifffile) = &mut self.ijtifffile { ijtifffile.comment = Some(String::from(comment)); } Ok(()) } #[pyo3(signature = (tag, czt=None))] fn append_extra_tag(&mut self, tag: PyTag, czt: Option<(usize, usize, usize)>) { if let Some(ijtifffile) = self.ijtifffile.as_mut() { if let Some(extra_tags) = ijtifffile.extra_tags.get_mut(&czt) { extra_tags.push(tag.tag) } } } #[pyo3(signature = (czt=None))] fn get_tags(&self, czt: Option<(usize, usize, usize)>) -> PyResult> { if let Some(ijtifffile) = &self.ijtifffile { if let Some(extra_tags) = ijtifffile.extra_tags.get(&czt) { let v = extra_tags .iter() .map(|tag| PyTag { tag: tag.to_owned(), }) .collect(); return Ok(v); } } Ok(Vec::new()) } fn close(&mut self) -> PyResult<()> { self.ijtifffile.take(); Ok(()) } } macro_rules! impl_save { ($T:ty, $t:ident) => { #[pymethods] impl PyIJTiffFile { fn $t( &mut self, frame: PyReadonlyArray2<$T>, c: usize, t: usize, z: usize, ) -> PyResult<()> { if let Some(ijtifffile) = self.ijtifffile.as_mut() { ijtifffile.save(frame.as_array(), c, t, z)?; } Ok(()) } } }; } impl_save!(u8, save_u8); impl_save!(u16, save_u16); impl_save!(u32, save_u32); impl_save!(u64, save_u64); impl_save!(i8, save_i8); impl_save!(i16, save_i16); impl_save!(i32, save_i32); impl_save!(i64, save_i64); impl_save!(f32, save_f32); impl_save!(f64, save_f64); #[pymodule] #[pyo3(name = "tiffwrite_rs")] fn tiffwrite_rs(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; Ok(()) }