OpenCV 边缘检测—霍夫变换直线检测详细原理 代码实现
OpenCV 边缘检测—霍夫变换直线检测详细原理 代码实现
霍夫变换(Hough Transform)是一种在图像处理中用于检测特定形状(如直线、圆等)的算法。本文将详细介绍霍夫变换在OpenCV中的直线检测原理,并通过代码实现帮助读者理解这一技术。
霍夫变换(Hough Transform)是一种图像分析技术,广泛用于检测图像中的形状,如直线、圆形等。霍夫直线变换特别适合于检测边缘图像中的直线。它通过将图像中的点转换到参数空间,并寻找累积的高值来确定直线的存在。
霍夫直线变换的原理
在直角坐标系中,用斜截式 y=kx+b表示直线,其中 k是斜率,b是截距。目前,在图像处理中,已知的是像素点的(x,y)坐标,想要确定一条直线,我们得知道斜率k,和截距b。尝试使用x和y 来表示k和b,一般的可能的直线很容易得就能表示出来,但是当y垂直于x的时候,k就不好表示了,因为k=y/x x趋于0了,k趋于无穷大了。
这个时候,可以使用极坐标来表示一条直线,在极坐标系中,任何直线都可以用 ρ和 θ来表示:
ρ = x c o s ( θ ) + y s i n ( θ ) ( 1 ) ρ=xcos(θ)+ysin(θ)(1)ρ=xcos(θ)+ysin(θ)(1)
其中 ρ 是直线到原点的垂直距离,θ 是直线与x轴的夹角。这个表示方式对所有角度的直线都适用,无论直线是水平、垂直还是斜向的,都可以用一个统一的公式来描述。
还是同样的问题, 我们已知的是 x和y 。未知的是 ρ 和 θ
将方程(1)转换一下,我们使用极坐标形式来表示直线:
ρ = x c o s ( θ ) + y s i n ( θ ) ρ=xcos(θ)+ysin(θ)ρ=xcos(θ)+ysin(θ)
这里:
- ρ 是直线到原点的垂直距离。
- θ 是直线与 x 轴的夹角。
- x和 y是边缘点的坐标。
对于图像中的每个边缘点 (x,y),我们不知道具体的 ρ 和 θ 是多少,但我们可以枚举所有可能的 θ(通常是从 0 到 180 度)。对于每个 θ,我们可以计算出对应的 ρ 值!这样,边缘点 (x,y)就形成了一条曲线,因为我们在变化 θ 的过程中得到了一系列的 (ρ,θ)组合。每个组合都表示可能的一条通过这个边缘点的直线
也就是说每一个边缘点都对应着一条曲线,这就是霍夫空间
霍夫空间的横轴为0-180度。对应变化的就是关于ρ的曲线
霍夫直线检测的原理
基本步骤
当明白霍夫变换的原理之后,就很容易明白直线是怎么检测出来了
参数化直线:直线可以用不同的参数来描述,其中一种常用的方法是使用极坐标系统中的参数。假设直线的方程是 y=mx+c斜截式),但在霍夫变换中,我们通常使用以下极坐标形式来表示直线:
ρ = x c o s ( θ ) + y s i n ( θ ) ρ=xcos(θ)+ysin(θ)ρ=xcos(θ)+ysin(θ)
霍夫空间:在霍夫空间中,每条直线用一对参数 (ρ,θ)来表示。每个图像中的边缘点会对应到霍夫空间中的一条曲线。这条曲线的所有点都表示可能经过该边缘点的所有直线的参数。
累加器数组:霍夫变换使用一个累加器数组(霍夫空间),用来统计不同 (ρ,θ) 组合的直线的“票数”。每个图像边缘点在霍夫空间中形成一条曲线,累加器数组记录这些曲线的交点,这些交点代表图像中的直线。
检测直线:在霍夫空间中找到的局部极大值(即累加器数组中值最高的点)对应于图像中的直线。累加器数组中的值表示了通过多个边缘点的直线的可能性。值越大,表示更多的边缘点符合这个 (ρ,θ)组合,也就是说,图像中有更多的边缘点位于这条直线上。这些点的 (ρ,θ)参数表示了图像中的实际直线。
举个栗子
假设图像中有多个边缘点 (x1,y1), (x2,y2), (x3,y3) 等,它们的霍夫空间曲线可能在某个点 (ρ0,θ0) 处重合,这意味着这些边缘点很可能位于同一条直线 ρ0=xcos(θ0)+ysin(θ0)上。因此,这个 (ρ0,θ0) 组合对应的累加器数组值会很大,表示直线存在的可能性很高。
图例说明
左侧为像素点,右侧为 ρ 和 θ累加次数最多的p和 θ 极有可能是一条直线
代码实现
int main()
{
// 读取图像
Mat image = imread("C:\\Users\\Marxist\\Pictures\\coco\\driver.jpg");
if (image.empty()) {
cout << "Could not open or find the image" << endl;
return -1;
}
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
Mat blur_gray;
GaussianBlur(gray,blur_gray,Size(3,3),0);
// 使用Canny边缘检测
Mat edges;
Canny(blur_gray, edges, 50, 150, 3);
// 标准霍夫直线变换
vector<Vec2f> lines;
HoughLines(edges, lines, 1, CV_PI/180, 120);
// 在原始图像上绘制检测到的直线
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(image, pt1, pt2, Scalar(0, 0, 255), 2, LINE_AA);
}
// 显示结果
imshow("Original Image", image);
imshow("Detected Edges", edges);
waitKey();
return 0;
}
当然可以,下面是对你的代码的详细解释,特别是关于霍夫变换部分。
详细解释
- 读取图像
Mat image = imread("C:\\Users\\Marxist\\Pictures\\coco\\driver.jpg");
if (image.empty()) {
cout << "Could not open or find the image" << endl;
return -1;
}
- 使用
imread
函数读取指定路径的图像文件。如果图像文件无法打开或读取,程序输出错误信息并返回 -1 退出程序。
- 灰度转换
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
- 使用
cvtColor
函数将读取的彩色图像转换为灰度图像。这是因为霍夫变换通常在灰度图像上执行,以简化计算。
- 高斯模糊
Mat blur_gray;
GaussianBlur(gray, blur_gray, Size(3, 3), 0);
- 使用
GaussianBlur
函数对灰度图像进行高斯模糊处理,以减少噪声和细小边缘的影响。模糊核的大小为 3x3,标准差为 0。
- Canny 边缘检测
Mat edges;
Canny(blur_gray, edges, 50, 150, 3);
- 使用
Canny
函数进行边缘检测,参数分别为低阈值 50,高阈值 150,Sobel 核的大小为 3。这个步骤提取图像中的边缘信息,生成一个二值图像,边缘像素为白色,非边缘像素为黑色。
- 霍夫直线变换
vector<Vec2f> lines;
HoughLines(edges, lines, 1, CV_PI / 180, 120);
- 使用
HoughLines
函数执行标准霍夫变换来检测直线。 - edges
:输入的边缘检测结果图像。 - lines
:输出的直线参数数组,每条直线用极坐标系中的 (ρ,θ) 表示。 - 1
:累加器的距离分辨率,这里设置为 1 像素。 - CV_PI / 180
:角度分辨率,这里设置为 1 度(用弧度表示)。 - 120
:累加器阈值,只有当某个 (ρ,θ) 位置的累加值超过这个阈值时,才认为检测到一条直线。
- 绘制检测到的直线
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(image, pt1, pt2, Scalar(0, 0, 255), 2, LINE_AA);
}
- 通过遍历检测到的直线参数
lines
数组,使用极坐标公式将 (ρ,θ) 转换为图像中的两点 ((pt1, pt2)),并用
line
函数在原始图像上绘制直线。 - 使用点斜式直线方程,延伸直线以绘制在图像上,保证直线在图像范围内可见。
- Scalar(0, 0, 255)
指定线条颜色为红色,
2
指定线条宽度,
LINE_AA
指定抗锯齿线条。
- 显示结果
imshow("Original Image", image);
imshow("Detected Edges", edges);
waitKey();
return 0;
- 使用
imshow
函数显示原始图像及边缘检测结果图像,
waitKey
函数等待用户按键,程序结束。
效果分析
当累加器阈值设置为60,基本上检测全是直线
当阈值设置为80,可信度增加,线条数量减少
继续增加阈值到120,线条数量继续减少
因此,根据不同图片,要进行不同的阈值实验进行不同分析
参考文章:
Hough Line Transform — OpenCV 3.0.0-dev 文档
OpenCV-Python——第21章:霍夫(Hough)直线变换(直线检测)_python opencv 霍夫直线-CSDN博客
霍夫变换直线检测(Line Detection)原理及示例_霍夫变换直线检测原理-CSDN博客