霍夫直线变换介绍
- Hough Line Transform用来做直线检测
- 前提条件 – 边缘检测已经完成
- 平面空间到极坐标空间转换
霍夫线变换的原理
-
众所周知, 一条直线在图像二维空间可由两个变量表示. 如:
<1>在笛卡尔坐标系: 可由参数: 斜率和截距(m,b)表示。
<2>在极坐标系: 可由参数: 极径和极角(r, θ)表示。
-
可以用极坐标系来表示直线. 因此, 直线的表达式可为:
-
化简便可得到:
-
凡是,通过点( x x x0, y y y0)的直线,都可以表示成
则,这就意味着每一对( r r rθ, θ θ θ)代表一条通过点( x x x0, y y y0)的直线。 -
如果对于一个给定点( x x x0, y y y0)我们在极坐标对极径极角平面绘出所有通过它的直线, 将得到一条正弦曲线. 例如, 对于给定点 x x x0= 8 和 y y y0= 6 我们可以绘出下图 (此时, r r rθ和 θ θ θ为变量):
只绘出满足下列条件的点 r>0 和 0< θ < 2π . -
我们可以对图像中所有的点进行上述操作. 如果两个不同点进行上述操作后得到的曲线在平面 θ-r 相交, 这就意味着它们通过同一条直线. 例如,接上面的例子我们继续对点 x x x1= 9 和 y y y1= 4和点 x x x2= 12 和 y y y2= 3 绘图, 得到下图:
-
这三条曲线在平面相交于点 (0.925, 9.6), 坐标表示的是参数对 θ-r 或者是说点( x x x0, y y y0), 点( x x x1, y y y1)和点( x x x2, y y y2)组成的平面内的的直线。
-
以上的说明表明,一般来说, 一条直线能够通过在平面 θ-r 寻找交于一点的曲线数量来检测。而越多曲线交于一点也就意味着这个交点表示的直线由更多的点组成. 一般来说我们可以通过设置直线上点的阈值来定义多少条曲线交于一点我们才认为检测到了一条直线。
-
这就是霍夫线变换要做的. 它追踪图像中每个点对应曲线间的交点. 如果交于一点的曲线的数量超过了阈值, 那么可以认为这个交点所代表的参数对( θ θ θ, r r rθ)在原图像中为一条直线。
OpenCV中的霍夫线变换
-
霍夫线变换是一种用来寻找直线的方法. 在使用霍夫线变换之前, 首先要对图像进行边缘检测的处理,也即霍夫线变换的直接输入只能是边缘二值图像.
-
OpenCV支持三种不同的霍夫线变换,它们分别是:
标准霍夫变换(Standard Hough Transform,SHT),
多尺度霍夫变换(Multi-Scale Hough Transform,MSHT),
累计概率霍夫变换(Progressive Probabilistic Hough Transform ,PPHT)。
其中,多尺度霍夫变换(MSHT)为经典霍夫变换(SHT)在多尺度下的一个变种。累计概率霍夫变换(PPHT)算法是标准霍夫变换(SHT)算法的一个改进,它在一定的范围内进行霍夫变换,计算单独线段的方向以及范围,从而减少计算量,缩短计算时间。之所以称PPHT为“概率”的,是因为并不将累加器平面内的所有可能的点累加,而只是累加其中的一部分,该想法是如果峰值如果足够高,只用一小部分时间去寻找它就够了。这样猜想的话,可以实质性地减少计算时间。 -
在OpenCV中,我们可以用HoughLines函数来调用标准霍夫变换SHT和多尺度霍夫变换MSHT。
而HoughLinesP函数用于调用累计概率霍夫变换PPHT。累计概率霍夫变换执行效率很高,所有相比于HoughLines函数,我们更倾向于使用HoughLinesP函数。 -
总结一下,OpenCV中的霍夫线变换有如下三种:
- <1>标准霍夫变换(StandardHough Transform,SHT),由HoughLines函数调用。
- <2>多尺度霍夫变换(Multi-ScaleHough Transform,MSHT),由HoughLines函数调用。
- <3>累计概率霍夫变换(ProgressiveProbabilistic Hough Transform,PPHT),由HoughLinesP函数调用。
相关的API介绍
1. HoughLines( )函数详解
标准的霍夫变换 cv::HoughLines从平面坐标转换到霍夫空间,最终输出是 (θ,rθ)表示极坐标空间
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
说明:
cv::HoughLines(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线,经过调用HoughLines函数后储存了霍夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量(ρ,θ)表示,其中,ρ是离坐标原点((0,0)(也就是图像的左上角)的距离。 θ是弧度线条旋转角度(0~ 垂直线,π/2~水平线)。
double rho, // 生成极坐标时候的像素扫描步长,一般取值为 1 ,不要大于图像尺寸的一半
double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180,即表示一度
int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线
double srn=0;// 是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换,多尺度表示的是使用图像金字塔,即多尺度图上进行霍夫变换
double stn=0;//是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
double min_theta=0; // 表示角度扫描范围 0 ~180之间, 默认即可
double max_theta=CV_PI
) // 一般情况是有经验的开发者使用,需要自己反变换到平面空间
另外,关于霍夫变换的详细解释,可以看此英文页面:
http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm
2. HoughLinesP( )函数详解
- 此函数在HoughLines的基础上末尾加了一个代表Probabilistic(概率)的P,表明它可以采用累计概率霍夫变换(PPHT)来找出二值图像中的直线。
- 霍夫变换直线概率 cv::HoughLinesP最终输出是直线的两个点( x x x0, y y y0, x x x1, y y y1)
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
说明:
cv::HoughLinesP(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线,经过调用HoughLinesP函数后后存储了检测到的线条的输出矢量,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点。
double rho, // 生成极坐标时候的像素扫描步长,一般取值为 1
double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180,即表示一度
int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线
double minLineLength=0;// 最小直线长度,有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
double maxLineGap=0;// 最大间隔,有默认值0,允许将同一行点与点之间连接起来的最大的距离。
)
程序代码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void main(int argc, char** argv)
{
//1. 读取图像
Mat src, canny, dst;
src = imread("E:/Experiment/OpenCV/Pictures/LineTest.jpg");
imshow("src", src);
//2. 获取边缘
Canny(src, canny, 100, 200);
imshow("canny", canny);
//3. 转成灰度图像
cvtColor(canny, dst, CV_GRAY2BGR);//将二值图转换为RGB图颜色空间,这里重新创建一张空Mat也行
//4. 霍夫变换检测
vector<Vec4f> plines;//保存霍夫变换检测到的直线
HoughLinesP(canny, plines, 1, CV_PI / 180, 10, 0, 10);//提取边缘时,会造成有些点不连续,所以maxLineGap设大点
//5. 显示检测到的直线
Scalar color = Scalar(0, 0, 255);//设置颜色
for (size_t i = 0; i < plines.size(); i++)
{
Vec4f hline = plines[i];
line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 3, LINE_AA);//绘制直线
}
imshow("plines", dst);
waitKey(0);
}