查找蒙版的周长坐标


[[  0,   0,   0,   0, 255,   0,   0,   0,   0],
[  0,   0, 255, 255, 255, 255, 255,   0,   0],
[  0, 255, 255, 255, 255, 255, 255, 255,   0],
[  0, 255, 255, 255, 255, 255, 255, 255,   0],
[255, 255, 255, 255, 255, 255, 255, 255, 255],
[  0, 255, 255, 255, 255, 255, 255, 255,   0],
[  0, 255, 255, 255, 255, 255, 255, 255,   0],
[  0,   0, 255, 255, 255, 255, 255,   0,   0],
[  0,   0,   0,   0, 255,   0,   0,   0,   0]]

我有一个像上面这样的掩码数组。我想获取属于掩码周长的 x 和 y 坐标。周长点是以下数组中显示的点:

[[  0,   0,   0,   0, 255,   0,   0,   0,   0],
[  0,   0, 255, 255,   0, 255, 255,   0,   0],
[  0, 255,   0,   0,   0,   0,   0, 255,   0],
[  0, 255,   0,   0,   0,   0,   0, 255,   0],
[255,   0,   0,   0,   0,   0,   0,   0, 255],
[  0, 255,   0,   0,   0,   0,   0, 255,   0],
[  0, 255,   0,   0,   0,   0,   0, 255,   0],
[  0,   0, 255, 255,   0, 255, 255,   0,   0],
[  0,   0,   0,   0, 255,   0,   0,   0,   0]]

在上面的数组中,我只能使用 numpy.nonzero(),但我无法将此逻辑应用于原始数组,因为它返回一个数组元组,每个数组包含所有非零元素的所有 x 或 y 值,而无需按行分区。

我写了下面的代码,它可以工作,但似乎效率低下:

height = mask.shape[0]
width = mask.shape[1]
y_coords = []
x_coords = []
for y in range(1,height-1,1):
for x in range(0,width-1,1):
val = mask[y,x]
prev_val = mask[y,(x-1)]
next_val = mask[y, (x+1)]
top_val = mask[y-1, x]
bot_val = mask[y+1, x]
if (val != 0 and prev_val == 0) or (val != 0 and next_val == 0) or (val != 0 and top_val == 0) or (val != 0 and bot_val == 0):
y_coords.append(y)
x_coords.append(x)

我是python的新手,想学习更好的方法来做到这一点。也许使用Numpy?

我对您的问题进行了一些研究并找到了一个解决方案,我意识到您可以使用卷积来计算每个单元格的相邻 255 个数量,然后根据邻居的适当值执行点过滤。

我在下面给出了详细的解释,尽管一部分是反复试验,如果您了解卷积可以计算二进制图像中的邻居,您可能会跳过它并直接进入代码。


第一个观察:点何时属于掩码的周长?
嗯,该点的值必须为 255 并且"围绕"它,必须至少有一个(可能更多)0。

下一篇:">周围"的定义是什么?
我们可以考虑所有四个主要(即北、东、南、西)邻居。在这种情况下,周界的一个点必须至少有一个基数邻居,即 0.
您已经这样做了,而且确实,根据这个定义,我无法找到更快的方法

如果我们扩展"周围"的定义会怎样?
现在,让我们考虑一个点的相邻点,(i,j)(i,j)为中心的N x N正方形的所有点。也就是说,所有点都(x,y)这样的i-N/2 <= x <= i+N/2j-N/2 <= y <= j+N/2(其中N是奇数的,暂时忽略越界).
从性能的角度来看,这更有用,因为沿 2D 数组点滑动"窗口"的操作称为"卷积"操作。有一些内置函数可以非常快速地在 numpy 数组上执行此类操作。scipy.ndimage.convolve效果很好.
我不会在这里尝试完全解释卷积(互联网上充满了漂亮的视觉效果),但主要思想是卷积本质上用其所有相邻单元格的值的加权和替换每个单元格的值。根据您指定的权重矩阵(或内核),卷积会执行不同的操作。

现在,如果你的掩码是 1 和 0,要计算单元格周围相邻掩码的数量,你需要在任何地方都有一个 1 的核矩阵(因为加权和将简单地添加掩码的原始和并取消 0)。因此,我们将值从 [0, 255] 缩放到 [0,1]。

太好了,我们知道如何快速计算区域内某个点的邻居,但两个问题是

  1. 我们应该选择什么面积?
  2. 现在我们包括对角线和更远的邻居,周长的点有多少个相邻点?

我想对此有一个明确的答案,但我做了一些试验和错误。事实证明,我们需要N=5,在这种情况下,原始掩码中每个点的邻居数量如下:

[[ 3  5  8 10 11 10  8  5  3]
[ 5  8 12 15 16 15 12  8  5]
[ 8 12 17 20 21 20 17 12  8]
[10 15 20 24 25 24 20 15 10]
[11 16 21 25 25 25 21 16 11]
[10 15 20 24 25 24 20 15 10]
[ 8 12 17 20 21 20 17 12  8]
[ 5  8 12 15 16 15 12  8  5]
[ 3  5  8 10 11 10  8  5  3]]

将该矩阵与原始掩码进行比较,周长上的点是值介于1115(含)之间的点 [1]。所以我们简单地使用np.where()过滤掉其余的。

最后需要注意的是:我们需要向convolve函数明确说明如何处理边缘附近的点,N x N窗口不适合。在这些情况下,我们告诉它将越界值视为 0。

完整代码如下:

from scipy import ndimage as ndi
mask   //= 255
kernel = np.ones((5,5))
C      = ndi.convolve(mask, kernel, mode='constant', cval=0)
#print(C) # The C matrix contains the number of neighbors for each cell.
outer  = np.where( (C>=11) & (C<=15 ), 255, 0)
print(outer)

[[  0   0   0   0 255   0   0   0   0]
[  0   0 255 255   0 255 255   0   0]
[  0 255   0   0   0   0   0 255   0]
[  0 255   0   0   0   0   0 255   0]
[255   0   0   0   0   0   0   0 255]
[  0 255   0   0   0   0   0 255   0]
[  0 255   0   0   0   0   0 255   0]
[  0   0 255 255   0 255 255   0   0]
[  0   0   0   0 255   0   0   0   0]]

[1] 请注意,我们也将点本身算作它自己的邻居之一。没关系。

我认为这会起作用,我根据我对您想要的问题的新理解编辑我的答案 255 圆的外部像素我在这里所做的是获取像素为 255items的所有轴(打印以查看),然后选择第一次出现和最后一次出现

,这很容易
result=np.where(pixel==255)
items=list(zip(result[0],result[1]))
unique=[]
perimiter=[]
for index in range(len(items)-1):
if items[index][0]!=items[index+1][0] or items[index][0] not in unique:
unique.append(items[index][0])
perimiter.append(items[index])
perimiter.append(items[-1])  

输出

[(0, 4),
(1, 2),
(1, 6),
(2, 1),
(2, 7),
(3, 1),
(3, 7),
(4, 0),
(4, 8),
(5, 1),
(5, 7),
(6, 1),
(6, 7),
(7, 2),
(7, 6),
(8, 4)]

最新更新