如何将Matplotlib Axes对象渲染到图像(作为Numpy数组)? [英] How can I render a Matplotlib Axes object to an image (as a Numpy array)?
问题描述
有没有办法将特定 Axes 对象的内容渲染为图像,作为 Numpy 数组?我知道您可以使用整个图形来完成此操作,但是我想获取特定轴的图像.
Is there a way to render the contents of a particular Axes object to an image, as a Numpy array? I know you can do this with the entire figure, but I want to get the image of a particular Axes.
我要渲染的轴包含一个图像(使用imshow绘制),顶部绘制了一些线.理想情况下,渲染的 ndarray 将只包含这些元素,而没有刻度线、边框等.
The Axes I'm trying to render contains an image (drawn with imshow), with some lines plotted on top. Ideally, the rendered ndarray would contain just these elements, and no tick marks, borders, etc.
我忘了提到我在寻找一种不需要保存图像文件的解决方案.
I forgot to mention that I'm looking for a solution that doesn't require saving an image file.
我编写了以下示例,几乎可以实现这一点,只是它不保留图像分辨率.关于我可能做错了什么的任何提示将不胜感激:
I've written the following example that almost achieves this, except it doesn't preserve image resolution. Any hints as to what I may be doing wrong would be greatly appreciated:
import matplotlib.pylab as plt
import numpy as np
def main():
"""Test for extracting pixel data from an Axes.
This creates an image I, imshow()'s it to one Axes, then copies the pixel
data out of that Axes to a numpy array I_copy, and imshow()'s the I_copy to
another Axes.
Problem: The two Axes *look* identical, but I does not equal I_copy.
"""
fig, axes_pair = plt.subplots(1, 2)
reds, greens = np.meshgrid(np.arange(0, 255), np.arange(0, 122))
blues = np.zeros_like(reds)
image = np.concatenate([x[..., np.newaxis] for x in (reds, greens, blues)],
axis=2)
image = np.asarray(image, dtype=np.uint8)
axes_pair[0].imshow(image)
fig.canvas.draw()
trans = axes_pair[0].figure.dpi_scale_trans.inverted()
bbox = axes_pair[0].bbox.transformed(trans)
bbox_contents = fig.canvas.copy_from_bbox(axes_pair[0].bbox)
left, bottom, right, top = bbox_contents.get_extents()
image_copy = np.fromstring(bbox_contents.to_string(),
dtype=np.uint8,
sep="")
image_copy = image_copy.reshape([top - bottom, right - left, 4])
image_copy = image_copy[..., :3] # chop off alpha channel
axes_pair[1].imshow(image_copy)
print("Are the images perfectly equal? {}".format(np.all(image == image_copy)))
plt.show()
if __name__ == '__main__':
main()
推荐答案
一个想法是中间关闭轴,找出轴的边界框(以英寸为单位),然后使用 bbox_inches
plt.savefig()
的参数.
One idea can be to intermediately turn the axes off, find out the bounding box of the axes in inches and then save the figure using the bbox_inches
argument to plt.savefig()
.
如果需要 numpy 数组,则可以使用 plt.imread
再次读取保存的图像.
If a numpy array is wanted, one can then read in the saved image again using plt.imread
.
在此解决方案中,返回的数组的尺寸与轴在屏幕上绘制时的像素完全相同.
In this solution the returned array has dimensions exactly as the axes has pixels when plotted on the screen.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
im = np.random.rand(16,16)
x = np.arange(9)
y = np.random.randint(1,14, size=(9,))
y2 = np.random.randint(1,7, size=(9,))
fig, (ax1, ax2) = plt.subplots(1,2)
ax1.imshow(im[:16,:9], cmap="viridis")
ax2.imshow(im[7:16,7:], cmap="YlGnBu")
ax1.plot(x, y, color="C3")
ax1.scatter(x, y, color="w")
ax2.plot(x, y2, color="C1")
ax2.scatter(x, y2, color="k")
ax1.set_xlabel("YlFasOcto")
def save_ax(ax, filename, **kwargs):
ax.axis("off")
ax.figure.canvas.draw()
trans = ax.figure.dpi_scale_trans.inverted()
bbox = ax.bbox.transformed(trans)
plt.savefig(filename, dpi="figure", bbox_inches=bbox, **kwargs)
ax.axis("on")
im = plt.imread(filename)
return im
arr = save_ax(ax1, __file__+".png")
print(arr.shape)
plt.show()
为了防止将文件保存到磁盘,可以使用 Stream 来保存数据.
In order to prevent saving a file to disk, one could use a Stream to save the data.
import io
def save_ax_nosave(ax, **kwargs):
ax.axis("off")
ax.figure.canvas.draw()
trans = ax.figure.dpi_scale_trans.inverted()
bbox = ax.bbox.transformed(trans)
buff = io.BytesIO()
plt.savefig(buff, format="png", dpi=ax.figure.dpi, bbox_inches=bbox, **kwargs)
ax.axis("on")
buff.seek(0)
im = plt.imread(buff )
return im
这篇关于如何将Matplotlib Axes对象渲染到图像(作为Numpy数组)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!