可以使用matplotlib的等高线绘制来匹配像素边缘吗?
可以使用matplotlib的等高线绘制来匹配像素边缘吗?
如何在matplotlib
中勾画像素边界?例如,对于像下面这样的半随机数据集,
# 下面的代码块与问题无关 import numpy as np k = [] for s in [2103, 1936, 2247, 2987]: np.random.seed(s) k.append(np.random.randint(0, 2, size=(2,6))) arr = np.hstack([np.vstack(k)[:, :-1], np.vstack(k).T[::-1].T ]) image = np.zeros(shape=(arr.shape[0]+2, arr.shape[1]+2)) image[1:-1, 1:-1] = arr
很明显,相较于轮廓函数的默认行为,即在边缘像素的对角线上绘制轮廓线,更好的选择是绘制与image
的像素边界匹配的轮廓。
import matplotlib.pyplot as plt plt.contour(image[::-1], [0.5], colors='r')
如何使轮廓与像素对齐?我在numpy
和matplotlib
库中寻找解决方案。
Can matplotlib contours match pixel edges?
问题的出现原因:
如果图像的分辨率是每个单位1像素,那么如何定义像素的“边缘”?如果边缘的概念只在与像素本身相同分辨率的框架中有意义,那么如果contour
与图像本身具有相同的分辨率,则不能绘制任何边缘。
解决方法:
另一方面,当然可以增加分辨率,使得“边缘”的概念具有意义。因此,假设将分辨率增加100倍,我们可以使用contour
绘制出边缘。
示例代码:
import matplotlib.pyplot as plt import numpy as np k = [] for s in [2103, 1936, 2247, 2987]: np.random.seed(s) k.append(np.random.randint(0, 2, size=(2,6))) arr = np.hstack([np.vstack(k)[:, :-1], np.vstack(k).T[::-1].T ]) image = np.zeros(shape=(arr.shape[0]+2, arr.shape[1]+2)) image[1:-1, 1:-1] = arr f = lambda x,y: image[int(y),int(x) ] g = np.vectorize(f) x = np.linspace(0,image.shape[1], image.shape[1]*100) y = np.linspace(0,image.shape[0], image.shape[0]*100) X, Y= np.meshgrid(x[:-1],y[:-1]) Z = g(X[:-1],Y[:-1]) plt.imshow(image[::-1], origin="lower", interpolation="none", cmap="Blues") plt.contour(Z[::-1], [0.5], colors='r', linewidths=[3], extent=[0-0.5, x[:-1].max()-0.5,0-0.5, y[:-1].max()-0.5]) plt.show()
通过比较,我们还可以在同一图中使用imshow
绘制图像本身。
为什么imshow
和contour
之间存在不匹配?尽管通过将分辨率增加10倍而不是100倍可以更好地可视化。
关于像素边缘定义-我想说,没有什么能阻止我们将像素边缘定义为x
和y
等于0.5, 1.0, 1.5, ...
的网格线。然后可以使用基本的Line2d
绘制这些线条。
Matplotlib的contour函数可以绘制出与像素边缘匹配的等高线。但是,有一些用户发现使用contour函数绘制的等高线与像素边缘不匹配。为了解决这个问题,用户提供了两种不同的解决方法。
首先,用户提供了一个名为contour_rect_slow的函数,该函数可以绘制出连接像素值为0和1的像素边缘的单条线段。该函数的实现过程如下:
1. 对输入的图像进行零填充。
2. 计算图像在垂直方向上相邻像素值的差异,并筛选出差异为1的像素点,将其作为线段的起点。
3. 计算图像在水平方向上相邻像素值的差异,并筛选出差异为1的像素点,将其作为线段的终点。
其次,用户还提供了一个名为contour_rect的更快的版本,该版本也能绘制出连接像素值为0和1的像素边缘的线段。该函数的实现过程如下:
1. 对输入的图像进行零填充。
2. 计算图像在垂直方向上相邻像素值的差异,并筛选出差异为1的像素点,将其作为线段的起点。
3. 计算图像在水平方向上相邻像素值的差异,并筛选出差异为1的像素点,将其作为线段的终点。
这两种方法都可以解决contour函数绘制的等高线与像素边缘不匹配的问题。用户可以根据自己的需求选择使用其中的一种方法。
需要注意的是,用户在使用上述方法时需要注意性能问题。对于大型图像来说,上述方法的执行速度明显较慢,比起使用mpl.contour函数要慢得多。
此外,用户还提出了一个建议,即在使用numpy版本低于1.17的情况下,需要更新np.pad()函数的调用方式,因为在旧版本的numpy中,np.pad()函数需要传入一个必需的参数mode。用户认为这个建议在仍在使用旧版本numpy的用户中可能会有用。