From 9a5724130c447030ef7d91368339496147a5707c Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Thu, 17 Apr 2025 13:42:35 -0500 Subject: [PATCH 1/2] Backport PR #29931: Allow Python native sequences in Matplotlib `imsave()`. --- lib/matplotlib/image.py | 7 ++++++- lib/matplotlib/tests/test_image.py | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 1cbd3c14c135..8191c5db0b2e 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1536,7 +1536,8 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, extension of *fname*, if any, and from :rc:`savefig.format` otherwise. If *format* is set, it determines the output format. arr : array-like - The image data. The shape can be one of + The image data. Accepts NumPy arrays or sequences + (e.g., lists or tuples). The shape can be one of MxN (luminance), MxNx3 (RGB) or MxNx4 (RGBA). vmin, vmax : float, optional *vmin* and *vmax* set the color scaling for the image by fixing the @@ -1567,6 +1568,10 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, default 'Software' key. """ from matplotlib.figure import Figure + + # Normalizing input (e.g., list or tuples) to NumPy array if needed + arr = np.asanyarray(arr) + if isinstance(fname, os.PathLike): fname = os.fspath(fname) if format is None: diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index bc98bdb3900c..ed94f154bbd6 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -184,6 +184,27 @@ def test_imsave(fmt): assert_array_equal(arr_dpi1, arr_dpi100) +def test_imsave_python_sequences(): + # Tests saving an image with data passed using Python sequence types + # such as lists or tuples. + + # RGB image: 3 rows × 2 columns, with float values in [0.0, 1.0] + img_data = [ + [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], + [(0.0, 0.0, 1.0), (1.0, 1.0, 0.0)], + [(0.0, 1.0, 1.0), (1.0, 0.0, 1.0)], + ] + + buff = io.BytesIO() + plt.imsave(buff, img_data, format="png") + buff.seek(0) + read_img = plt.imread(buff) + + assert_array_equal( + np.array(img_data), + read_img[:, :, :3] # Drop alpha if present + ) + @pytest.mark.parametrize("origin", ["upper", "lower"]) def test_imsave_rgba_origin(origin): # test that imsave always passes c-contiguous arrays down to pillow From 4cd3b06a78a4db8ef0453844e470ae2ef495381b Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 18 Apr 2025 10:46:56 -0400 Subject: [PATCH 2/2] STY: placate lint --- lib/matplotlib/tests/test_image.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index ed94f154bbd6..40dc6b812186 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -205,6 +205,7 @@ def test_imsave_python_sequences(): read_img[:, :, :3] # Drop alpha if present ) + @pytest.mark.parametrize("origin", ["upper", "lower"]) def test_imsave_rgba_origin(origin): # test that imsave always passes c-contiguous arrays down to pillow