霍夫直线检测(投票)
霍夫变换是一种在图像中寻找直线,圆及其他简单形状的方法。 opencv支持两种不同的霍夫变换:标准霍夫变换(SHT)和累积概率霍夫变换(PPHT)。
在opencv中可以使用同一个函数来使用两种算法。
CvSeq* cvHonghLines2(CvArr* image, void* line_storage,int mehtod, double rho, double theta, int threshold, double param1 =0, double param2 =0 );
image 输入 8-比特、单通道 (二值) 图像
line_storage 存储检测到的线段,可以是序列或者单行/单列矩阵
mehtod Hough 变换变量 rho 与象素相关单位的距离精度theta 弧度测量的角度精度
threshold 阈值参数。如果相应的累计值大于 threshold, 则函数返回的这个线段
Param1:对传统 Hough 变换,不使用(0);对概率 Hough 变换,它是最小线段长度.
Param2:对传统 Hough 变换,不使用 (0);对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于param2时,将其合二为一。
二、霍夫直线检测原理
Hough变换在计算机视觉、军事防御、办公自动化等领域都得到了普遍的关注和广泛的应用。其基本思想是将原图像变换到参数空间,
用大多数边界点满足某种参数形式来描述图像中的线,通过设置累加器进行累积,求得峰值对应的点所需要的信息。
Hough变换以其对局部缺损的不敏感,对随机噪声的鲁棒性以及适于并行处理等优良特性,备受图像处理、模式识别和计算机视觉领域学者的青睐。
Hough变换的突出优点就是可以将图像中较为困难的全局检测问题转换为参数空间中相对容易解决的局部峰值检测问题。
1962 年,Paul Hough根据数学对偶性原理提出了检测图像直线的方法,此后该方法被不断地研究和发展,主要应用于模式识别领域中对二值图像进行直线检测。
1、Hough变换原理
a.在图像中检测直线的问题,其实质是找到构成直线的所有的像素点。那么问题就是从找到直线,变成找到符合y=kx+b的所有(x,y)的点的问题。
b.进行坐标系变化y=kx+b,变成b=-xk+y。这样表示为过点(k,b)的直线束。
c.x-y空间的直线上每一个点在k-b坐标系中都表现为经过(k,b)的直线。找到所有点的问题,转变为寻找直线的问题。
d.对于图像中的每一个点,在k-b坐标系中对应着很多的直线。找到直线的交点,就对应着找到图像中的直线。
目前,opencv霍夫检测直线常用的方法是,是将平面中任一条直线用极坐标方式表示:ρ=xcosθ+ysinθ ,
其中p表示直角坐标系中原点到直线的距离,θ表示x轴与p的夹角这样,图像平面上的一个点就对应到ρ-θ平面上的一条曲线上。
如果对位于同一直线上的n个点进行变换,原图像空间的n个点在参数空间对应得到有n条正弦曲线,并且这些曲线相交于一点。
2、Hough检测步骤
Hough变换在算法设计上就可以如下步骤:
a.在ρ,θ合适的最大值与最小值之间建立一个离散的参数空间,如下图1-1。
b.将参数空间(ρ,θ) 量化成m*n(m为ρ的等份数,n为θ的等份数)个单元,并设置累加器矩阵,如下图。
c.给参数空间的每一个单元分配一个累加器Q(i,j),并把累加器的初始值设置为0
d.对图像边界上的每一个点(x,y)带入ρ=xcosθ+ysinθ,求得每个θ对应的ρ值
e.在参数空间中,找到ρ和θ所对应的单元,并将该单元的累加器加1,即:Q(i,j)=Q(i,j)+1。
f.当直角坐标系中的所有点都经过4和5两步遍历后,检验参数空间中每个累加器的值,累加器最大的单元所对应的ρ和θ即为直角坐标系中直线方程的参数。
注意:当直角坐标系中的点分布在 R 条直线附近时,可在第 5 步检测累加器时,取出累加器中前 R 个值最大的单元所对应的ρk和θk(k=1,2,…,R ),
以ρk和θk为直角坐标系中直线方程式ρ=xcosθ+ysinθ的参数,即可同时实现多条直线的检测。
3、总结
由上所述,Hough 变换的基本策略是:用图像空间的边缘数据点计
算参数空间中的参考点的可能轨迹,并在一个累加器中给计算出参考点的计数,最后选出峰值。该峰值表明在图像空间上有一共线点较多的直线,
该直线的参数由累加器的ρ和θ决定,即按照 Q(i,j)=Q (i,j)+1确定,则图像空间中满足该式的点(x,y)就组成了该直线。
API介绍
void cv::HoughLinesP( InputArray _image, OutputArray _lines,
double rho, double theta, int threshold,
double minLineLength, double maxGap )
- image: 必须是二值图像,推荐使用canny边缘检测的结果图像;
- rho: 线段以像素为单位的距离精度,double类型的,推荐用1.0
- theta: 线段以弧度为单位的角度精度,推荐用numpy.pi/180 或CV_PI/180
- threshod: 累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。根据情况推荐先用100试试
- lines:是一个vector<Vec4i>,Vec4i是一个包含4个int数据类型的结构体,[x1,y1,x2,y2],可以表示一个线段
- minLineLength:线段以像素为单位的最小长度,根据应用场景设置
- maxLineGap:同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段
霍夫直线检测实例
-
高斯(均值)模糊
-
转灰度
-
canny边缘提取
-
霍夫边缘提取
以下为具体步骤以及实现:
1. 彩色图像RBG->灰度图Gray
(opencv上需要注意颜色空间是RGB还是BGR,CImg中RGB分别对应0,1,2通道)
2. 去噪(高斯核)
3. 边缘提取(梯度算子、拉普拉斯算子、canny; 此处实现用sobel)
4. 二值化(判断此处是否为边缘点,就看灰度值==255),在高斯去噪和边界提取之后都需要二值化
5. 映射到霍夫空间(此处准备两个容器,一个CImg用来展示hough-space概况,一个数组hough-space用来储存voting的值,因为投票过程往往有某个极大值超过255,多达几千,不能直接用灰度图来记录投票信息)
6. 取局部极大值,设定阈值,过滤干扰直线
7. 绘制直线、标定角点
示例代码
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
Mat src1,gray_src,src2,dst;
int main()
{
src1 = imread("C:\\Users\\马迎伟\\Desktop\\douyin.jpg");
if (src1.empty())
{
cout << "could not find src1" << endl;
return -1;
}
namedWindow("input", CV_WINDOW_AUTOSIZE);
imshow("input", src1);
//霍夫直线 canny边缘提取 --> cvtcolor转灰度 --> HoughLinesP霍夫边缘检测
cvtColor(src1,src1,CV_BGR2GRAY);
Canny(src1,gray_src,100,200,3,false);
cvtColor(gray_src,dst,CV_GRAY2BGR);
vector<Vec4f> lines;
//'1'生成极坐标时候的像素扫描步长,'CV_PI/180'生成极坐标时候的角度步长,'10'最小直线长度,'0'最大间隔(能构成一条直线)
HoughLinesP(gray_src,lines,1,CV_PI/180,10,0,0);
Scalar color = Scalar(0,0,255);
for (size_t i = 0; i < lines.size(); i++)
{
Vec4f plines=lines[i]; //一个plines里边是四个点一条直线
line(dst,Point(plines[0],plines[1]),Point(plines[2],plines[3]),color,3,LINE_AA);
}
imshow("output", dst);
waitKey(0);
return 0;
}