【问题标题】:Can matplotlib contours match pixel edges?matplotlib 轮廓可以匹配像素边缘吗?
【发布时间】:2017-04-15 00:04:03
【问题描述】:

如何在matplotlib 中勾勒出像素边界?例如,对于如下所示的半随机数据集,

# the code block that follows is irrelevant
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')

如何使轮廓与像素对齐?我正在 numpymatplotlib 库中寻找解决方案。

【问题讨论】:

  • 我想知道你是怎么想出这些种子来画这幅画的?
  • @Andyk 没什么特别的 - 只是重复随机抽样,直到找到所需的种子。

标签: python numpy matplotlib


【解决方案1】:

contour_rect_slow 在值为 0 和 1 的像素之间的边界处绘制单线。contour_rect 是一个更紧凑的版本,将较长的线连接到一条线。

代码:

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[::1]

#    image[1, 1] = 1

import matplotlib.pyplot as plt
plt.imshow(image, interpolation="none", cmap="Blues")

def contour_rect_slow(im):
    """Clear version"""

    pad = np.pad(im, [(1, 1), (1, 1)])  # zero padding

    im0 = np.abs(np.diff(pad, n=1, axis=0))[:, 1:]
    im1 = np.abs(np.diff(pad, n=1, axis=1))[1:, :]

    lines = []

    for ii, jj in np.ndindex(im0.shape):
        if im0[ii, jj] == 1:
            lines += [([ii-.5, ii-.5], [jj-.5, jj+.5])]
        if im1[ii, jj] == 1:
            lines += [([ii-.5, ii+.5], [jj-.5, jj-.5])]

    return lines



def contour_rect(im):
    """Fast version"""

    lines = []
    pad = np.pad(im, [(1, 1), (1, 1)])  # zero padding

    im0 = np.abs(np.diff(pad, n=1, axis=0))[:, 1:]
    im1 = np.abs(np.diff(pad, n=1, axis=1))[1:, :]

    im0 = np.diff(im0, n=1, axis=1)
    starts = np.argwhere(im0 == 1)
    ends = np.argwhere(im0 == -1)
    lines += [([s[0]-.5, s[0]-.5], [s[1]+.5, e[1]+.5]) for s, e
              in zip(starts, ends)]

    im1 = np.diff(im1, n=1, axis=0).T
    starts = np.argwhere(im1 == 1)
    ends = np.argwhere(im1 == -1)
    lines += [([s[1]+.5, e[1]+.5], [s[0]-.5, s[0]-.5]) for s, e
              in zip(starts, ends)]

    return lines

lines = contour_rect(image)
for line in lines:
    plt.plot(line[1], line[0], color='r', alpha=1)

警告:这明显慢然后mpl.contour对于大图像..

【讨论】:

  • 效果很好,正是我的想法。你可以更新 np.pad() 行吗?在 numpy mode 是一个必需的参数,还有很多人仍在运行旧版本。
【解决方案2】:

如果图像的分辨率为每单位 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在同一图中绘制图像本身。

【讨论】:

  • 很好,我没想到在扩展网格上制作轮廓。为什么imshowcontour 之间存在不匹配?分辨率增加10 而不是100 可以更好地可视化。
  • 关于像素边缘定义 - 我想说没有什么可以阻止我们将像素边缘定义为 xy 等于 0.5, 1.0, 1.5, ... 网格线。然后可以使用基本的Line2d 绘制这些线条。
猜你喜欢
  • 2013-04-02
  • 1970-01-01
  • 2011-12-13
  • 2017-07-21
  • 1970-01-01
  • 2020-05-23
  • 1970-01-01
  • 1970-01-01
  • 2013-12-29
相关资源
最近更新 更多