为了更好地看懂本文,大家可以先看下我写的另一篇博文,链接如下:
https://blog.csdn.net/wenhao_ir/article/details/51774444
OpenCV的霍夫变换线检测函数HoughLines()是利用极坐标下的参数ρ和θ值来表示直线的,我们在实际应用中往往要根据直线的参数ρ和θ值来绘制出直线,那么怎么绘制呢?
在OpenCV中绘制直线的函数为函数line(),它的原型如下:
void cv::line ( InputOutputArray img,
Point pt1,
Point pt2,
const Scalar & color,
int thickness = 1,
int lineType = LINE_8,
int shift = 0
)
从这个原型中,我们可以看出,如果要使用这个函数绘制线条,那么需要知道线条的两个端点,即参数中的pt1和pt2,所以问题就转化为根据参数ρ和θ值计算要绘制直线的两个端点。
在进行计算前,我们先要搞清楚利用函数HoughLines()得到的θ到底是哪个角的度数。
当直线为水平或垂直时θ的值分别为π/2(即90度)和0(即0度),这个在官方文档中已经说得很清楚了。如下面的截图所示:
至于其它的情况,我们用两幅图来试验证一下就知道了。
情况一的图片如下:
情况二的图片如下:
检测图中两条直线的代码如下:
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
//OpenCV版本:3.0
//VS版本:2012
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
#define HOUGH_VOTE 100
int main()
{
cv::Mat srcImg1 = cv::imread("F:/material/images/P0041-HoughLines-angle-01.bmp", 0);//读取原图像并同时转换为灰度图
if (!srcImg1.data)
{
cout<<"error load image"<<endl;
return -1;
}
vector<Vec2f> lines;
float pi180 = (float)CV_PI / 180;
HoughLines(srcImg1, lines, 1, pi180, HOUGH_VOTE, 0, 0);
int numLines = lines.size();
for (int l = 0; l<numLines; l++)
{
float theta = lines[l][1];
cout <<"检测到的第"<< l+1<<"条线的角度为"<<":" << theta* 180 / CV_PI<< endl;
}
waitKey(0);
return 0;
}
两幅图的检测结果如下:
这里说明一下为什么检测到多条直线,这是因为我们的直线是有宽度的,所以检测到了多条。
从这个运行结果我们可以看出两种情况下哪个角是θ。如下面两幅图所示:
情况二:
知道了哪个角是θ,我们再把其它已知量和待求点及一些辅助线标注在图上,如下面两幅图所示:
图中的r就是
由以上几何关系并结合三角函数的和差公式:
不难得到在两种情况下都有pt1的坐标为:
p t 1. x = x 0 − L s i n ( θ ) = x 0 + ( − L s i n ( θ ) ) pt1.x = x0-Lsin(θ)=x0+(-Lsin(θ)) pt1.x=x0−Lsin(θ)=x0+(−Lsin(θ))
p t 1. y = y 0 + L c o s ( θ ) pt1.y = y0+Lcos(θ) pt1.y=y0+Lcos(θ)
两种情况下都有pt2的坐标为:
p t 2. x = x 0 + L s i n ( θ ) = x 0 − ( − L s i n ( θ ) ) pt2.x = x0+Lsin(θ)=x0-(-Lsin(θ)) pt2.x=x0+Lsin(θ)=x0−(−Lsin(θ))
p t 2. y = y 0 − L c o s ( θ ) pt2.y = y0-Lcos(θ) pt2.y=y0−Lcos(θ)
其中
x 0 = ρ c o s ( θ ) x0=ρcos(θ) x0=ρcos(θ)
y 0 = ρ s i n ( θ ) y0=ρsin(θ) y0=ρsin(θ)
有了以上计算公式后,我们便可以根据得到的ρ和θ值计算出pt1和pt2的坐标,进而绘制出直线。
相关代码如下:
HoughLines(magImg, lines, 1, pi180, HOUGH_VOTE, 0, 0);
int numLines = lines.size();
int L = 1000;
for (int l = 0; l<numLines; l++)
{
float rho = lines[l][0], theta = lines[l][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + L * (-b));
pt1.y = cvRound(y0 + L * (a));
pt2.x = cvRound(x0 - L * (-b));
pt2.y = cvRound(y0 - L * (a));
line(linImg, pt1, pt2, Scalar(255, 0, 0), 3, 8, 0);
}
很明显,上面的代码中,L值是需要我们根据图像的大小和直线的大小来确定,并不是每一幅图像或每一条直线都取1000。L是什么的长度,博主在上面的手绘图中应该是已经标注得很清楚了。
最后,对这篇博文有不明白的可以加博主的微信/QQ 2487872782交流。