python - 在 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 函数或技巧可以更快地执行此操作?
解决方案
我认为使用morpholical transforms有一个非常简单的解决方案。opening
(an后跟erosion
a dilation
)将简单地减少小于您所需大小(3x3)的区域,然后恢复剩余的区域。a
这是转换后的视图uint8
:
现在我将申请opening
它:
out = cv2.morphologyEx(a, cv2.MORPH_OPEN, np.ones((3,3), dtype=np.uint8))
可视化out
:
如您所见,只需一行代码即可识别矩形区域。您也可以将此输出用作位掩码来过滤掉原始图像。
a_ = a.copy()
a_[np.logical_not(out.astype('bool'))] = 0
现在,如果您需要找出矩形的角坐标,则更具挑战性。你可以打破大炮并应用轮廓检测,但我觉得更简单的连接组件分析也应该工作。
from skimage.measure import label
out_ = label(out, connectivity=1)
现在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)
推荐阅读
- bash - 当子进程反复产生并杀死 -9 时如何在 bash 脚本退出时杀死后台子进程
- jquery - 为所有控制器/动作显示一次模态弹出窗口
- java - 将子字符串添加到 Zoo
- java - 视频播放无数firebase
- php - 如何计算通过会员注册的人的预订
- android - Kivy App 卡在 Presplash 上
- java - 在哪些情况下需要将 volatile 关键字与 Atomic 字段一起使用?
- vba - 变量未更新通过 ByRef
- qt - QML 中基于 WebSockets 的 Qt WebView 和 WebChannel
- python-3.x - Python 3 结合文件打开和读取命令 - 需要关闭文件以及如何关闭?