霍夫直线检测的作用——计算得到输入图像(一般是二值化的边缘检测结果图像)中包含的所有直线的数目与位置
- 在取得
图像边缘
的基础上,
对一些特定的几何形状边缘
,如直线、圆
,通过图像霍夫变换
把图像从平面坐标空间
变换到霍夫坐标空间
,
就可以通过求取霍夫空间
的局部极大值方法(其实就是霍夫空间中的曲线交集点)
,
得到极坐标空间
对应参数方程
中直线的两个参数(r,θ)
,
从而计算得到边缘图像中
的所有直线
(基于平面坐标
)的数目与位置
。
假设有一条直线如下图:
(红色部分是计算过程,递等到右下角的结果,待会儿要用)
在笛卡儿平面坐标系
统中的斜率参数与截距参数为(k,b)
;
若变换到
极坐标空间
则变成求取另外两个参数(r,θ)
,r 和 θ
之间的关系可以表示为:
(公式的来源运算过程见上图)
对于每个平面空间的像素点坐标(x,y)
,
随着角度θ
的取值不同,都会得到r值
,
(%+++%要点.B)而对于任意一条直线
来说,在极坐标空间
它的(r,θ)
都是固定不变的
,
则对于边缘图像
的每个平面空间坐标点
可绘制极坐标的曲线
如图所示:
- 上图中,
左侧是一个
平面空间的像素点
,
基于公式r = x * cosθ + y * sinθ
,
通过给定不同的θ
值,得到唯一对应r
值,
无数个(r,θ)
数对构成的一道极坐标曲线
;
右侧是三个
平面空间的像素点
,
基于公式r = x * cosθ + y * sinθ
,
通过给定不同的θ
值,得到唯一对应r
值,
无数个(r,θ)
数对构成的三道极坐标曲线
;
- 无论截图的左侧还是右侧,都是所谓
霍夫空间的一部分
,所谓霍夫空间
,如下图: 图片参考于此博文
霍夫空间 概念详析
霍夫空间
就是一个基于(r,θ)两个参数坐标轴的数据空间,
数量级规模
是可以是一个边缘图像
的像素点数量
;
并且这个空间包括了这样的一系列曲线 :
一个边缘图像
的所有(all & each,假设为 N 个)像素点(x,y)
,
基于公式r = x * cosθ + y * sinθ
,
通过给定不同的θ
值,得到唯一对应r
值,
无数个(r,θ)
数对构成对应上N个 像素点
的N 道 极坐标曲线(霍夫空间的曲线)
;
霍夫直线检测 的 知识要点
- (要点.A)
输入的边缘图像中的每一个像素点一 一 对 应
一条霍夫空间(or 极坐标参数)曲线
;
- (要点.B)
而对于边缘图像中的 任意一条直线
来说,在极坐标空间
它的(r,θ)
都是固定不变的
,
- (由上可得 要点.C)
霍夫空间中的一个交集点(若干曲线的交点(r,θ))
就是一条直线
(点的参数(r,θ)可变换成直线);- 而重叠在这个
交集点
上的霍夫(极坐标)曲线集
,
其实就是该交集点
代表的(存在边缘图像中 的 对应的)直线
所包含的(像素)点集
;
- (要点.D)
交集点
上累积的曲线
越多
;
对应(平面坐标系的边缘图像上的)直线
所包含的像素点集
就越多
;
也即对应直线
的长度
越大
;
霍夫直线检测 从二值化.边缘检测.结果图像到检测绘制出直线 的大概步骤
以上引用框中的内容是个人的梳理总结,下面继续读书笔记的内容。
- 由在平面空间
同属于一条直线的像素点
绘制出来的曲线
必然会相交于一点
(上方截图的b)右侧所示的曲线), - 而这个点正是
存在边缘对象中的对应的直线
在极坐标空间
中的参数方程的参数
,
这样就在极坐标空间找到了直线的参数方程,
反变换
回到平面坐标空间就可以求得直线的两个参数(k,b),
得到直线位置,
而它们在极坐标的交点
就是直线在霍夫空间的表达
,
直线越长
,其在霍夫空间这个点的累积值
就越高
,相对的灰度值
也就越(亮)
。
OpenCV关于霍夫直线变换
提供了两个相关API函数,
一个
是在霍夫空间求取直线两个极坐标的参数
,
需要开发者自己转换到平面坐标空间计算直线;
另外
一个则会直接返回平面空间直线/线段的两个点坐标信息
。
返回极坐标参数的API函数如下:
-
HoughLines(Mat image, Mat lines, double rho, double theta, int threshold)
image
:表示输入
图像,8位单通道图像,一般为二值图像。
lines
:表示输出
的每个直线的极坐标参数方程的两个参数。
rho
:表示极坐标空间r值
每次的步长,一般设置为1
。
theta
:表示角度θ
,每次移动1°
即可。
threshold
:表示霍夫空间
中该点的累积数
,
该累积数越大,则得到的直线可能就越长,
取值范围通常为30~50,单位是像素,
假设为30的话,则表示大于30个像素长度的线段才会被检测到。
threshold
解释中所述的累积数
可以看做我们数据处理中的投票机制
,
票数
大于threshold
的交集点
(即累积的曲线数
大于threshold
的交集点
),
才认定是有效直线
,
才能被函数检测到
并提取出来
用于返回/变换
并绘制
成直线;
使用该API实现直线检测:
private void houghLinesDemo(Mat src, Mat dst) {
Mat edges = new Mat();
Imgproc.Canny(src, edges, 50, 150, 3, true);
Mat lines = new Mat();
Imgproc.HoughLines(edges, lines, 1,Math.PI/180.0, 200);
Mat out = Mat.zeros(src.size(), src.type());
float[] data = new float[2];
for(int i=0; i<lines.rows(); i++) {
lines.get(i, 0, data);
float rho = data[0], theta = data[1];
double a = Math.cos(theta), b = Math.sin(theta);
double x0 = a*rho, y0 = b*rho;
Point pt1 = new Point();
Point pt2 = new Point();
pt1.x = Math.round(x0 + 100*(-b));//!!!!!!!!!!!!!!!!!
pt1.y = Math.round(y0 + 100*(a));
pt2.x = Math.round(x0 - 100*(-b));
pt2.y = Math.round(y0 - 100*(a));
Imgproc.line(out, pt1, pt2, new Scalar(0,0,255), 3, Imgproc.LINE_AA, 0);
}
out.copyTo(dst);
out.release();
edges.release();
}
关于pt1.x = Math.round(x0 + 100*(-b));这一行代码,
- 关于参数100的意义,可参考 原作者博文:
x0与y0是直线上的点,100是表示对该点到直线上分别向前后延长的距离;
在实际效果图中(下方结果图源程序用的原图是lena
),这个100偏移量,决定的就是这些生成直线的长度:
- 关于 Math.round()函数
- 关于 Imgproc.HoughLines() 与 Imgproc.HoughLinesP() 的 区别 以及 lines 参数位 的意义详析
以上的这个API函数需要对得到的每对极坐标参数(r,θ)
做计算
,
使其变换
到平面空间
(x0 = r * cosθ ; y0 = r * sinθ
),
接着通过对x0
和y0
添加偏移量
并进行计算,得到直线的两个点
;
然后绘制直线
。
另外一个
API函数则比较简单,
它省去了
开发者自己把极坐标变换为直线坐标的过程
,
直接返回
每个线段/直线对应的两个点坐标
,
其API函数与参数的解释具体如下:
-
HoughLinesP(Mat image, Mat lines, double rho, double theta, int threshold, double minLineLength, double maxLineGap)
image
:表示输入图像,8位单通道图像,一般为二值图像。
lines
:表示输出的每个直线最终要绘制用
的两个 平面坐标系参数
。
rho
:表示极坐标空间r值每次的步长,一般设置为1。
theta
:表示角度θ,每次移动1°即可。
threshold
:表示极坐标中该点的累积数,该累积数越大,则得到的直线可能就越长,取值范围通常为30~50,单位是像素,假设取值为30,则表示大于30个像素长度的线段才会被检测到。
minLineLength
:表示可以检测的最小线段长度
,根据实际需要进行设置。
maxLineGap
:表示线段之间的最大间隔像素,假设5表示小于5个像素的两个相邻线段可以连接
起来。
使用该API实现图像直线检测:
private void houghLinePDemo(Mat src, Mat dst) {
Mat edges = new Mat();
Imgproc.Canny(src, edges, 50, 150, 3, true);
Mat lines = new Mat();
Imgproc.HoughLinesP(edges, lines, 1, Math.PI/180.0, 100, 50, 10);
Mat out = Mat.zeros(src.size(), src.type());
for(int i=0; i<lines.rows(); i++) {
int[] oneline = new int[4];
lines.get(i, 0, oneline);
Imgproc.line(out, new Point(oneline[0], oneline[1]),
new Point(oneline[2], oneline[3]),
new Scalar(0, 0, 255), 2, 8, 0);
}
out.copyTo(dst);
// 释放内存
out.release();
edges.release();
}
- 这里需要注意的是,
图像二值化
与边缘检测算法输出结果
的质量
在很大程度上影响 霍夫直线变换
的结果
,
同时在使用HoughLinesP
的时候,最后两个参数
的设置
也会影响
霍夫直线检测的结果。
网友评论