霍夫变换(Hough Line Transform)-直线检测
(2条消息) 霍夫变换(Hough Line Transform)-直线检测_zzyczzyc的博客-CSDN博客_hough line transform
Goal
在本教程中,您将学习如何:
使用 OpenCV 函数 HoughLines() 和 HoughLinesP() 检测图像中的线条
Theory
笔记
下面的解释属于 Bradski 和 Kaehler 的《Learning OpenCV》一书。
Hough Line Transform
-
- 霍夫线变换是一种用于检测直线的变换。
- 为了应用变换,首先需要进行边缘检测预处理。
The Hough Line Transform is a transform used to detect straight lines.
To apply the Transform, first an edge detection pre-processing is desirable.
How does it work?
如您所知,图像空间中的一条线可以用两个变量表示。 例如:
在Cartesian coordinate system笛卡尔坐标系中: 参数:(m,b)。
在Polar coordinate system极坐标系中: 参数:(r,θ)
对于霍夫变换,我们将在 Polar 系统中表达直线。 因此,直线方程可以写成:
Arranging the terms: r=xcosθ+ysinθ
1. 一般来说,对于每个点 (x0,y0),我们可以将通过该点的线族定义为
这意味着每一对 (rθ,θ) 表示经过 (x0,y0) 的每一条线
2. 如果对于给定的 (x0,y0) 我们绘制穿过它的线族,我们得到一个正弦曲线sinusoid。 例如,对于 x0=8 和 y0=6,我们得到以下图(在平面 θ - r 中):
我们只考虑 r>0 和 0<θ<2π 的点
3. 我们可以对图像中的所有点进行上述相同的操作。 如果两个不同点的曲线在平面 θ - r 中相交,这意味着两个点属于同一条直线。 例如,按照上面的示例并绘制另外两个点的图:x1=4,y1=9 和 x2=12,y2=3,我们得到:
这三个图在一个点 (0.925,9.6) 相交,这些坐标是参数 (θ,r) 或 (x0,y0)、(x1,y1) 和 (x2,y2) 所在的线。
4. 上面所有的东西是什么意思? 这意味着一般情况下,可以通过查找曲线之间的交点数来检测一条线。相交的曲线越多,意味着该交点所代表的线的点越多。 一般来说,我们可以定义检测一条线所需的最小交叉点数的阈值。
5. 这就是霍夫线变换的作用。 它跟踪图像中每个点的曲线之间的交点。 如果交叉点的数量高于某个阈值,则将其声明为具有交叉点参数 (θ,rθ) 的线。
Standard and Probabilistic Hough Line Transform
标准和概率霍夫线变换
OpenCV 实现了两种霍夫线变换:
a. The Standard Hough Transform
它几乎包含我们在上一节中刚刚解释的内容。 它为您提供了一个配对向量 (θ,rθ)
在 OpenCV 中,它是用函数 HoughLines() 实现的
b. The Probabilistic Hough Line Transform
霍夫线变换的更有效实现。 它将检测到的线的极值作为输出 (x0,y0,x1,y1)
在 OpenCV 中,它是用函数 HoughLinesP() 实现的
A more efficient implementation of the Hough Line Transform. It gives as output the extremes of the detected lines (x0,y0,x1,y1)
What does this program do?
加载图像
应用标准霍夫线变换和概率线变换。
在三个窗口中显示原始图像和检测到的线。
Code
我们将解释的示例代码可以从这里raw.githubusercontent.com下载。 可以在此处raw.githubusercontent.com找到一个稍微花哨的版本(显示 Hough 标准和带有用于更改阈值的滑动条的概率)。
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
// Declare the output variables
Mat dst, cdst, cdstP;
const char* default_file = "sudoku.png";
const char* filename = argc >=2 ? argv[1] : default_file;
// Loads an image
Mat src = imread( samples::findFile( filename ), IMREAD_GRAYSCALE );
// Check if image is loaded fine
if(src.empty()){
printf(" Error opening image\n");
printf(" Program Arguments: [image_name -- default %s] \n", default_file);
return -1;
}
// Edge detection
Canny(src, dst, 50, 200, 3);
// Copy edges to the images that will display the results in BGR
cvtColor(dst, cdst, COLOR_GRAY2BGR);
cdstP = cdst.clone();
// Standard Hough Line Transform
vector<Vec2f> lines; // will hold the results of the detection
HoughLines(dst, lines, 1, CV_PI/180, 150, 0, 0 ); // runs the actual detection
// Draw the lines
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( cdst, pt1, pt2, Scalar(0,0,255), 3, LINE_AA);
}
// Probabilistic Line Transform
vector<Vec4i> linesP; // will hold the results of the detection
HoughLinesP(dst, linesP, 1, CV_PI/180, 50, 50, 10 ); // runs the actual detection
// Draw the lines
for( size_t i = 0; i < linesP.size(); i++ )
{
Vec4i l = linesP[i];
line( cdstP, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
}
// Show results
imshow("Source", src);
imshow("Detected Lines (in red) - Standard Hough Line Transform", cdst);
imshow("Detected Lines (in red) - Probabilistic Line Transform", cdstP);
// Wait and Exit
waitKey();
return 0;
}
Explanation
Load an image:
const char* default_file = "sudoku.png";
const char* filename = argc >=2 ? argv[1] : default_file;
// Loads an image
Mat src = imread( samples::findFile( filename ), IMREAD_GRAYSCALE );
// Check if image is loaded fine
if(src.empty()){
printf(" Error opening image\n");
printf(" Program Arguments: [image_name -- default %s] \n", default_file);
return -1;
}
Detect the edges of the image by using a Canny detector:
// Edge detection
Canny(src, dst, 50, 200, 3);
现在我们将应用霍夫线变换。 我们将解释如何使用这两个可用的 OpenCV 函数。
Standard Hough Line Transform:
首先,您应用变换:
// Standard Hough Line Transform
vector<Vec2f> lines; //将保存检测结果
HoughLines(dst, lines, 1, CV_PI/180, 150, 0, 0 ); // runs the actual detection
具有以下参数:
dst:边缘检测器的输出。 应该是灰度图(虽然实际上是二值图)
lines:将存储检测到的线的参数(r,θ)的向量
rho :参数 r 的分辨率(以像素为单位)。 我们使用 1 个像素。
theta:以弧度为单位的参数 θ 的分辨率。 我们使用 1 度 (CV_PI/180)
threshold:“*detect*”一条线的最小交叉点数
srn 和 stn:默认参数为零。 查看 OpenCV 参考以获取更多信息。
然后通过绘制线条来显示结果。
// Draw the lines
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);//直线向量的法向量,过原点
//直线的向量应该为 (–b,a)
double x0 = a*rho, y0 = b*rho;//计算原点在直线上垂直点的坐标
pt1.x = cvRound(x0 + 1000*(-b));//垂足向一个方向沿伸1000
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));//垂足向另一个方向沿伸1000
pt2.y = cvRound(y0 - 1000*(a));
line( cdst, pt1, pt2, Scalar(0,0,255), 3, LINE_AA);//绘制直线
Probabilistic Hough Line Transform
概率霍夫线变换
首先应用转换:
// Probabilistic Line Transform
vector<Vec4i> linesP; // will hold the results of the detection
HoughLinesP(dst, linesP, 1, CV_PI/180, 50, 50, 10 ); // runs the actual detection
参数:
dst:边缘检测器的输出。 应该是灰度图(虽然实际上是二值图)
lines:将存储检测到的line的参数(xstart,ystart,xend,yend)的向量
rho :参数 r 的分辨率(以像素为单位)。 我们使用 1 个像素。
theta:以弧度为单位的参数 θ 的分辨率。 我们使用 1 度 (CV_PI/180)
threshold:“*detect*”一条线的最小交叉点数
minLineLength:可以形成一条线的最小点数。 点数少于此数量的线将被忽略。
maxLineGap:在同一条线上要考虑的两点之间的最大间隙。
然后通过绘制线条来显示结果。
// Draw the lines
for( size_t i = 0; i < linesP.size(); i++ )
{
Vec4i l = linesP[i];
line( cdstP, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
}
Display the original image and the detected lines:
// Show results
imshow("Source", src);
imshow("Detected Lines (in red) - Standard Hough Line Transform", cdst);
imshow("Detected Lines (in red) - Probabilistic Line Transform", cdstP);
Wait until the user exits the program
// Wait and Exit
waitKey();
return 0;
Result
下面的结果是使用我们在代码部分提到的稍微花哨的版本获得的。 它仍然实现了与上面相同的东西,只是为阈值添加了 Trackbar。
使用输入图像,例如数独图像。 我们使用标准霍夫线变换得到以下结果:
通过使用概率霍夫线变换:
您可能会观察到在更改阈值时检测到的line数会发生变化。 解释很明显:如果您建立更高的阈值,将检测到更少的线(因为您需要更多的点来声明检测到的线)。