Matplotlib 画布作为 numpy 数组伪影
Posted
技术标签:
【中文标题】Matplotlib 画布作为 numpy 数组伪影【英文标题】:Matplotlib canvas as numpy array artefacts 【发布时间】:2018-05-20 17:11:18 【问题描述】:我想将 matplotlib 图形转换为 numpy 数组。我已经能够通过直接访问渲染器的内容来做到这一点。但是,当我在 numpy 数组上调用 imshow 时,它看起来像沿边缘的锯齿伪影,而这些伪影在原始图中不存在。
我尝试过使用各种参数,但不知道如何修复 imshow 中的伪影。如果我将数字保存到图像文件中,图像中的差异仍然存在。
注意,我要实现的是一种确认数组内容与我之前查看的图相同的方法。我认为这些伪影可能不存在于 numpy 数组中,而是在 imshow 调用期间创建的。或许适当的 imshow 配置可以解决这个问题。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
import math
fig = plt.figure(frameon=False)
ax = plt.gca()
ax.add_patch(Rectangle((0,0), 1, 1, angle=45, color="red"))
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
ax.set_aspect(1)
plt.axis("off")
fig.canvas.draw()
plt.savefig("rec1.png")
plt.show()
X = np.array(fig.canvas.renderer._renderer)
fig = plt.figure(frameon=False)
ax = plt.gca()
plt.axis("off")
plt.imshow(X)
plt.savefig("rec2.png")
plt.show()
【问题讨论】:
plt.imshow(X, interpolation='bicubic')
移除别名但添加阴影
这可能是您使用的 python 版本或系统设置的问题吗?如果我只是复制粘贴您的示例代码,原始矩形和 imshow 矩形在默认分辨率下看起来相同(没有任何伪影),并且只有在我放大到极远时才会变得模糊(矩形的角占据了完整的全高清屏幕)。我在(都相当老)fedora 27 和2.7.13 |Anaconda custom (64-bit)| (default, Dec 20 2016, 23:09:15) [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]
我原以为最大的差异是由于使用了后端,而不是 matplotlib 版本。其他@Paul:您是否尝试过使用不同的后端?我会考虑/调查的其他因素是 DPI 和图形大小,可能是屏幕分辨率。
@JC_CL 您可能使用的是旧的 matplotlib 版本。在版本 2 之前,他们默认为 imshow
使用 interpolation="bilinear"
(或者 Fedora 维护者保留默认值以实现逆向兼容性,或者您在 matplotlibrc
上拥有它)。当前默认 (interpolation="nearest"
) 显示问题
@bobrobbob 看起来像投影的东西实际上可能是 alpha 合成的一些错误或 alpha 插值出现的问题,如果你这样做 imshow(X[..., :3], interpolation="bilinear")
它看起来不错
【参考方案1】:
这些明显是重采样伪影,可以通过使用plt.figimage
来避免,该plt.figimage
专门在图中添加了未经重采样的图像。
plt.figimage(X)
plt.show()
请注意,这不适用于 Jupyter Notebook 中的 %matplotlib inline
,但它适用于 %matplotlib notebook
和 GUI 后端。
【讨论】:
【参考方案2】:通过添加填充为 -1.08 的 fig.tight_layout,我能够获得与真实图像完全相同的图像。
X = np.array(fig.canvas.renderer._renderer)
fig = plt.figure(frameon=False)
ax = plt.gca()
plt.axis("off")
plt.imshow(X)
fig.tight_layout(pad=-1.08)
plt.savefig("rec2.png")
plt.show()
Real Image
From numpy array
我希望这可以解决您的问题,至少在您找到更好的方法之前。干杯。
【讨论】:
【参考方案3】:我能想到的最好的方法是使用 cv2 (openCV-python) 库。我的解决方案确实需要保存图像,在彩色图像的情况下,解码后的图像将以 B G R 顺序存储通道。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
import math
import cv2 #import openCV
fig = plt.figure(frameon=False)
ax = plt.gca()
ax.add_patch(Rectangle((0,0), 1, 1, angle=45, color="red"))
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
ax.set_aspect(1)
plt.axis("off")
fig.canvas.draw()
plt.savefig("rec1.png")
plt.show()`
im = cv2.imread("rec1.png")
print(type(im)) #prints numpy.ndarray
cv2.imshow("pic",im) #creates a window named pic, loads im
cv2.waitKey(0) #has no time limit, window destroyed on any key press
cv2.destroyAllWindows()
最终结果看起来像
由于它是一个numpy数组,你可以调用它的方法进行比较。
print(im.shape) #prints (288, 432, 3)
【讨论】:
【参考方案4】:第二个图中显示的图像比第一个图像小;原因是完整的第一个图形的图像被压缩到一个新创建的更小的轴上——这在不关闭轴时会很明显。
为了确保第二个图形只显示图像本身,您可以使用subplots_adjust
调整边距,使图形边缘和轴之间没有间距。
fig = plt.figure(frameon=False)
fig.subplots_adjust(0,0,1,1)
ax = plt.gca()
plt.axis("off")
plt.imshow(X)
这会产生所需的情节。
但请注意,由于在保存 png 文件时应用了抗锯齿,因此数组并不完全相同。您可以通过
了解X = np.array(fig.canvas.renderer._renderer)/255.
Y = plt.imread("rec1.png")
print(np.all(X==Y))
## This prints False
反过来说,如果你想拥有与保存的相同的numpy数组,你应该确保使用保存的图像本身。
plt.savefig("rec1.png")
X = plt.imread("rec1.png")
# use X from here onwards
【讨论】:
【参考方案5】:感谢指出插值是原因的 cmets。我找到了以下代码(适用于 Python 3),它以我想要的方式显示图像;与第一张图片相同,但通过 numpy 数组。
import PIL.Image
from io import BytesIO
import IPython.display
import numpy as np
def showarray(a, fmt='png'):
a = np.uint8(a)
f = BytesIO()
PIL.Image.fromarray(a).save(f, fmt)
IPython.display.display(IPython.display.Image(data=f.getvalue()))
来源:https://gist.github.com/kylemcdonald/2f1b9a255993bf9b2629
【讨论】:
如果没有更好的答案很快就会接受这个答案(例如使用 matplotlib)以上是关于Matplotlib 画布作为 numpy 数组伪影的主要内容,如果未能解决你的问题,请参考以下文章
11-2 numpy/pandas/matplotlib模块