matplotlib轮廓可以匹配像素边缘吗?

时间:2016-11-30 15:31:17

标签: python numpy matplotlib

如何勾画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')

binary_invader

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

2 个答案:

答案 0 :(得分:7)

如果图像的每单位分辨率为1像素,您将如何定义" edge"一个像素? " edge"的概念仅在与像素本身相比增加分辨率的帧中才有意义,contour如果使用与图像本身相同的解析,则无法绘制任何边。

另一方面,当然可以增加分辨率,使得概念" edge"有意义。因此,我们假设我们将分辨率提高了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()

enter image description here

为了进行比较,我们还可以使用imshow在同一个图中绘制图像。

答案 1 :(得分:1)

enter image description here

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)

警告:对于大图像,此速度慢得多