如何将Matplotlib轴对象渲染到图像(作为Numpy数组)?
是否有一种方法可以将特定轴对象的内容渲染为图像(如Numpy数组)?我知道你可以对整个图形这样做,但我想得到一个特定轴的图像 我试图渲染的轴包含一个图像(使用imshow绘制),顶部绘制了一些线。理想情况下,渲染的ndarray将只包含这些元素,而不包含记号、边框等 更新: 我忘了提到我正在寻找一个不需要保存图像文件的解决方案 我已经写了下面的例子,几乎实现了这一点,除了它不保留图像分辨率。任何关于我可能做错了什么的提示都将不胜感激:如何将Matplotlib轴对象渲染到图像(作为Numpy数组)?,matplotlib,Matplotlib,是否有一种方法可以将特定轴对象的内容渲染为图像(如Numpy数组)?我知道你可以对整个图形这样做,但我想得到一个特定轴的图像 我试图渲染的轴包含一个图像(使用imshow绘制),顶部绘制了一些线。理想情况下,渲染的ndarray将只包含这些元素,而不包含记号、边框等 更新: 我忘了提到我正在寻找一个不需要保存图像文件的解决方案 我已经写了下面的例子,几乎实现了这一点,除了它不保留图像分辨率。任何关于我可能做错了什么的提示都将不胜感激: import matplotlib.pylab as pl
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\u inches
参数将图形保存到plt.savefig()
如果需要numpy阵列,则可以使用plt.imread
再次读取保存的图像
在该解决方案中,返回的数组的尺寸与在屏幕上打印时轴的像素完全相同
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()
为了防止将文件保存到磁盘,可以使用流来保存数据
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版本可能不同)。我已经用我自己的一些示例代码更新了我的问题,它借用了您使用dpi_scale_trans.inversed()的想法,但不需要保存到文件(我忘了提到这是一个必要的属性)。它主要做我想要的,除了保留原来显示的图像的分辨率。这是一个问题。“我无法运行您的代码”不是一个充分的问题描述。起初,我确实尝试了与问题中类似的解决方案,使用
to_string()
方法,但图像与原始图像相比发生了移动。因此,我在这个答案中提出了解决办法。我更新了答案,以包括不需要输出文件的情况。