将L形分成两条线

Separate L shape into two lines

我想要检测标记的距离和方向。标记为L形;两条等宽等高的线连成一个L型

为了检测方向和距离,我需要将相机捕捉到的 L 形分解成两条线。

鉴于代码需要每帧 运行 多次,执行此操作的轻量级方法是什么?

我已经使用此处描述的算法实现了斑点检测:http://www.labbookpages.co.uk/software/imgProc/blobDetection.html .

我考虑过使用霍夫变换,但我担心执行每一帧都需要大量的处理能力。最重要的是,我认为这不是这种情况下最准确的方法。我已经进行了适当的分析以从单条线中提取出准确的矢量,但我不确定如何将 L 形切割成两条线。

下面是相机如何捕捉到 L 形的不同结果的图片。

我在想我可以通过用一条穿过边界框的质心和中心的线来分割 L 形,但是那行不通。

关于我认为可能如何完成的粗略头脑风暴草图:

扫描线 !

请参阅 Python 中的(更新的)演练 here,使用图像可视化我通过您的数据的旅程:)

跳到 "Scanlines" 解决方案的最后


扫描线

基础知识

假设

我假设这些限制来自您的样本图像

  • 你的图像中有(几个)L 形。
  • 您可以轻松地将它们分割(没有重叠的形状,没有一个 L 延续到另一个 L ...)
  • 您知道 Ls 中线条笔画的确切宽度

标记你的二值图像

您想首先知道哪些像素是哪个 L 的一部分,这是由 "labeling" 二值图像完成的。

还计算每个体积的边界框,如下所示:

正在旋转您的坐标系

现在真正的技巧是从 x/y 正交系统变为每个形状的 "L-Shaped referential"。

可以将其视为将 X 轴重新定义为 L 的一个分支,将 Y 轴重新定义为另一个分支。 一旦我们计算出一个到另一个的变换向量,我们就安全了!

让我们考虑一下 PCA 方式

我们现在面临的问题("estimate the biggest axis of variations in dataset")是可以使用协方差特征向量的时刻。

我不会深入探讨,但您可以查看此 intro to PCA 帖子以了解它。

问题通常在更高的维度 ("given a 50 dimensional dataset, compute the 10-biggest axes of variation in it") 中定义,但可以通过单独考虑每个形状并声明属于 L 的每个像素是一个点来扩展到二维点云问题你的 2D space.

虽然这会浪费计算能力,因为与通常的 PCA 情况相比,您已经对 L 点位置进行了限制(它们在一条线上,而不是随机分散)。这个问题涉及到的线性代数猛兽在这个小问题上是大材小用

Hough Lines,来救援!

您只想在 2D 图像中找到线条? 对直线使用霍夫变换(也称为 "hough lines")。 OpenCV 有它。

又是一个不错的介绍:OpenCV's python tutorial on Hough Lines

我使用了你的二值图像的骨架(这样每行只被投票一次),并手动选择了我为算法提供给 OpenCV 的参数。 这就是线条有时似乎与特定图像不完全匹配的原因,这是因为采样率等=)


新的希望

在您指出对计算速度的需求之后,我想到了一些更多的技术来利用您的图像属性。

RANSAC

我考虑过对您的数据使用 RANSAC 变体:毕竟,您想要将线拟合到您的点云中。 您可能知道,基本技术总结为

  • (随机)选择足够的数据来拟合模型(在你的例子中是行)
  • 评估异常值的数量(模型对其不起作用的数据点)
  • 重复并记录得分最高的模型(并持续这样做一定时间,涉及数学)

RANSAC 的一个很好的介绍是 this song(很奇怪)

但我看到了并发症:

  • Which Model? : 你是用4个点来定义2条线,还是用2个点来定义一条线,再做两次?
  • 没有离群值:你真的没有合适的离群值,那么为什么要用 RANSAC 解决这样一个微不足道的问题?
  • 计算能力 : 你真的会无缘无故地迭代数千次,因为你在随机查看你的点。

毋庸置疑,RANSAC 不能只做这个把戏,但我们可以将其用作灵感

扫描线 !

让我们考虑一下您的 Ls 边界框。

如果我们在 Y=0 处对其进行水平切片,我们将得到一个一维数组,其连续区域定义为 True

那么,如果我们像这样每隔一段时间对图像进行切片以定义 L 的向量呢?

设置5个百分点作为基线,我们只需要找到"which X-index is the center of the 1D array of Y=0 values",然后对Y = 0.05 * img_width做同样的事情。

我们现在有 2 个 2D 点定义图像的第一条线。

在另一边重复,你就有了你的解决方案!

在计算上,您只是在 img_width 长度数组中找到 4 个中位数, 每一个都是来自你的图像的连续内存块(Heeeeelllo L2-cache 命中!)。

再说一次,如果刚才有点难以想象,请看我的code walkthrough

的结尾部分