-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Closed
Milestone
Description
Bug summary
Getting an error when saving an animated RGB image that was loaded from a pickled figure. I've isolated the error to matplotlib 3.9.0, with this code working in 3.8.3, which makes me think that this is to do with the pybind11 upgrade in #26275?
Things I've tried:
- Grayscale images (eg
data = np.random.rand(100, 100)
) work. - Numpy v1.26.4 and v2.0.0 show no difference in behavior
- This shows up at least on WSL and Ubuntu
- In the debugger, both
data.dtype
andout.dtype
are showing'float64'
prior to the_image.resample
call.- However, if I re-cast the arrays with
data = data.astype('float64')
,out = ...
, then the_image.resample
call no longer fails! - If I re-cast only one, then
out.dtype == data.dtype
returnsTrue
, but on the function call I get the errorValueError: Input and output arrays have mismatched types
- ... so something is up with the types, and the C++ code is bombing. But python is saying things line up.
- However, if I re-cast the arrays with
See these parts of the source:
matplotlib/lib/matplotlib/image.py
Lines 205 to 213 in d7d1bba
out = np.zeros(out_shape + data.shape[2:], data.dtype) # 2D->2D, 3D->3D. | |
if resample is None: | |
resample = image_obj.get_resample() | |
_image.resample(data, out, transform, | |
_interpd_[interpolation], | |
resample, | |
alpha, | |
image_obj.get_filternorm(), | |
image_obj.get_filterrad()) |
matplotlib/src/_image_wrapper.cpp
Lines 174 to 199 in d7d1bba
if (auto resampler = | |
(ndim == 2) ? ( | |
(dtype.is(py::dtype::of<std::uint8_t>())) ? resample<agg::gray8> : | |
(dtype.is(py::dtype::of<std::int8_t>())) ? resample<agg::gray8> : | |
(dtype.is(py::dtype::of<std::uint16_t>())) ? resample<agg::gray16> : | |
(dtype.is(py::dtype::of<std::int16_t>())) ? resample<agg::gray16> : | |
(dtype.is(py::dtype::of<float>())) ? resample<agg::gray32> : | |
(dtype.is(py::dtype::of<double>())) ? resample<agg::gray64> : | |
nullptr) : ( | |
// ndim == 3 | |
(dtype.is(py::dtype::of<std::uint8_t>())) ? resample<agg::rgba8> : | |
(dtype.is(py::dtype::of<std::int8_t>())) ? resample<agg::rgba8> : | |
(dtype.is(py::dtype::of<std::uint16_t>())) ? resample<agg::rgba16> : | |
(dtype.is(py::dtype::of<std::int16_t>())) ? resample<agg::rgba16> : | |
(dtype.is(py::dtype::of<float>())) ? resample<agg::rgba32> : | |
(dtype.is(py::dtype::of<double>())) ? resample<agg::rgba64> : | |
nullptr)) { | |
Py_BEGIN_ALLOW_THREADS | |
resampler( | |
input_array.data(), input_array.shape(1), input_array.shape(0), | |
output_array.mutable_data(), output_array.shape(1), output_array.shape(0), | |
params); | |
Py_END_ALLOW_THREADS | |
} else { | |
throw std::invalid_argument("arrays must be of dtype byte, short, float32 or float64"); | |
} |
Code for reproduction
import io
import pickle
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
from matplotlib.animation import FuncAnimation
dir = Path(__file__).parent.resolve()
# generate random rgb data
fig, ax = plt.subplots()
np.random.seed(0)
data = np.random.rand(100, 100, 3)
ax.imshow(data)
# pick the figure and reload
buf = io.BytesIO()
pickle.dump(fig, buf)
buf.seek(0)
fig_pickled = pickle.load(buf)
# Animate
def update(frame):
return ax,
ani = FuncAnimation(fig_pickled, update, frames=2)
# Save the animation
filepath = dir / 'test.gif'
ani.save(filepath)
Actual outcome
Exception has occurred: ValueError
arrays must be of dtype byte, short, float32 or float64
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/image.py", line 208, in _resample
_image.resample(data, out, transform,
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/image.py", line 567, in _make_image
output = _resample( # resample rgb channels
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/image.py", line 952, in make_image
return self._make_image(self._A, bbox, transformed_bbox, clip,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/image.py", line 653, in draw
im, l, b, trans = self.make_image(
^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/artist.py", line 72, in draw_wrapper
return draw(artist, renderer)
^^^^^^^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/image.py", line 132, in _draw_list_compositing_images
a.draw(renderer)
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/axes/_base.py", line 3110, in draw
mimage._draw_list_compositing_images(
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/artist.py", line 72, in draw_wrapper
return draw(artist, renderer)
^^^^^^^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/image.py", line 132, in _draw_list_compositing_images
a.draw(renderer)
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/figure.py", line 3157, in draw
mimage._draw_list_compositing_images(
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/artist.py", line 72, in draw_wrapper
return draw(artist, renderer)
^^^^^^^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/artist.py", line 95, in draw_wrapper
result = draw(artist, renderer, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/backends/backend_agg.py", line 387, in draw
self.figure.draw(self.renderer)
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/backends/backend_agg.py", line 432, in print_raw
FigureCanvasAgg.draw(self)
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/backend_bases.py", line 2054, in <lambda>
print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(
^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/backend_bases.py", line 2204, in print_figure
result = print_method(
^^^^^^^^^^^^^
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/backends/backend_qtagg.py", line 75, in print_figure
super().print_figure(*args, **kwargs)
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/figure.py", line 3390, in savefig
self.canvas.print_figure(fname, **kwargs)
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/animation.py", line 371, in grab_frame
self.fig.savefig(self._proc.stdin, format=self.frame_format,
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/lib/matplotlib/animation.py", line 1109, in save
writer.grab_frame(**savefig_kwargs)
File "/mnt/c/Users/Scott/Documents/Documents/Coding/matplotlib/_test_pybind11_error.py", line 35, in <module>
ani.save(filepath)
ValueError: arrays must be of dtype byte, short, float32 or float64
Matplotlib Version
3.9.0
cgebbe