OpenCV_霍夫变换_直线检测_HougLines
目录
原理介绍
OpenCV中的 C++ 函数定义
2.1 标准的霍夫线变换 cv::HoughLines
2.2 概率统计霍夫线变换 cv::HoughLinesP
示例1
示例2(官方)
1. 原理介绍
霍夫变换在检测各种形状的的技术中非常流行,如果你要检测的形状可以用数学表达式写出,你就可以是使用霍夫变换检测它。及时要检测的形状存在一点破坏或者扭曲也可以使用。我们下面就看看如何使用霍夫变换检测直线。
首先将一条直线用一个点表示,这样用一个点表示直线上所有的点,一开始人们使用斜截式y=kx+q中的(k,q)来表示一条直线。
变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):
再来看看A、B两个点,对应霍夫空间的情形:
一步步来,再看一下三个点共线的情况:
可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。如果不止一条直线呢?再看看多个点的情况(有两条直线):
其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式:选择由尽可能多直线汇成的点。看看,霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。
到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?
但这样做会有一个弊端,那就是当直线趋近于垂直时,斜率a会趋近于无穷大。这一困难的解决方法之一就是使用法线来表示直线。
在极坐标系下,其实是一样的:极坐标的点→霍夫空间的正弦线,这条正弦线上的所有点都过极坐标中的那个点。
简单来说就是每个点在霍夫空间的弦线与其他点的弦线交一次,就会在累加器的ρθ平面中累加一次。下面的动画很好的演示了这个过程。
2. OpenCV中的 C++ 函数定义
源码位置: imgproc.hpp
2.1 标准的霍夫线变换 cv::HoughLines
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0, double min_theta = 0, double max_theta = CV_PI )
参数:
- image:第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
- lines:第二个参数,InputArray类型的lines,经过调用HoughLines函数后储存了霍夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量表示,其中,rho 是离坐标原点((0,0)(也就是图像的左上角)的距离。 theta 是弧度线条旋转角度(0~垂直线,π/2~水平线)。
- lines:第三个参数,double类型的rho,以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。PS:Latex中/rho就表示 。
- theta:第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
- threshold:第五个参数,int类型的,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
- srn:第六个参数,double类型的srn,有默认值0。对于多尺度的霍夫变换,这是第三个参数进步尺寸rho的除数距离。粗略的累加器进步尺寸直接是第三个参数rho,而精确的累加器进步尺寸为rho/srn。
- stn:第七个参数,double类型的stn,有默认值0,对于多尺度霍夫变换,srn表示第四个参数进步尺寸的单位角度theta的除数距离。且如果srn和stn同时为0,就表示使用经典的霍夫变换。否则,这两个参数应该都为正数。
- min_theta:第八个参数,double类型的 min_theta,对于标准和多尺度Hough变换,检查线条的最小角度。必须介于0和max_theta之间。
- max_theta:第九个参数,double类型的 max_theta, 对于标准和多尺度Hough变换,检查线条的最大角度。必须介于min_theta和CV_PI之间.
2.2 概率统计霍夫线变换 cv::HoughLinesP
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold,double minLineLength=0, double maxLineGap=0 )
参数:
- image: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图) *
- lines: 储存着检测到的直线的参数对 的容器,也就是线段两个端点的坐标,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点。
- rho : 参数极径 以像素值为单位的分辨率. 我们使用 1 像素。以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。
- theta: 参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180);以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
- threshold: 要”检测” 一条直线所需最少的的曲线交点 ;累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
- minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.线段的最小长度。有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
- maxLineGap: 线段上最近两点之间的阈值;有默认值0,允许将同一行点与点之间连接起来的最大的距离。
3. 示例1
cv::Mat image = cv::imread("../../aTestImage/road2.jpg", 0);//Building
cv::Mat contours;
cv::Canny(image, contours, 125, 350);//主体为白色
std::vector<cv::Vec2f> lines;//二维向量组
cv::HoughLines(contours, lines, 1, PI / 180, 150);//获取hough变换的直线数据存储在lines向量中
std::cout << "lines count:" << lines.size() << std::endl;
cv::Mat result(contours.rows, contours.cols, CV_8U, cv::Scalar(255));//和轮廓图像大小一致的图像
image.copyTo(result);
std::vector <cv::Vec2f>::const_iterator it = lines.begin();
while (it != lines.end())
{
float rho = (*it)[0];//半径
float theta = (*it)[1];//角度
if (theta<PI / 4. || theta>3.*PI / 4.)
{
//垂直线
cv::Point pt1(rho / cos(theta), 0);//与第一行交点
cv::Point pt2((rho - result.rows*sin(theta)) / cos(theta), result.rows);//与最后一行交点
cv::line(result, pt1, pt2, cv::Scalar(255), 1);
}
else
{
//水平线
cv::Point pt1(0, rho / sin(theta));//与第一列交点
cv::Point pt2(result.cols, (rho - result.cols*cos(theta)) / sin(theta));//与最后一列交点
cv::line(result, pt1, pt2, cv::Scalar(255), 1);
}
std::cout << "lines Detail:" <<"("<<rho<<","<<theta <<")"<< std::endl;
++it;
}
cv::namedWindow("resultL", 1);
cv::imshow("resultL", result);
4. 示例2(官方)
/* This is a standalone program. Pass an image name as the first parameter
of the program. Switch between standard and probabilistic Hough transform
by changing "#if 1" to "#if 0" and back */
#include <cv.h>
#include <highgui.h>
#include <math.h>
using namespace cv;
int main(int argc, char** argv)
{
Mat src, dst, color_dst;
if( argc != 2 || !(src=imread(argv[1], 0)).data)
return -1;
Canny( src, dst, 50, 200, 3 );
cvtColor( dst, color_dst, CV_GRAY2BGR );
#if 0
vector<Vec2f> lines;
HoughLines( dst, lines, 1, CV_PI/180, 100 );
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0];
float theta = lines[i][1];
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
Point pt1(cvRound(x0 + 1000*(-b)),
cvRound(y0 + 1000*(a)));
Point pt2(cvRound(x0 - 1000*(-b)),
cvRound(y0 - 1000*(a)));
line( color_dst, pt1, pt2, Scalar(0,0,255), 3, 8 );
}
#else
vector<Vec4i> lines;
HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );
for( size_t i = 0; i < lines.size(); i++ )
{
line( color_dst, Point(lines[i][0], lines[i][1]),
Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 );
}
#endif
namedWindow( "Source", 1 );
imshow( "Source", src );
namedWindow( "Detected Lines", 1 );
imshow( "Detected Lines", color_dst );
waitKey(0);
return 0;
}
参考:OpenCV-Python——第21章:霍夫(Hough)直线变换(直线检测)
还没有评论,来说两句吧...