我正在尝试了解质心到轮廓(CtC)检测器的工作原理。 Here是我在Github上找到的示例代码,我试图了解其背后的想法。在此示例中,作者尝试使用CtC检测速度标志。只有两个重要功能:
pre_process()
CtC_features
我已经了解了代码的一部分及其工作方式,但是在理解 CtC_features 函数的工作方式时遇到了一些问题。 如果您可以帮助我,我想了解以下内容(仅3分):
我想了解这段代码背后的思想,如果有更多经验和了解三角学的人对我有帮助,那就太好了。
谢谢。
答案 0 :(得分:4)
您提供的代码不是最好的,但是让我们看看会发生什么。
我拍了一个标志的起始图像:
然后,调用pre_process
,它基本上运行一个Canny边缘检测器,以及一些技巧,可以使边缘检测更好。我不会调查它们,但这就是它的返回内容:
不是最大的。也许一些参数调整会有所帮助。
但是现在调用CtC_features
,这是问题的范围。 CtC_features
的作用是为机器学习算法获取一些功能。这相当于找到图像的数字描述,这将有助于ML算法检测符号。这样的描述可以是任何东西。想一想一个从未见过STOP标志并且不知道如何阅读的人将如何描述它。他们会说类似“一块红色的平板,有8个侧面,中间有一些白色的东西”。根据此说明,可能有人可以说出它是STOP符号。我们想做同样的事情,但是由于计算机是计算机,因此我们寻找数值特征。并且,借助它们,可以训练一些算法来“学习”每个标志的特征。
所以,让我们看看CtC_features
从轮廓中获得了什么特征。
它要做的第一件事是调用findContours
。此函数获取二进制图像,并返回代表图像轮廓的点数组。基本上,它将边缘并放入点数组中。使用连通性,因此我们基本上知道哪些点已连接。如果使用此处的代码进行可视化,我们可以看到发生了什么:
因此,数组contours
是一个std::vector<std::vector<cv::Point>>
,您在每个子数组中都有一个连续的轮廓,此处以不同的颜色绘制。
接下来,我们计算点(边缘像素)的数量,并对它们的坐标进行平均以找到边缘图像的质心。重心是实心圆:
然后,我们遍历所有点,并创建一个std::pair<double, double>
向量,为每个点记录距质心和角度的距离。角度函数在文件底部定义为
double angle(Point2f a, Point2f b) {
return atan((a.y - b.y) / (a.x - b.x));
}
基本上,它使用反正切函数计算从a
到b
的线相对于x轴的角度。我将让您观看video的反正切,而tl; dr是它为您提供了一个比率的角度。以弧度为单位(一个圆为2 PI弧度,一个半圆为PI弧度)。问题在于该函数是周期性的,周期为PI。这意味着圆上有2个角度(所有点的圆在质心周围的距离相同)都具有相同的值。因此,我们计算比率(比率被称为角度的正切),应用反函数(arctangent),然后得到一个角度(对应于一个点)。但是,如果还有另一点呢?好吧,我们知道另一个点恰好具有PI度偏移(完全相反),因此,如果我们检测到另一个点,则添加PI。
下面的图片也有助于理解为什么有两点:
角度的切线突出显示垂直距离。但是,对角线另一侧与左下角的圆相交的角度也具有相同的切线。 atan
函数仅针对中心左侧的角度提供切线。请注意,没有两个切线方向相同的方向。
检查的目的是询问该点是否在质心的右侧。这样做是为了能够加上半个圆(PI弧度或180度)以校正atan
的结果。
现在,我们知道了距离(一个简单的公式),并且已经找到(并校正了)该角度。我们将此对插入向量feature_v
中,并对其进行排序。这样的排序函数在该对的第一个元素之后排序,因此我们在角度之后排序,然后在距离之后排序。
interval
变量:
int degree = 10;
double interval = double((double(degree) / double(360)) * 2 * 3.14159); //5 degrees interval
仅是degree
的值,从度转换为弧度。我们需要弧度,因为到目前为止已经以弧度计算了角度,并且度数对用户更友好。是的,评论是错误的,间隔是10度,而不是5度。
在其下方定义的ang
变量为-PI / 2(四分之一圈):
double ang = - 1.57079;
现在,要做的就是根据角度将质心周围的点划分为箱。每个垃圾箱宽10度。这是通过迭代按角度排序的点来完成的,所有点都被累积,直到到达下一个bin。我们只对每个容器中一个点的最大距离感兴趣。起点应该足够小,以至于可以捕获所有方向(点)。
为了理解为什么它从-PI / 2开始,我们必须回到上面的三角函数图。如果角度是这样的话,会发生什么:
查看突出显示的垂直线段如何在y轴上“向下”移动。这意味着此处的长度(以及隐含的切线)为负。同样,角度也被认为是负的(否则,在中心的同一侧将有2个角度且切线相同)。现在,我们对我们拥有的角度范围感兴趣。它是形心右侧的所有角度,从-PI / 2的底部开始到PI / 2的顶部开始。 PI弧度范围或180度。这也写在atan的文档中:
如果未发生错误,则返回arg(arctan(arg))的反正切值,其范围为[-PI / 2,+ PI / 2]弧度。
因此,我们将所有可能的方向(360度)简单地分成10度的水桶,并取每个仓中最远点的距离。由于圆具有360度,我们将获得360/10 = 36个bin。然后,将它们标准化以使最大值为1。这对机器学习算法有帮助。
我们怎么知道所选的点是否属于符号?我们没有。大多数计算机视觉都会对图像进行一些假设,以简化问题。该算法的思想是通过记录从中心到边缘的距离来确定标志的形状。这使得质心大致位于符号中间。根据所使用的ML算法和训练数据,可以获得不同级别的鲁棒性。
此外,它假设可以可靠地识别(某些)边缘。看看我的图像中的算法如何无法检测到左上边缘?
好消息是,这不一定是完美的。机器学习算法知道如何处理这种变化(在某种程度上),只要对它们进行适当的训练即可。它不一定是完美的,但必须足够好。为了回答足够好的含义,该算法的实际局限性是什么,需要做更多的测试,以及对使用的ML算法的一些理解。但这也是ML在视觉界如此流行的原因:它可以很好地处理很多变化。
最后,我们基本上得到了一个由36个数字组成的数组,每个10度的36个面元中的每一个对应一个,代表面元中点的最大距离。我认为这是因为算法的开发人员想要一种方法,通过观察各个方向上距中心的距离来捕获标志的形状。假设在背景中没有检测到边缘,并且该符号看起来像:
最大距离用于拾取边框,而不是符号上的或其他符号。
这里没有直接使用它,但是可能相关的读数是Hough transform,它使用类似的规范来检测图像中的直线。