霍夫变换(Hough Transform)于1962年由Paul Hough 首次提出,后于1972年由Richard Duda和Peter Hart推广使用,是图像处理领域内从图像中检测几何形状的基本方法之一。经典霍夫变换用来检测图像中的直线,后来霍夫变换经过扩展可以进行任意形状物体的识别,例如圆和椭圆。
霍夫变换运用两个坐标空间之间(图像空间和霍夫空间)的变换,将在一个图像空间中具有相同形状的曲线或直线映射成霍夫空间的一个点,从而把检测任意形状的问题转化为统计霍夫空间中点重合的问题。
更多关于霍夫变换的介绍可以参考:霍夫变换
霍夫变换检测直线具体步骤如下:
1.彩色图像转化为灰度图;
2.去噪(高斯核);
3.边缘提取(梯度算子、拉普拉斯算子、canny、sobel);
4.二值化(判断此处是否为边缘点,就看灰度值==255);
5.映射到霍夫空间(准备两个容器,一个用来展示 hough-space 概况,一个数组 hough-space 用来储存 voting 的值,因为投票过程往往有某个极大值超过阈值,多达几千,不能直接用灰度图来记录投票信息);
6.取局部极大值,设定阈值,过滤干扰直线;
7.绘制直线、标定角点
HoughLines() 函数可以进行标准和多尺度霍夫直线变换。
C++: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 )
Items | Disc. |
---|---|
image | 输入图像,必须是8位单通道图像,有可能被修改 |
lines | 检测到的线条参数集合 |
rho | 以像素为单位的距离步长 |
theta | 以弧度为单位的角度步长 |
threshold | 累加计数值的阈值参数,小于阈值的直线将被忽略 |
srn | 默认值为0,用于在多尺度霍夫变换中作为参数rho的除数, r h o = r h o / s r n rho=rho/srn rho=rho/srn |
stn | 默认值为0,用于在多尺度霍夫变换中作为参数theta的除数, t h e t a = t h e t a / s t n theta=theta/stn theta=theta/stn |
min_theta | 所检测的线条的最小角度,取值范围 介于 0 和 max_theta 之间 |
max_theta | 所检测的线条的最大角度,取值范围 介于 min_theta 和 CV_PI 之间 |
如果 srn 和 stn 同时为 0,就表示 HoughLines 函数执行标准霍夫变换,否则就是执行多尺度霍夫变换。
HoughLines函数输出检测到直线的矢量表示集合,每一条直线由具有两个元素的矢量(ρ, θ)表示,其中ρ表示直线距离原点(0, 0)的长度,θ表示直线的角度(以弧度为单位)。
HoughLines函数无法输出图像空间中线段的长度,这也是霍夫变换本身的弱点。
HoughLinesP() 函数提供渐进概率式霍夫变换。
void HoughLinesP
(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )Items | Disc. |
---|---|
image | 输入图像,必须是8位单通道图像,有可能被修改 |
lines | 检测到的线条参数集合 |
rho | 以像素为单位的距离步长 |
theta | 以弧度为单位的角度步长 |
threshold | 累加计数值的阈值参数,小于阈值的直线将被忽略 |
minLineLength | 默认值为0,表示最小线段长度阈值(像素) |
maxLineGap | 线段上最近两点之间的阈值。默认值为 0,表示直线断裂的最大间隔距离阈值。即如果有两条线段是在一条直线上,但它们之间有间隙,那么如果这个间隔距离小于该值,则被认为是一条线段,否则认为是两条线段。 |
HoughLinesP能够检测出线端,即能够检测出图像中直线的两个端点,确切地定位图像中的直线。
HoughLinesP函数输出检测到直线的矢量表示集合,每一条直线由具有四个元素的矢量(x1, y1, x2, y2)表示,其中(x1, y1)表示线段的起点,(x2, y2)表示线段的终点。
下面是调用 HoughLines() 函数的例子,HoughLinesP() 函数的调用类似。
#include
#include
using namespace cv;
using namespace std;
void drawLine(Mat &img,
vector lines,
double rows,
double cols,
Scalar scalar,
int n
)
{
Point pt1, pt2;
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0];
float theta = lines[i][1];
double a = cos(theta);
double b = sin(theta);
double x0 = a * rho, y0 = b * rho;
double length = max(rows, cols);
pt1.x = cvRound(x0 + length * (-b));
pt1.y = cvRound(y0 + length * (a));
pt2.x = cvRound(x0 - length * (-b));
pt2.y = cvRound(y0 - length * (a));
line(img, pt1, pt2, scalar, n);
}
}
int main()
{
Mat src = imread("D:\\Code\\test\\image\\pp.jpg", 1); //color
Mat img;
cvtColor(src, img, CV_BGR2GRAY);
if (img.empty())
{
cout << "load image error" << endl;
return -1;
}
Mat edge;
// get edge and binary image
Canny(img, edge, 80, 180, 3, false);
threshold(edge, edge, 170, 255, THRESH_BINARY);
//hought line
vector lines1, lines2;
HoughLines(edge, lines1, 1, CV_PI / 180, 100); //threshold = 100
HoughLines(edge, lines2, 1, CV_PI / 180, 120); //threshold = 120
//copy image
Mat img1, img2, img3;
src.copyTo(img1);
src.copyTo(img2);
drawLine(img1, lines1, edge.rows, edge.cols, Scalar(0,255,0), 2);
drawLine(img2, lines2, edge.rows, edge.cols, Scalar(0,255,0), 2);
//show image
imshow("edge", edge);
imshow("img", img);
imshow("img1", img1);
imshow("img2", img2);
imshow("src", src);
waitKey(0);
return 0;
}