From 7e008e3118c788c343f7c5d08c952fbedcdfcec1 Mon Sep 17 00:00:00 2001 From: caleb Date: Sun, 15 Oct 2023 01:15:38 +0300 Subject: [PATCH] python: Expose new functions on the python side Signed-off-by: caleb --- zune-python/src/lib.rs | 3 +- zune-python/src/py_enums.rs | 34 ++++++++++++ zune-python/src/py_image.rs | 101 ++++++++++++++++++++++++++++++++++-- 3 files changed, 134 insertions(+), 4 deletions(-) diff --git a/zune-python/src/lib.rs b/zune-python/src/lib.rs index 90d36cc1..65a8aa4b 100644 --- a/zune-python/src/lib.rs +++ b/zune-python/src/lib.rs @@ -10,7 +10,7 @@ use py_functions::*; use py_image::*; use pyo3::prelude::*; -use crate::py_enums::{PyImageColorSpace, PyImageDepth, PyImageFormats}; +use crate::py_enums::{PyImageColorSpace, PyImageDepth, PyImageFormats, PyImageThresholdType}; mod py_enums; mod py_functions; @@ -23,6 +23,7 @@ fn zune_python(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; m.add_function(wrap_pyfunction!(guess_format, m)?)?; m.add_function(wrap_pyfunction!(decode_image, m)?)?; diff --git a/zune-python/src/py_enums.rs b/zune-python/src/py_enums.rs index 5b5d3c3f..06a56cf0 100644 --- a/zune-python/src/py_enums.rs +++ b/zune-python/src/py_enums.rs @@ -11,6 +11,7 @@ use zune_core::bit_depth::BitDepth; use zune_core::colorspace::ColorSpace; use zune_image::codecs::ImageFormat; use zune_image::errors::ImageErrors; +use zune_image::filters::threshold::ThresholdMethod; #[pyclass] pub struct PyImageErrors { @@ -135,6 +136,7 @@ impl From for PyImageColorSpace { } #[pyclass] +#[derive(Copy, Clone, Debug)] pub enum PyImageDepth { Eight, Sixteen, @@ -142,6 +144,17 @@ pub enum PyImageDepth { Unknown } +impl PyImageDepth { + pub(crate) fn to_depth(self) -> BitDepth { + match self { + PyImageDepth::Eight => BitDepth::Eight, + PyImageDepth::Sixteen => BitDepth::Sixteen, + PyImageDepth::F32 => BitDepth::Float32, + PyImageDepth::Unknown => BitDepth::Unknown + } + } +} + impl From for PyImageDepth { fn from(value: BitDepth) -> Self { match value { @@ -153,3 +166,24 @@ impl From for PyImageDepth { } } } + +/// Different threshold arguments for the threshold parameter +#[pyclass] +#[derive(Copy, Clone, Debug)] +pub enum PyImageThresholdType { + Binary, + BinaryInv, + ThreshTrunc, + ThreshToZero +} + +impl PyImageThresholdType { + pub(crate) fn to_threshold(self) -> ThresholdMethod { + match self { + PyImageThresholdType::Binary => ThresholdMethod::Binary, + PyImageThresholdType::BinaryInv => ThresholdMethod::BinaryInv, + PyImageThresholdType::ThreshTrunc => ThresholdMethod::ThreshTrunc, + PyImageThresholdType::ThreshToZero => ThresholdMethod::ThreshToZero + } + } +} diff --git a/zune-python/src/py_image.rs b/zune-python/src/py_image.rs index 92f599f4..409cdc17 100644 --- a/zune-python/src/py_image.rs +++ b/zune-python/src/py_image.rs @@ -10,11 +10,15 @@ use std::fs::read; use pyo3::exceptions::PyException; use pyo3::prelude::*; use zune_image::filters::crop::Crop; +use zune_image::filters::threshold::Threshold; +use zune_image::filters::transpose::Transpose; use zune_image::image::Image; use zune_image::traits::OperationsTrait; use zune_png::zune_core::options::DecoderOptions; -use crate::py_enums::{PyImageColorSpace, PyImageDepth, PyImageErrors, PyImageFormats}; +use crate::py_enums::{ + PyImageColorSpace, PyImageDepth, PyImageErrors, PyImageFormats, PyImageThresholdType +}; #[pyclass] #[derive(Clone)] @@ -82,8 +86,6 @@ impl PyImage { } /// Convert from one colorspace to another /// - /// This operation modifies the image in place - /// /// # Arguments /// - to: The new colorspace to convert to /// - in_place: Whether to perform the conversion in place or to create a copy and convert that @@ -183,6 +185,99 @@ impl PyImage { Ok(Some(im_clone)) }; } + /// Transpose the image. + /// + /// This rewrites pixels into `dst(i,j)=src(j,i)` + /// + /// # Arguments + /// - inplace: Whether to transpose the image in place or generate a clone + /// and transpose the new clone + #[pyo3(signature = (in_place = false))] + pub fn transpose(&mut self, in_place: bool) -> PyResult> { + let exec = |image: &mut PyImage| -> PyResult<()> { + Transpose::new() + .execute(&mut image.image) + .map_err(|x| PyImageErrors::from(x))?; + Ok(()) + }; + return if in_place { + exec(self)?; + Ok(None) + } else { + let mut im_clone = self.clone(); + exec(&mut im_clone)?; + Ok(Some(im_clone)) + }; + } + + /// Convert from one depth to another + /// + /// # Arguments + /// - to: The new depth to convert to + /// - in_place: Whether to perform the conversion in place or to create a copy and convert that + /// + /// # Returns + /// - If `in_place=True`: Nothing on success, on error returns error that occurred + /// - If `in_place=False`: An image copy on success on error, returns error that occurred + #[pyo3(signature = (to, in_place = false))] + pub fn convert_depth(&mut self, to: PyImageDepth, in_place: bool) -> PyResult> { + let color = to.to_depth(); + + let exec = |image: &mut PyImage| -> PyResult<()> { + if let Err(e) = image.image.convert_depth(color) { + return Err(PyErr::new::(format!( + "Error converting: {:?}", + e + ))); + } + Ok(()) + }; + + if in_place { + exec(self)?; + Ok(None) + } else { + let mut im_clone = self.clone(); + exec(&mut im_clone)?; + + Ok(Some(im_clone)) + } + } + /// Applies a fixed-level threshold to each array element. + /// + /// Thresholding works best for grayscale images, passing a colored image + /// does not implicitly convert it to grayscale, you need to do that explicitly + /// + /// # Arguments + /// - value: Non-zero value assigned to the pixels for which the condition is satisfied + /// - method: The thresholding method used, defaults to binary which generates a black + /// and white image from a grayscale image + /// - in_place: Whether to perform the image in-place or to clone and return a copy + /// + #[pyo3(signature = (value, method = PyImageThresholdType::Binary, in_place = false))] + pub fn threshold( + &mut self, value: f32, method: PyImageThresholdType, in_place: bool + ) -> PyResult> { + let exec = |image: &mut PyImage| -> PyResult<()> { + if let Err(e) = Threshold::new(value, method.to_threshold()).execute(&mut image.image) { + return Err(PyErr::new::(format!( + "Error converting: {:?}", + e + ))); + } + Ok(()) + }; + + if in_place { + exec(self)?; + Ok(None) + } else { + let mut im_clone = self.clone(); + exec(&mut im_clone)?; + + Ok(Some(im_clone)) + } + } } #[pyfunction]