阅读(712)
赞(0)
OpenCV Hough Line变换
2017-09-12 09:46:03 更新
目标
在本教程中,您将学习如何:
- 使用OpenCV函数cv :: HoughLines和cv :: HoughLinesP来检测图像中的行。
理论
- 注意
- 下面的解释属于Bradski和Kaehler 的“ 学习OpenCV ”一书。
Hough Line变换
- Hough Line变换是用于检测直线的变换。
- 为了应用变换,首先需要边缘检测预处理。
它是如何工作的?
- 如你所知,图像空间中的一行可以用两个变量表示。例如:在笛卡尔坐标系中:参数:(m,b);在极坐标系中:参数:(r,θ)
对于Hough 变换,我们将在极地系统中表达线条。因此,线性方程可以写为:
排列术语:r=xcosθ+ysinθ
- 通常,对于每个点(x0,y0),我们可以定义通过该点的行给出如下公式:
意思是每一对的 (rθ,θ)表示通过每一排的 (x0,y0)
- 如果对于给定的 (x0,y0)我们绘制通过它的线族,我们得到一个正弦曲线,例如,对于x0=8和y0=6 ,我们得到以下图(在平面 θ - r)
我们只需考虑这样的情况: r>0 和 0<θ<2π.
- 我们可以对图像中的所有点执行上述相同的操作。如果两个不同点的曲线在平面θ - r相交,那意味着两个点都属于同一行。例如,按照上面的例子,并绘制两个点的x1=4, y1=9 和 x2=12, y2=3,,我们得到:
三个曲线在一个点(0.925,9.6)相交,这些坐标是参数 ( θ,r) 或其中(x0,y0), (x1,y1) 和 (x2,y2) lay.
- 上面所有的东西是什么意思?这意味着一般来说,通过找到曲线之间的交点数可以检测到一条线。更多的相交曲线意味着由该交点表示的线具有更多的点。通常,我们可以定义检测线路所需的最小交点数的阈值。
- 这就是Hough Line变换所做的。它跟踪图像中每个点的曲线之间的交点。如果交叉点的数量高于某个阈值,则它将其声明为具有交点的参数(θ,rθ)的行。
标准和概率Hough Line变换
OpenCV实现了两种Hough线变换:
一个。标准Hough变换
- 它几乎包含在上一节中我们刚刚解释的内容。它给你一个向量的结果(θ,rθ)
- 在OpenCV中,它使用函数cv :: HoughLines实现
湾 概率霍夫线变换
- 更有效地实现Hough Line变换。它给出检测出的行的极值(x0,y0,x1,y1)
- 在OpenCV中,它使用函数cv :: HoughLinesP来实现
Code
- 这个程序是做什么的?加载图像应用标准Hough Line变换或概率线变换。在两个窗口中显示原始图像和检测到的线。
- 我们将从这里下载我们将要解释的示例代码。这里可以找到一个稍微优雅的版本(其中显示了Hough 标准和带有轨迹的概率更改阈值)。
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
static void help()
{
cout << "\nThis program demonstrates line finding with the Hough transform.\n"
"Usage:\n"
"./houghlines <image_name>, Default is ../data/pic1.png\n" << endl;
}
int main(int argc, char** argv)
{
cv::CommandLineParser parser(argc, argv,
"{help h||}{@image|../data/pic1.png|}"
);
if (parser.has("help"))
{
help();
return 0;
}
string filename = parser.get<string>("@image");
if (filename.empty())
{
help();
cout << "no image_name provided" << endl;
return -1;
}
Mat src = imread(filename, 0);
if(src.empty())
{
help();
cout << "can not open " << filename << endl;
return -1;
}
Mat dst, cdst;
Canny(src, dst, 50, 200, 3);
cvtColor(dst, cdst, COLOR_GRAY2BGR);
#if 0
vector<Vec2f> lines;
HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );
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, CV_AA);
}
#else
vector<Vec4i> lines;
HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 );
for( size_t i = 0; i < lines.size(); i++ )
{
Vec4i l = lines[i];
line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
}
#endif
imshow("source", src);
imshow("detected lines", cdst);
waitKey();
return 0;
}
说明
- 加载图像
Mat src = imread(filename, 0);
if(src.empty())
{
help();
cout << "can not open " << filename << endl;
return -1;
}
- 使用Canny检测器检测图像的边缘
Canny(src,dst,50,200,3);
现在我们将应用Hough Line变换。我们将解释如何使用可用于此目的的两个OpenCV功能:
- 标准霍夫线变换
首先,你应用变换:
vector<Vec2f> lines;
HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );
具有以下参数:
- dst:边缘检测器的输出。它应该是一个灰度图像(尽管事实上它是二进制的)
- 行:一个向量,将存储检测到的行的参数(r,θ)
- rho:参数的分辨率(以像素为单位)。我们使用1像素。r
- theta:以弧度表示的参数的分辨率。我们使用1度(CV_PI / 180)θ
- threshold:将“* detect *”一行的最小交点数
- srn和stn:默认参数为零。查看OpenCV参考了解更多信息
然后通过绘制线条显示结果。
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 Hough Line变换
首先你应用变换:
vector<Vec4i> lines;
HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 );
有论据:
- dst:边缘检测器的输出。它应该是一个灰度图像(尽管事实上它是二进制的)
- 行:将存储检测到的行的参数的(xstart,ystart,xend,yend)
- rho:参数的分辨率(以像素为单位)。我们使用1像素。r
- theta:以弧度表示的参数的分辨率。我们使用1度(CV_PI / 180)θ
- threshold:将“* detect *”一行的最小交点数
- minLinLength:可以形成一行的最小点数。小于这个点数的行被忽略。
- maxLineGap:在同一行中考虑的两点之间的最大差距。
然后通过绘制线条显示结果。
for( size_t i = 0; i < lines.size(); i++ )
{
Vec4i l = lines[i];
line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
}
- 显示原始图像和检测到的线条:
imshow("source", src);
imshow("detected lines", cdst);
- 等到用户退出程序
waitKey();
结果
- 注意
- 以下结果是使用我们在“ 代码”部分提到的稍微优雅的版本获得的。它仍然实现与上述相同的内容,只添加Threshold的Trackbar。
使用输入图像,如:
我们通过使用Probabilistic Hough Line变换得到以下结果:
您可能会发现在更改阈值时检测到的行数会有所不同。解释很明显:如果建立更高的Threshold,将检测到更少的行(因为您需要更多的点来声明检测到的行)。