gpt4 book ai didi

python - 如何将 AxesImage 中的坐标映射到保存的图像文件中的坐标?

转载 作者:太空狗 更新时间:2023-10-29 22:11:56 25 4
gpt4 key购买 nike

我使用 matplotlib将数字矩阵显示为图像,沿轴附加标签,并将绘图保存为 PNG 文件。为了创建 HTML 图像映射,我需要知道 imshow 显示的图像中某个区域在 PNG 文件中的像素坐标。

我找到了 an example关于如何使用常规绘图执行此操作,但是当我尝试使用 imshow 执行相同操作时,映射不正确。这是我的代码,它保存了一张图像并尝试打印对角线上每个正方形中心的像素坐标:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
axim = ax.imshow(np.random.random((27,27)), interpolation='nearest')
for x, y in axim.get_transform().transform(zip(range(28), range(28))):
print int(x), int(fig.get_figheight() * fig.get_dpi() - y)
plt.savefig('foo.png', dpi=fig.get_dpi())

这是生成的 foo.png,显示为包含标尺的屏幕截图:

Screenshot of foo.png with rulers

脚本的输出开始和结束如下:

73 55
92 69
111 83
130 97
149 112

509 382
528 396
547 410
566 424
585 439

如您所见,y 坐标是正确的,但 x 坐标被拉伸(stretch)了:它们的范围从 73 到 585,而不是预期的 135 到 506,并且它们的间距为 19 像素 o.c。而不是预期的 14。我做错了什么?

最佳答案

这是尝试从 matplotlib 获取精确像素值时比较令人困惑的部分之一。 Matplotlib 将处理精确像素值的渲染器与绘制图形和轴的 Canvas 分开。

基本上,最初创建图形(但尚未显示)时存在的渲染器不一定与显示图形或将其保存到文件时使用的渲染器相同。

您所做的是正确的,但它使用的是初始渲染器,而不是保存图形时使用的渲染器。

为了说明这一点,这里是您的代码的一个稍微简化的版本:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(np.random.random((27,27)), interpolation='nearest')

for i in range(28):
x, y = ax.transData.transform_point([i,i])
print '%i, %i' % (x, fig.bbox.height - y)

fig.savefig('foo.png', dpi=fig.dpi)

这会产生与上面类似的结果:(差异是由于您的机器和我的机器之间的渲染后端不同)

89, 55
107, 69
125, 83
...
548, 410
566, 424
585, 439

但是,如果我们做完全相同的事情,但不是在显示坐标之前绘制图形,我们就会得到正确的答案!

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(np.random.random((27,27)), interpolation='nearest')

fig.canvas.draw()

for i in range(28):
x, y = ax.transData.transform_point([i,i])
print '%i, %i' % (x, fig.bbox.height - y)

fig.savefig('foo.png', dpi=fig.dpi)

这会产生:(请记住,图形的边缘在数据坐标中为 <-0.5, -0.5>,而不是 <0, 0>。(即绘制图像的坐标以像素为中心)这就是 <0, 0> 产生的原因143, 55 ,而不是 135, 48 )

143, 55
157, 69
171, 83
...
498, 410
512, 424
527, 439

当然,绘制图形只是为了在保存时再次绘制它是多余的并且计算量大。

为避免绘制两次,您可以将回调函数连接到绘制事件,并在该函数内输出您的 HTML 图像映射。举个简单的例子:

import numpy as np
import matplotlib.pyplot as plt

def print_pixel_coords(event):
fig = event.canvas.figure
ax = fig.axes[0] # I'm assuming there's only one subplot here...
for i in range(28):
x, y = ax.transData.transform_point([i,i])
print '%i, %i' % (x, fig.bbox.height - y)

fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(np.random.random((27,27)), interpolation='nearest')

fig.canvas.mpl_connect('draw_event', print_pixel_coords)

fig.savefig('foo.png', dpi=fig.dpi)

这会产生正确的输出,同时在保存时只绘制一次图形:

143, 55
157, 69
171, 83
...
498, 410
512, 424
527, 439

另一个优点是您可以在调用 fig.savefig 时使用任何 dpi无需手动设置 fig对象的 dpi 预先。因此,在使用回调函数的时候,直接做fig.savefig('foo.png')即可。 , (或 fig.savefig('foo.png', dpi=whatever) ),您将获得与保存的 .png 文件匹配的输出。 (保存图形时默认的 dpi 是 100,而图形对象的默认 dpi 是 80,这就是为什么你必须首先指定 dpi 与 fig.dpi 相同)

希望这至少有点清楚!

关于python - 如何将 AxesImage 中的坐标映射到保存的图像文件中的坐标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4668432/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com