【OpenCv】霍夫直线检测

前言

  Hough变换是实现边缘检测的一种有效方法,其基本思想是将测量空间的一点变换到参量空间的一条曲线或曲面,而具有同一参量特征的点变换后在参量空间中相交,通过判断交点处的积累程度来完成特 征曲线的检测。

1 原理

  保罗·哈夫于1962年提出了Hough变换法,并申请了专利。该方法 将图像空间中的检测问题转换到参数空间,通过在参数空间里进行简单的累加统计完成检测任务,并用大多数边界点满足的某种参数形式来描述图像的区域边界曲线。这种方法对于被噪声干扰或间断区域边界的图像具有良好的容错性。Hough变换最初主要应用于检测图像空间中的直线,最早的直线变换是在两个笛卡儿坐标系之间进行的,这给检测斜率无穷大的直线带来了困难。1972年,杜达(Duda)将变换形式进行了转化,将数据空间中的点变换为 ρ − θ ρ-θ ρθ参数空间中的曲线,改善了其检测直线的性能。该方法被不断地研究和发展,在图像分析、计算机视觉、模式识别等领域得到了非常广泛的应用,已经成为模式识别的一种重要工具。
  直线的方程可由下面式子表示:
y = k x + b \begin{aligned} &y = kx + b \end{aligned} y=kx+b
其中, k k k b b b分别是斜率和截距。现在将 y = k x + b y=kx+b y=kx+b转换成 b = − x k + y b=-xk+y b=xk+y,因为 k k k b b b都是确定值所以在 x − y x-y xy平面上的一条线在 k − b k-b kb平面上代表一个点。
在这里插入图片描述
反过来在 k − b k-b kb平面上的一条直线 b = − x k + y b=-xk+y b=xk+y x − y x-y xy平面上代表一个点,因为此时 x x x y y y在直线 b = − x k + y b=-xk+y b=xk+y中分别是斜率和截距为定值。在这里插入图片描述
其次是过 x − y x-y xy平面上的某一点( x 0 x_0 x0 y 0 y_0 y0) 的所有直线的参数都满足方程 y 0 = k x 0 + b y_0=kx_0+b y0=kx0+b。即过 x − y x-y xy平面上点( x 0 x_0 x0 y 0 y_0 y0)的一族直线在参数 k − b k-b kb平面上对应于一条直线。同样的道理将该族直线 y 0 = k x 0 + b y_0=kx_0+b y0=kx0+b转变到 k − b k-b kb平面上有 b = − x 0 k + y 0 b=-x_0k+y_0 b=x0k+y0,此时斜率( − x 0 -x_0 x0)和斜距( y 0 y_0 y0)固定, b b b k k k的函数,所以在 k − b k-b kb平面上对应于一条直线。
在这里插入图片描述
有了上面的知识,再来看看在 x − y x-y xy平面上三点共线是怎么等效到到 k − b k-b kb平面的。在这里插入图片描述
可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。再来考虑特殊情况,当三点共线恰好垂直 x x x轴呢?此时直线的斜率 k k k为无穷大, y = k x + b y=kx+b y=kx+b形式的直线方程无法表示 x = c x=c x=c c c c为常数)形式的直线。所以在实际应用中,一般采用距离和角度参数方程来表示如下:
ρ = x c o s θ + y s i n θ \begin{aligned} &ρ=xcosθ+ysinθ \end{aligned} ρ=xcosθ+ysinθ
其中, ρ ρ ρ为原点到直线的垂直距离, θ θ θ ρ ρ ρ x x x轴的夹角,转换过程如下,注意的是这个并不是极坐标表达式,只是形式写起来跟极坐标的形式是一样的。这是因为 ρ ρ ρ θ θ θ都是固定的,对应唯一的直线,而如果是极坐标,那其他对的 ρ ρ ρ θ θ θ也会满足这一直线。
在这里插入图片描述
  根据 ρ = x c o s θ + y s i n θ ρ=xcosθ+ysinθ ρ=xcosθ+ysinθ,直线上不同的点在参数空间中被变换为一族相交于 p p p点的正弦曲线,因此可以通过检测参数空间中的局部最大值 p p p点,来实现 x − y x-y xy坐标系中直线的检测。在这里插入图片描述

2 算法流程

①将参数空间量化成 m × n m×n m×n m m m θ θ θ的等份数, n n n ρ ρ ρ的等份数)个单元,并设置累加器矩阵 Q [ m × n ] Q[m×n] Q[m×n]
②给参数空间中的每个单元分配一个累加器 Q Q Q( θ i θ_i θi p j p_j pj)( 0 < i < m − 1 0<i<m- 1 0<i<m1 0 < j < n − 1 0<j<n-1 0<j<n1),并把累加器的初始值置为零;
③将直角坐标系中的各点( x k x_k xk y k y_k yk)( k = 1 k=1 k=1 2 2 2,…, s s s s s s为直角坐 标系中的点数)代入式 ρ = x c o s θ + y s i n θ ρ=xcosθ+ysinθ ρ=xcosθ+ysinθ,然后将 θ 0 ~ θ m − 1 θ_0~θ_{m-1} θ0θm1也都代入其中,分别计算出相应的值 p j p_j pj
④在参数空间中,找到每一个( θ i θ_i θi p j p_j pj)所对应的单元,并将该 单元的累加器加1,即 Q Q Q( θ i θ_i θi p j p_j pj) = = = Q Q Q( θ i θ_i θi p j p_j pj) + 1 +1 +1,对该单元进行一次投票;
⑤待 x − y x-y xy坐标系中的所有点都进行运算之后,检查参数空间的累加 器,必有一个出现最大值,这个累加器对应单元的参数值作为所求直线的参数输出。当然你可以指定一个阈值,就是投票数达到多少就可以认定为一条直线,这样就可以一次性输出多条直线。
例子:在这里插入图片描述
r r r( p p p)分成了9份,区间是[0,9], r r r一定是取有效值,因为我们的图像的最长直线就是其对角线;然后将 θ θ θ以90°为步长分成了4个区间,当然你可以分成你想要的 θ θ θ区间。然后开始遍历 Canny 图像(很关键,我们在利用霍夫变换进行直线检测时,要先对图像进行边缘检测)。
在这里插入图片描述
遇到黑点直接跳过,我们只关注白点。然后将每个白点的坐标( x 0 x_0 x0 y 0 y_0 y0)和四个角度 θ θ θ( θ 1 = 90 ° θ_1=90° θ1=90° θ 2 = 180 ° θ_2=180° θ2=180° θ 3 = 270 ° θ_3=270° θ3=270° θ 4 = 360 ° θ_4=360° θ4=360°)带入到 r = x c o s θ + y s i n θ r=xcosθ+ysinθ r=xcosθ+ysinθ中,得到对应的 r 1 r_1 r1 r 2 r_2 r2 r 3 r_3 r3 r 4 r_4 r4,根据这些 r r r值在对应区间进行投票。不断重复上述步骤直至图像遍历完毕。就可以根据 r r r的投票数来确定直线。

3 优缺点

  霍夫变换是一种全局性的检测方法,具有极佳的抗干扰能力,可 以很好地抑制数据点集中存在的干扰,同时还可以将数据点集拟合成多条直线。但是,霍夫变换的精度不容易控制,参数的微变就可能影响效果的大幅度变化,因此,不适合对拟合直线的精度要求较高的实际问题。同时,它所要求的巨大计算量使其处理速度很慢,从而限制了它在实时性要求很高的领域的应用

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
要将容器中的点拟合成四条或三条直线,可以使用霍夫变换算法。以下是使用OpenCV库实现的C++代码示例: ```c++ #include <opencv2/opencv.hpp> #include <vector> using namespace std; using namespace cv; void fitLines(vector<Point>& points, int numLines, vector<Vec4f>& lines) { Mat pointsMat(points); float rho = 1; // distance resolution in pixels float theta = CV_PI / 180; // angle resolution in radians float threshold = 50; // minimum number of intersections to detect a line float minLineLength = 50; // minimum length of a line float maxLineGap = 10; // maximum allowed gap between points on the same line HoughLinesP(pointsMat, lines, rho, theta, threshold, minLineLength, maxLineGap); // sort lines by their length sort(lines.begin(), lines.end(), [](const Vec4f& line1, const Vec4f& line2) { return norm(Point(line1[0], line1[1]) - Point(line1[2], line1[3])) > norm(Point(line2[0], line2[1]) - Point(line2[2], line2[3])); }); // keep only the first numLines lines lines.resize(numLines); } int main() { vector<Point> points = {Point(0, 0), Point(10, 10), Point(20, 0), Point(30, 10), Point(40, 0)}; vector<Vec4f> lines; fitLines(points, 2, lines); for (const auto& line : lines) { cout << "line: (" << line[0] << "," << line[1] << ") - (" << line[2] << "," << line[3] << ")" << endl; } return 0; } ``` 这里的`points`是输入的点集,`numLines`是要拟合的直线条数,`lines`是输出的直线参数。在函数`fitLines`中,我们使用了OpenCV霍夫变换函数`HoughLinesP`来得到直线参数。然后按照直线长度从大到小排序,取前`numLines`条直线即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

八岁爱玩耍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值