diff --git a/src/python_bindings.rs b/src/python_bindings.rs index 07caedb..f406d15 100644 --- a/src/python_bindings.rs +++ b/src/python_bindings.rs @@ -1204,7 +1204,7 @@ pub fn resize_lanczos4_opencv<'py>( #[pyfunction] pub fn imdecode_py<'py>( py: Python<'py>, - buf: &[u8], + buf: pyo3::pybacked::PyBackedBytes, flags: i32, ) -> PyResult>> { use crate::cv_compat::{imdecode, ImreadFlags}; @@ -1220,7 +1220,6 @@ pub fn imdecode_py<'py>( } }; - let buf = buf.to_vec(); let decoded = py.allow_threads(move || imdecode(&buf, imread_flags)); match decoded { diff --git a/tests/test_python_bindings.py b/tests/test_python_bindings.py index d00637f..53c4684 100644 --- a/tests/test_python_bindings.py +++ b/tests/test_python_bindings.py @@ -1,9 +1,11 @@ """Tests for Python bindings of training_sample_rust.""" import importlib.util +import io import numpy as np import pytest +from PIL import Image try: import trainingsample as tsr @@ -34,6 +36,17 @@ def sample_video(): class TestImageOperations: """Test image processing operations.""" + @pytest.mark.parametrize("buffer_type", [bytes, bytearray]) + def test_imdecode_owned_buffer(self, buffer_type): + """Decode buffers that remain valid while the GIL is released.""" + image = np.arange(4 * 5 * 3, dtype=np.uint8).reshape((4, 5, 3)) + encoded = io.BytesIO() + Image.fromarray(image).save(encoded, format="PNG") + + decoded = tsr.imdecode_py(buffer_type(encoded.getvalue()), 1) + + np.testing.assert_array_equal(decoded, image) + def test_batch_crop_images(self, sample_images): """Test batch cropping with custom coordinates.""" crop_boxes = [(10, 10, 50, 50), (20, 20, 40, 40), (5, 5, 60, 60)]