开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第28天
1 霍夫线变换的原理
(1)众所周知,一条直线在图像二维空间可由两个变量表示,有以下两种情况。如图所示。
①在笛卡尔坐标系:可由参数斜率和截距(m,b)表示。
②在极坐标系:可由参数极径和极角(r,θ)表示。
对于霍夫变换,我们将采用第二种方式极坐标系来表示直线.因此,直线的表达式可为:
化简便可得到:
(2)一般来说对于点(xoyo),可以将通过这个点的一族直线统一定义为:
这就意味着每一对(ro,θ)代表一条通过点(xoyo)的直线。
(3)如果对于一个给定点(xoyo),我们在极坐标对极径极角平面绘出所有通过它的直线,将得到一条正弦曲线.例如,对于给定点x0=8和yo=6可以绘出如图所示平面图。
只绘出满足下列条件的点r>0和.0<θ<2π。
(4)我们可以对图像中所有的点进行上述操作,如果两个不同点进行上述操作后得到的曲线在平面θ—r相交,这就意味着它们通过同一条直线。例如,接上面的例子继续对点x1=9,y1=4和点x2=12,y2=3绘图,得到图7.18。
这三条曲线在平面相交于点(0.925,9.6),坐标表示的是参数对θ—r或者是说点(xoyo),点(x1y1)和点(x2y2)组成的平面内的的直线。
(5)以上的说明表明,一般来说,一条直线能够通过在平面θ—r寻找交于一点的曲线数量来检测。而越多曲线交于一点也就意味着这个交点表示的直线由更多的点组成。一般来说我们可以通过设置直线上点的阈值来定义多少条曲线交于一点,这样才认为检测到了一条直线。
(6)这就是霍夫线变换要做的。它追踪图像中每个点对应曲线间的交点.如果交于一点的曲线的数量超过了阈值,那么可以认为这个交点所代表的参数对(θ,ro)在原图像中为一条直线。
2 标准霍夫变换:HoughLines()函数
此函数可以找出采用标准霍夫变换的二值图像线条。在OpenCV中,我们可以用其来调用标准霍夫变换SHT和多尺度霍夫变换MSHT的OpenCV内建算法。
C++:
void HoughLines(InputArray image, OutputArray lines, double rho, double theta,
int threshold, double srn=0, double stn=0);
- 第一个参数,InputArray类型的image,输入图像,即源图像。需为8位的单通道二进制图像,可以将任意的源图载入进来,并由函数修改成此格式后,再填在这里。
- 第二个参数,InputArray类型的lines,经过调用 HoughLines 函数后储存了霍 夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量(p,θ)表示,其中,p是离坐标原点(0,0)(也就是图像的左上角)的距离,θ是弧度线条旋转角度(0度表示垂直线,π/2度表示水平线)。
- 第三个参数,double类型的rho,以像素为单位的距离精度。另一种表述方式是直线搜索时的进步尺寸的单位半径。(Latex 中/rho即表示p)
- 第四个参数,double类型的theta,以弧度为单位的角度精度。另一种表述方式是直线搜索时的进步尺寸的单位角度。
- 第五个参数,int类型的threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
- 第六个参数,double类型的srn,有默认值0。对于多尺度的霍夫变换,这是第三个参数进步尺寸 rho的除数距离。粗略的累加器进步尺寸直接是第三个参数rho,而精确的累加器进步尺寸为rho/srn。
- 第七个参数,double 类型的 stn,有默认值0,对于多尺度霍夫变换,smn表示第四个参数进步尺寸的单位角度 theta的除数距离。且如果 srn和stn同时为0,就表示使用经典的霍夫变换。否则,这两个参数应该都为正数。
学完函数解析后,看一个以HoughLines为核心的示例程序,就可以全方位了解HoughLines函数的使用方法。
3 示例
代码:
//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//---------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------------------------------------------
int main( )
{
//【1】载入原始图和Mat变量定义
Mat srcImage = imread("1.jpg"); //工程目录下应该有一张名为1.jpg的素材图
Mat midImage,dstImage;//临时变量和目标图的定义
//【2】进行边缘检测和转化为灰度图
Canny(srcImage, midImage, 50, 200, 3);//进行一此canny边缘检测
cvtColor(midImage,dstImage, CV_GRAY2BGR);//转化边缘检测后的图为灰度图
//【3】进行霍夫线变换
vector<Vec2f> lines;//定义一个矢量结构lines用于存放得到的线段矢量集合
HoughLines(midImage, lines, 1, CV_PI/180, 150, 0, 0 );
//【4】依次在图中绘制出每条线段
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
line( dstImage, pt1, pt2, Scalar(55,100,195), 1, CV_AA);
}
//【5】显示原始图
imshow("【原始图】", srcImage);
//【6】边缘检测后的图
imshow("【边缘检测后的图】", midImage);
//【7】显示效果图
imshow("【效果图】", dstImage);
waitKey(0);
return 0;
}
效果图:
原图
效果图1
效果图2