首页 > 解决方案 > 在 numpy 数组中提取非零值组

问题描述

我正在尝试从 numpy 数组中提取非零值的矩形组。数组可能看起来像这样(但更大):

a = np.array([
     [0,0,0,0,0,0,0,0,0,0,0],
     [0,0,0,0,0,1,1,1,1,1,0],
     [0,0,0,0,6,1,1,1,3,1,0],
     [0,0,0,0,0,1,1,1,1,1,0],
     [0,0,0,0,2,2,2,0,1,0,0],
     [0,0,0,0,2,2,0,0,0,0,0],
     [0,0,0,0,0,0,0,0,0,0,0],
     [1,1,1,1,0,0,0,0,0,0,0],
     [1,1,1,1,0,0,0,0,7,2,0],
     [1,1,1,1,0,0,0,0,0,0,0]])

我想提取大于给定大小(例如大于3x3)的非零值的组/块,即这些块的最小和最大角的坐标。在这个例子中,我应该得到以下信息:

res = [[(7,0), (10,4)],
       [(1,5), (4,10)]]

以便

In [12]: xmin, ymin = res[0][0]

In [13]: xmax, ymax = res[0][1]

In [14]: a[xmin:xmax, ymin:ymax]
Out[14]:
array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]])

In [15]: xmin, ymin = res[1][0]

In [16]: xmax, ymax = res[1][1]

In [17]: a[xmin:xmax, ymin:ymax]
Out[17]:
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 3, 1],
       [1, 1, 1, 1, 1]])

我已经尝试查看数组的每个非零值,并从此时开始增长所需大小的形状,直到它确实包含零。它可以工作,但速度很慢。对于这个示例数组,大约需要 1.17 毫秒,在实际应用程序(即 600x1000 数组)中大约需要 18 秒,这太慢了。是否有 numpy 或 OpenCV 函数或技巧可以更快地执行此操作?

标签: pythonnumpy

解决方案


我认为使用morpholical transforms有一个非常简单的解决方案。opening(an后跟erosiona dilation)将简单地减少小于您所需大小(3x3)的区域,然后恢复剩余的区域。a这是转换后的视图uint8

f1:一个

现在我将申请opening它:

out = cv2.morphologyEx(a, cv2.MORPH_OPEN, np.ones((3,3), dtype=np.uint8))

可视化out

f2:出

如您所见,只需一行代码即可识别矩形区域。您也可以将此输出用作位掩码来过滤掉原始图像。

a_ = a.copy()
a_[np.logical_not(out.astype('bool'))] = 0

现在,如果您需要找出矩形的角坐标,则更具挑战性。你可以打破大炮并应用轮廓检测​​,但我觉得更简单的连接组件分析也应该工作。

from skimage.measure import label
out_ = label(out, connectivity=1)

f3: out_

现在out_数组中的每个区域都标有一个单独的数字,从 0 到 N_regions-1(其中 0 是背景区域)。其余的工作非常简单。您可以遍历每个数字并进行一些简单的 numpy 比较以找出每个编号区域的坐标。

利用 skimage 的优势,我们可以更快地完成工作regionprops。我们将把它应用到out_我们之前计算的标签图像上。

from skimage.measure import regionprops

for r in regionprops(out_):
  print('({},{}), ({},{})'.format(*r.bbox))

出去:

(1,5), (4,10)
(7,0), (10,4)

推荐阅读