OpenCV HoughLine()函数与HoughlinesP()函数都用于图像中的直线检测,但二者是有区别的。
HoughLine()函数
HoughLines()基于霍夫变换的原理,通过投票机制来确定图像中直线的存在及其参数。该函数返回检测到的直线的极坐标参数。它是一个 cv::Vec2f 类型元素组成的向量,每个元素是一对浮点数,表示检测到的直线的参数,即(ρ, θ)。ρ是原点到直线的距离,θ是原点到直线垂线与x轴的夹角。由于无法直接确定检测到直线的起始及终位置,只能通过直线上的两个点来画直线。将画出来的直线显示出来,就可看到直线检测结果。OpenCV HoughLine()函数的原型如下:
OpenCV HoughLine()函数参数:
image :待检测直线的原图像。该图像必须是CV_8U的单通道二值图像,即边缘检测后的图像。
lines :存储检测到的直线的参数对的容器。每一条直线都由两个参数表示,分别是直线距离坐标原点的距离rho和坐标原点到直线的垂线与x轴的夹角theta。
rho :以像素为单位的距离分辨率,即距离r离散化时的单位长度。这个参数决定了霍夫空间中r坐标轴的分辨率。
theta :以弧度为单位的角度分辨率,即夹角theta离散化时的单位角度。这个参数决定了霍夫空间中theta坐标轴的分辨率。
threshold :累加器的阈值。参数空间中离散化后的每个方格被通过的累计次数大于该阈值时,将被识别为直线,否则不被识别为直线。这个参数决定了检测直线的灵敏度。
srn = 0 (可选):对于多尺度霍夫变换算法,该参数表示距离分辨率的除数。粗略的累加器距离分辨率是第三个参数rho,精确的累加器分辨率是rho/srn。这个参数必须是非负数,默认参数是0。
stn = 0 (可选):对于多尺度霍夫变换算法,该参数表示角度分辨率的除数。粗略的累加器角度分辨率是第四个参数theta,精确的累加器分辨率是theta/stn。这个参数必须是非负数,默认参数为0。当这个参数与第六个参数srn同时为0时,此函数表示的是标准霍夫变换。
min_theta = 0 (可选):检测直线的最小角度,默认参数为0。
max_theta = CV_PI (可选):检测直线的最大角度,默认参数为CV_PI,即圆周率。这个参数限定了检测直线的角度范围。
示例:
在完成具体代码之前,先讨论一下如何画出检测到的直线。如上图,这里用P1、P2两点来画直线,P1、P2是直线上以垂足P为对称中心的两个点,为保证画的直线会落在被检测图像内,这里d取图像长、宽中的最大值。P点的坐标为:(ρ*cosθ, ρ*sinθ),P1的坐标为:(ρ*cosθ-d*sinθ,ρ*sinθ+d*cosθ),P2的坐标为(ρ*cosθ+d*sinθ,ρ*sinθ-d*cosθ)。有了两个点P1、P2的坐标就可以画出直线了。
新建一个控制台应用程序,在源程序中加入如下代码:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <math.h>
using namespace cv;
using namespace std;
void DrawLine(Mat& dst, vector<Vec2f> lines, Scalar scalar, int n);
int main()
{
Mat src = imread(".\\pic\\1.png");
if (src.empty())
{
cout << "Cann't Open Image!" << endl;
return -1;
}
imshow("Src", src);
Mat dst;
cvtColor(src, dst, COLOR_BGR2GRAY);
GaussianBlur(dst, dst, Size(9, 9), 0);
//imshow("Dst", dst);
Mat m_Edges;
Canny(dst, m_Edges, 75, 150);
imshow("Edges", m_Edges);
vector<Vec2f> lines;
HoughLines(m_Edges, lines, 1, CV_PI / 180, 100);
DrawLine(src, lines, Scalar(0, 0, 255), 1);
imshow("Result", src);
waitKey(0);
}
void DrawLine(Mat& dst, vector<Vec2f> lines, Scalar scalar, int n)
{
Point2f p, p1, p2;
float rho, theta;
int d = MAX(dst.rows, dst.cols);
int sizeofLines = lines.size();
for (size_t i = 0; i < sizeofLines; i++)
{
rho = lines[i][0];
theta = lines[i][1];
p.x = rho * cos(theta);
p.y = rho * sin(theta);
p1.x = p.x - d * sin(theta);
p2.x = p.x + d * sin(theta);
p1.y = p.y + d * cos(theta);
p2.y = p.y - d * cos(theta);
line(dst, p1, p2, scalar, n);
}
}
试运行, 结果如下:
HoughLinesP()函数
HoughLinesP()函数基于霍夫变换(Hough Transform)的原理,通过检测图像中的边缘来识别线段。它主要用于在二值图像中检测直线段,并返回这些线段的端点坐标。HoughLinesP()函数的原型如下:
HoughLinesP()函数参数 :
image :输入的8位单通道二值图像,通常是由边缘检测算法(如Canny算法)得到的图像。
lines :输出的直线向量Vector<Vec4i>,每条线用4个元素表示,即直线的两个端点的4个坐标值(x1, y1, x2, y2)。
rho :霍夫空间中坐标的分辨率,以像素为单位。它决定了霍夫空间中r坐标轴的分辨率。通常设置为1。
theta :角度的分辨率,以弧度为单位。它决定了霍夫空间中θ坐标轴的分辨率。通常设置为π/180,即1度。
threshold :判定直线点数量的阈值。累加器中的票数大于该阈值的线段才会被检测出来。阈值越高,检测到的线段越少。
minLineLength (可选):线段的最小长度。小于此值的线段将被拒绝。默认值为0,表示不进行此检查。
maxLineGap (可选):最大允许的线段间距,以使它们被视为单一线段。如果两条线段在同一直线上且它们之间的间隙小于此值,则它们将被视为一条线段。默认值为0,表示不进行此检查。
HoughLinesP()函数工作原理
1 边缘检测 :首先对图像进行边缘检测,以获取所有可能形成直线的像素点。边缘检测可以通过各种算法(如Canny算法)来实现。边缘检测并不是在HoughLinesP()函数的内部完成,而是将边缘检测的输出结果作为HoughLinesP()函数的输入源。
2 霍夫变换 :然后对这些边缘像素点进行霍夫变换。霍夫变换的基本思想是将每个边缘像素点的梯度和角度映射到霍夫空间中。在霍夫空间中,每个可能的直线参数(斜率和截距,但在这里用极坐标ρ和θ表示)对应一个投票桶。如果一个边缘像素点对应的梯度和角度落在两个投票桶的边界上,则该像素点会给两个投票桶各投一票。
3 检测线段 :最后,通过查找投票数最多的投票桶来确定检测到的线段。根据设置的阈值、最小线段长度和最大线段间距等参数,筛选出符合条件的线段。
HoughLinesP()函数示例:
屏蔽掉HoughLines的部分代码,加入新代码如下:
//HoughLinesP
Mat src = imread(".\\pic\\1.png");
if (src.empty())
{
cout << "Cann't Open Image!" << endl;
return -1;
}
imshow("Src", src);
Mat dst;
cvtColor(src, dst, COLOR_BGR2GRAY);
GaussianBlur(dst, dst, Size(9, 9), 0);
//imshow("Dst", dst);
Mat m_Edges;
Canny(dst, m_Edges, 75, 150);
//imshow("Edges", m_Edges);
vector<Vec4i> lines;
HoughLinesP(m_Edges, lines, 1, CV_PI / 180, 60);
int sizeofLines = lines.size();
for (size_t i = 0; i < sizeofLines; i++)
{
line(src,Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]),Scalar(0,0,255),2);
}
imshow("Result", src);
waitKey(0);
}
试运行结果如下:
HoughCircles()函数
HoughCircles()函数用于在灰度图中检测圆。其原型如下:
**HoughCircles()**函数参数:
image 输入图像,即源图像,应为8位单通道图像。如果使用彩色图像,需要先转换成灰度图像。
circles 用来存储HoughCircles的结果,类型为list。list中对象格式为(x, y, r),分别表示圆心的x坐标、y坐标和圆的半径。
method 定义检测图像中圆的方法。目前唯一实现的方法是cv::HOUGH_GRADIENT。
dp 图像像素分辨率与参数空间分辨率的比值(官方文档上写的是图像分辨率与累加器分辨率的比值)。dp=1时,参数空间与图像像素空间(分辨率)一样大;dp=2时,参数空间的分辨率只有像素空间的一半大。通过设置dp可以减少计算量。
minDist 检测到的圆中心(x, y)坐标之间的最小距离。如果minDist太小,会保留大部分圆心相近的圆;如果minDist太大,则可能会将圆心相近的圆合并(若两圆心距离小于minDist,则认为是同一个圆)。
param1 Canny边缘检测的高阈值,低阈值被自动置为高阈值的一半,默认为100。
param2 累加平面某点是否是圆心的判定阈值。只有当累加值大于该阈值时,才判断为圆。param2值设置得越小,检测到的圆越多;设置得越大,检测到的圆越接近完美的圆形。
minRadius 半径的最小大小(以像素为单位),默认为0。
maxRadius 半径的最大大小(以像素为单位),默认为0。如果maxRadius小于等于0,则使用最大图像尺寸。如果maxRadius小于0,HOUGH_GRADIENT返回中心而不查找半径,此时可以使用其他过程来找到正确的半径。
HoughCircles()函数示例:
屏蔽掉上面程序的部分代码,加入新代码,如下:
//HoughCircles
Mat src = imread(".\\pic\\2.png");
if (src.empty())
{
cout << "Cann't Open Image!" << endl;
return -1;
}
imshow("Src", src);
Mat dst;
cvtColor(src, dst, COLOR_BGR2GRAY);
GaussianBlur(dst, dst, Size(7, 7), 0);
vector<Vec3f> circles;
HoughCircles(dst, circles, HOUGH_GRADIENT, 1, 5,100);
int sizeofLines = circles.size();
for (size_t i = 0; i < sizeofLines; i++)
{
circle(src, Point(circles[i][0], circles[i][1]),circles[i][2], Scalar(0, 0, 255), 2);
}
imshow("Result", src);
waitKey(0);
}
试运行,结果如下:
本博文到此结束,本博文示例程序的源代码已上传到CSDN。下载链接为:https://mp.csdn.net/mp_download/manage/download/UpDetailed