Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Bug]: imsave fails on RGBA data when origin is set to lower #28020

Closed
@jonas87

Description

@jonas87

Bug summary

Under certain conditions pyplot's imsave() function will fail, with the underlying PIL library throwing an "array is not C-contiguous" error (while the array provided to imsave is C-contiguous).

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt

result = np.zeros((100, 100, 4), dtype='uint8')

print(result.flags) # the ndarray is actually C-contiguous

plt.imsave(fname="test_upper.png", arr=result, format="png", origin="upper")# no problem
plt.imsave(fname="test_lower.png", arr=result, format="png", origin="lower")# error

Actual outcome

File [/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/matplotlib/pyplot.py:2200](http://localhost:8888/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/matplotlib/pyplot.py#line=2199), in imsave(fname, arr, **kwargs)
   2198 @_copy_docstring_and_deprecators(matplotlib.image.imsave)
   2199 def imsave(fname, arr, **kwargs):
-> 2200     return matplotlib.image.imsave(fname, arr, **kwargs)

File [/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/matplotlib/image.py:1659](http://localhost:8888/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/matplotlib/image.py#line=1658), in imsave(fname, arr, vmin, vmax, cmap, format, origin, dpi, metadata, pil_kwargs)
   1657     pil_kwargs = pil_kwargs.copy()
   1658 pil_shape = (rgba.shape[1], rgba.shape[0])
-> 1659 image = PIL.Image.frombuffer(
   1660     "RGBA", pil_shape, rgba, "raw", "RGBA", 0, 1)
   1661 if format == "png":
   1662     # Only use the metadata kwarg if pnginfo is not set, because the
   1663     # semantics of duplicate keys in pnginfo is unclear.
   1664     if "pnginfo" in pil_kwargs:

File [/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/PIL/Image.py:3020](http://localhost:8888/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/PIL/Image.py#line=3019), in frombuffer(mode, size, data, decoder_name, *args)
   3018 if args[0] in _MAPMODES:
   3019     im = new(mode, (1, 1))
-> 3020     im = im._new(core.map_buffer(data, size, decoder_name, 0, args))
   3021     if mode == "P":
   3022         from . import ImagePalette

ValueError: ndarray is not C-contiguous

Expected outcome

saved image

Additional information

  • The input must be an input of size MxNx4. RGBA fails but RGB works.
  • The dtype must be uint8.
  • origin must be set to "lower"
  • Image file type can be set to png, jpg, gif, or tiff, and all trigger the same issue. It's likely not a codec-specific problem.
  • The data in the image does not matter.

Suggestion from stackoverflow user Nick ODell:
[https://github.com/matplotlib/matplotlib/blob/v3.8.3/lib/matplotlib/image.py#L1605](link to code)

If origin == "lower", then the array is reversed in a zero-copy fashion. If this happens, then arr is no longer C contiguous. It then uses ScalarMappable to convert to rgba. However, if the input is already in rgba, it does not copy it. Because of this, using RGB masks the bug, because the copy would be C contiguous.

It then calls PIL.Image.frombuffer, which appears to assume that its input is C contiguous. (Pillow doesn't appear to document this assumption, so this may actually be a Pillow bug?)

Operating system

No response

Matplotlib Version

3.8.3

Matplotlib Backend

No response

Python version

No response

Jupyter version

No response

Installation

None

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions