我的目标是能够使用 OpenCV 3 复制此链接中显示的避障方法。他们提供的软件似乎仅适用于 Windows。我认为这可以使用 OpenCV 进行复制。我目前正在使用 Canny 边缘检测的第 2 步。我不确定我可以使用哪些函数来创建第 3 步,从底部填充图像直到检测到边缘。任何参考材料将不胜感激。谢谢。
2 回答
方法 0
这是完成此任务的标准、基于循环的方法。这个想法是从每列的底部开始,并将每个像素着色为白色,直到碰到一个白色像素。这就是 Piglet 下面的建议。
h, w = edges.shape[:2]
filled_from_bottom = np.zeros((h, w))
for col in range(w):
for row in reversed(range(h)):
if edges[row][col] < 255: filled_from_bottom[row][col] = 255
else: break
方法一
现在这个方法使用了一些numpy
技巧来加速操作。
首先,对于每一列,找到边缘图像中存在非零值的最大行索引。
h, w = img.shape[:2]
row_inds = np.indices((h, w))[0] # gives row indices in shape of img
row_inds_at_edges = row_inds.copy()
row_inds_at_edges[edges==0] = 0 # only get indices at edges, 0 elsewhere
max_row_inds = np.amax(row_inds_at_edges, axis=0) # find the max row ind over each col
然后您可以创建一个布尔数组,其中每个大于或等于最大索引的索引是True
:
inds_after_edges = row_inds >= max_row_inds
然后你可以简单地在布尔数组给出的那些新索引处用白色填充一个新的空白图像
filled_from_bottom = np.zeros((h, w))
filled_from_bottom[inds_after_edges] = 255
方法二
这种方法的内存效率略高,速度效率略高。与方法一的基本前提相同。
首先,对于每一列,找到对应于每一列中最大值的行索引(这将是边缘图像中的白色)。请注意,该函数np.argmax
将返回数组中最大值的第一个实例,而我们想要最后一个:
如果最大值多次出现,则返回与第一次出现对应的索引。
所以解决这个问题的一个简单方法是垂直翻转数组,但这会给我们提供反向数组的索引。看到单线我觉得单单解释更直观:
h, w = img.shape[:2]
max_row_inds = h - np.argmax(edges[::-1], axis=0)
切片[::-1]
从上到下反转边缘(或者可以使用np.flipud
)。然后由于数组被翻转,np.argmax
从末尾h - np.argmax
给出索引,因此将索引提供到正确定向的数组中。并且np.argmax(..., axis=0)
意味着我们在每一列上取最大值。
现在我们可以像以前一样创建布尔数组:
row_inds = np.indices((h, w))[0]
inds_after_edges = row_inds >= max_row_inds
这种方法更好一点的原因是因为我们没有创建数组的副本,而是删除了许多值的数组赋值。
速度测试
第一种方法是最简单的,但在 Python 中是最慢的。Python 循环非常慢,而numpy
操作通常在基于 C 或 Fortran 的方法中实现,因此它们非常敏捷。我用以下代码测试了差异:
import timeit
times = range(1000)
start_time = timeit.default_timer()
A = [method0(edges) for t in times]
print("method0: ", timeit.default_timer() - start_time)
start_time = timeit.default_timer()
B = [method1(edges) for t in times]
print("method1: ", timeit.default_timer() - start_time)
start_time = timeit.default_timer()
C = [method2(edges) for t in times]
print("method2: ", timeit.default_timer() - start_time)
所以每个方法运行 1000 次。结果:
method0: 62.79985192901222
method1: 0.9703722179983743
method2: 0.7760374149947893
正如预期的那样,我们看到最终的方法是最快的;只是比method1快一点,但并不疯狂。但是,基于循环的方法之间的差异是巨大的。
输出
其工作原理是从图像底部开始,逐个像素地垂直填充每个空的黑色像素,直到看到一个非黑色像素。然后填充停止该垂直列并继续下一个。
你不需要任何花哨的 OpenCV 函数。这可以通过简单地使用几个循环来完成。
您可以就地执行此操作,也可以使用使用零初始化的单独输出图像。
您所要做的就是从底部开始遍历图像的列。如果像素的值为零,则将输出像素设置为 255,一旦您击中一个非零像素,则将剩余像素设置为 0(或将它们保留为 0)