In order to get the figure contents as RGB pixel values, the matplotlib.backend_bases.Renderer needs to first draw the contents of the canvas.
You can do this by manually calling canvas.draw():
from matplotlib.figure import Figure
fig = Figure()
canvas = fig.canvas
ax = fig.gca()
ax.text(0.0,0.0,"Test", fontsize=45)
ax.axis('off')
canvas.draw() # Draw the canvas, cache the renderer
image_flat = np.frombuffer(canvas.tostring_rgb(), dtype='uint8') # (H * W * 3,)
# NOTE: reversed converts (W, H) from get_width_height to (H, W)
image = image_flat.reshape(*reversed(canvas.get_width_height()), 3) # (H, W, 3)
See here for more info on the Matplotlib API.
Answer from ali_m on Stack OverflowMatplotlib to numpy array
Converting matplotlib plot to numpy array
python - How do I convert a numpy array to (and display) an image? - Stack Overflow
python - Save plot to numpy array - Stack Overflow
In order to get the figure contents as RGB pixel values, the matplotlib.backend_bases.Renderer needs to first draw the contents of the canvas.
You can do this by manually calling canvas.draw():
from matplotlib.figure import Figure
fig = Figure()
canvas = fig.canvas
ax = fig.gca()
ax.text(0.0,0.0,"Test", fontsize=45)
ax.axis('off')
canvas.draw() # Draw the canvas, cache the renderer
image_flat = np.frombuffer(canvas.tostring_rgb(), dtype='uint8') # (H * W * 3,)
# NOTE: reversed converts (W, H) from get_width_height to (H, W)
image = image_flat.reshape(*reversed(canvas.get_width_height()), 3) # (H, W, 3)
See here for more info on the Matplotlib API.
For people who are searching an answer for this question, this is the code gathered from previous answers. Keep in mind that the method np.fromstring is deprecated and np.frombuffer is used instead.
#Image from plot
ax.axis('off')
fig.tight_layout(pad=0)
# To remove the huge white borders
ax.margins(0)
fig.canvas.draw()
image_from_plot = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
image_from_plot = image_from_plot.reshape(fig.canvas.get_width_height()[::-1] + (3,))
As the title states I am trying to convert matplotlib results to numpy image arrays. When I google it I get a lot of hits however they all seem to be using functions that no longer exist. You would think there would be a simple function to convert it to an image or array.. but seems not.
Hey all,
in my code - which is actually longer that what I show here - I'm calculating a magnitude spectrogram of an audio signal. Afterwards, I'm plotting a pcolormesh plot. When I view the spectrogram with matplotlib.pyplot.show(), almost immediately the plot pops up (see below). I measured this with time.time() and the reaction is very quick.
The issue is the following: I need to take this matplotlib.pyplot.pcolormesh and convert it to numpy array image for the purpose of sending it through SpoutGL. I've tried rendering the plot onto a canvas with FigureCanvasAgg. While this does work, it takes too long (around 0.6 sec for HD dimensions - my goal is max 0.05 sec). I've also tried get_array(). This is very fast, but it completely destroys the spectrogram plot into a confusing mix of pixels (see below).
Since the plt.show() reaction is so fast and it does show the pcolormesh as... well, essentially an image, I figured there must be a way to use this and convert the plot somewhat "directly" into a numpy array to be sent through SpoutGL. But I have no idea how. The get_array() failed so far. Is there a way to make get_array() work? Or is there another way? I'd very much appreciate any help. This has been bugging me for days and days (especially since the plot.show() is so quick).
# Calculating and plotting the spectrogram
frequencies, times, magnitude_spectrogram = spectrogram(audio_signal, fs=44100, window=hann, nperseg=2000, noverlap=int(nperseg / 1.2))
spectrogram_fig = matplotlib.pyplot.figure(figsize=(12.8, 7.2), frameon=False)
matplotlib.pyplot.pcolormesh(times, frequencies, 10 * np.log10(magnitude_spectrogram), shading='gouraud', cmap="inferno", vmin=-100, vmax=-30)
matplotlib.pyplot.show()
# Here I need to convert the plot to numpy array
# Sending through SpoutGL
SpoutGL.SpoutSender().sendImage(spectrogram_plot_to_image, 1280, 720, GL.GL_RGB, False, 0)
Show() spectrogram plot: https://postimg.cc/LnX4rgHG
Get_array() result: https://postimg.cc/fV1b7WQp
Edits: formatting
Use plt.imshow to create the figure, and plt.show to display it:
from matplotlib import pyplot as plt
plt.imshow(data, interpolation='nearest')
plt.show()
For Jupyter notebooks, add this line before importing matplotlib:
%matplotlib inline
For interactive plots in Jupyter [demo], install ipyml pip install ipympl, then use:
%matplotlib widget
You could use PIL to create (and display) an image:
from PIL import Image
import numpy as np
w, h = 512, 512
data = np.zeros((h, w, 3), dtype=np.uint8)
data[0:256, 0:256] = [255, 0, 0] # red patch in upper left
img = Image.fromarray(data, 'RGB')
img.save('my.png')
img.show()
This is a handy trick for unit tests and the like, when you need to do a pixel-to-pixel comparison with a saved plot.
One way is to use fig.canvas.tostring_rgb and then numpy.fromstring with the approriate dtype. There are other ways as well, but this is the one I tend to use.
E.g.
import matplotlib.pyplot as plt
import numpy as np
# Make a random plot...
fig = plt.figure()
fig.add_subplot(111)
# If we haven't already shown or saved the plot, then we need to
# draw the figure first...
fig.canvas.draw()
# Now we can save it to a numpy array.
data = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
data = data.reshape(fig.canvas.get_width_height()[::-1] + (3,))
There is a bit simpler option for @JUN_NETWORKS's answer. Instead of saving the figure in png, one can use other format, like raw or rgba and skip the cv2 decoding step.
In other words the actual plot-to-numpy conversion boils down to:
io_buf = io.BytesIO()
fig.savefig(io_buf, format='raw', dpi=DPI)
io_buf.seek(0)
img_arr = np.reshape(np.frombuffer(io_buf.getvalue(), dtype=np.uint8),
newshape=(int(fig.bbox.bounds[3]), int(fig.bbox.bounds[2]), -1))
io_buf.close()
Hope, this helps.