1、简介
霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体。Hough变换是图像处理中从图像中识别几何形状的基本方法之一。Hough变换的基本原理在于利用点与线的对偶性,将原始图像空间的给定的曲线通过曲线表达形式变为参数空间的一个点。这样就把原始图像中给定曲线的检测问题转化为寻找参数空间中的峰值问题。也即把检测整体特性转化为检测局部特性。比如直线、椭圆、圆、弧线等。
2、霍夫直线变换(Hough Lines Transform)
2.1、基本介绍
霍夫线变换是一种在图像中寻找直线的方法。OpenCV中支持三种霍夫线变换,分别是标准霍夫线变换、多尺度霍夫线变换、累计概率霍夫线变换。
在OpenCV中可以调用函数HoughLines来调用标准霍夫线变换和多尺度霍夫线变换。HoughLinesP函数用于调用累积概率霍夫线变换。
我们都知道,二维坐标轴上表示一条直线的方程式y = a*x + b,我们想求出一条直线就得想方设法求出其中的a和b的值。如果用极坐标来表示就是:
theta就是直线与水平线所成的角度,而rho就是圆的半径(也可以理解为原点到直线的距离),同样地,这两个参数也是表征一条直线的重要参数,确定他们俩了,也就确定一条直线了。正如下图所示:
2.2、霍夫变换检测直线具体步骤
- 彩色图像->灰度图
- 去噪(高斯核)
- 边缘提取(梯度算子、拉普拉斯算子、canny、sobel)
- 二值化(判断此处是否为边缘点,就看灰度值==255)
- .映射到霍夫空间(准备两个容器,一个用来展示hough-space概况,一个数组hough-space用来储存voting的值,因为投票过程往往有某个极大值超过阈值,多达几千,不能直接用灰度图来记录投票信息)
- 取局部极大值,设定阈值,过滤干扰直线
- 绘制直线、标定角点
2.3、示例与应用
2.3.1、HoughLines:标准霍夫变换、多尺度霍夫变换
void HoughLines( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double srn = 0, double stn = 0,
double min_theta = 0, double max_theta = CV_PI );
参数说明:
InputArray image:输入图像,必须是8位单通道图像,推荐使用canny边缘检测的结果图像。
OutputArray lines:检测到的线条参数集合。
double rho:以像素为单位的距离步长,推荐用1.0 。
double theta:以弧度为单位的角度步长,推荐用CV_PI/180。
int threshold:累加计数值的阈值参数,当参数空间某个交点的累加计数的值超过该阈值,则认为该交点对应了图像空间的一条直线。
double srn:默认值为0,用于在多尺度霍夫变换中作为参数rho的除数,rho=rho/srn。
double stn:默认值为0,用于在多尺度霍夫变换中作为参数theta的除数,theta=theta/stn。
如果srn和stn同时为0,就表示HoughLines函数执行标准霍夫变换,否则就是执行多尺度霍夫变换。
说明:HoughLines函数输出检测到直线的矢量(lines)表示集合,每一条直线由具有两个元素的矢量(ρ, θ)表示,其中ρ表示直线距离原点(0, 0)的长度,θ表示直线的角度(以弧度为单位)。
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat img = imread("D:/test01.jpg");
if (img.empty()) {
cout << "could not find src1" << endl;
return -1;
}
imshow("原图", img);
Mat gray, dst;
//灰度化
cvtColor(img, gray, CV_BGR2GRAY);
//二值化
threshold(gray, gray, 100, 255, THRESH_BINARY);
//边缘检测
Canny(gray, gray, 50, 200, 3);
//灰度转bgr
cvtColor(gray, dst, CV_GRAY2BGR);
vector<Vec2f> lines;
HoughLines(gray, lines, 1, CV_PI/180, 100, 0, 0);
for (size_t i = 0; i < lines.size(); i++) {
float rho = lines[i][0];
float 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(dst, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA);
}
imshow("霍夫直线检测后的图", dst);
waitKey();
return 0;
}
2.3.2、HoughLinesP:渐进概率式霍夫变换
void HoughLinesP( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double minLineLength = 0, double maxLineGap = 0 );
参数解释:
InputArray image:输入图像,必须是8位单通道图像。
OutputArray lines:检测到的线条参数集合,是一个vector<Vec4i>,Vec4i是一个包含4个int数据类型的结构体,[x1,y1,x2,y2],可以表示一个线段。
double rho:直线搜索时的距离步长,以像素为单位, 推荐用1.0 。
double theta:直线搜索时的角度步长,以弧度为单位, 推荐CV_PI/180。
int threshold:累加计数值的阈值参数,当参数空间某个交点的累加计数的值超过该阈值,则认为该交点对应了图像空间的一条直线。
double minLineLength:默认值为0,表示最小线段长度阈值(像素)。
double maxLineGap:线段上最近两点之间的阈值.默认值为0,表示直线断裂的最大间隔距离阈值。即如果有两条线段是在一条直线上,但它们之间有间隙,那么如果这个间隔距离小于该值,则被认为是一条线段,否则认为是两条线段。
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat img = imread("D:/test01.jpg");
if (img.empty()) {
cout << "could not find src1" << endl;
return -1;
}
imshow("原图", img);
Mat gray, dst;
//灰度化
cvtColor(img, gray, CV_BGR2GRAY);
//二值化
threshold(gray, gray, 100, 255, THRESH_BINARY);
//边缘检测
Canny(gray, gray, 20, 200, 3);
//灰度转bgr
cvtColor(gray, dst, CV_GRAY2BGR);
vector<Vec4f> lines;
HoughLinesP(gray, lines, 1, CV_PI/180, 20, 0, 0);
for (size_t i = 0; i < lines.size(); i++) {
Vec4f pline = lines[i];
Point pt1, pt2;
pt1 = Point(pline[0], pline[1]);
pt2 = Point(pline[2], pline[3]);
line(dst, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA);
}
imshow("霍夫直线检测后的图", dst);
waitKey();
return 0;
}
3、霍夫圆变换(Hough Circles Transform)
3.1、基本介绍
霍夫圆变换的基本思路是认为图像上每一个非零像素点都有可能是一个潜在的圆上的一点,跟霍夫线变换一样,也是通过投票,生成累积坐标平面,设置一个累积权重来定位圆。
3.2、应用示例
void HoughCircles( InputArray image, OutputArray circles,
int method, double dp, double minDist,
double param1=100, double param2=100,
int minRadius=0, int maxRadius=0 )
参数解释:
第一个参数image是输入图像矩阵,要求是灰度图像;
第二个参数 circles是一个包含检测到的圆的信息的向量,向量内第一个元素是圆的横坐标,第二个是纵坐标,第三个是半径大小;
第三个参数 methodmethod是所使用的圆检测算法,目前只有CV_HOUGH_GRADIENT一个可选;
第四个参数 dp是累加面与原始图像相比的分辨率的反比参数,dp=2时累计面分辨率是元素图像的一半,宽高都缩减为原来的一半,dp=1时,两者相同。(关于这个分辨率的概念没有理解透,按道理低分辨率应该意味着更快的检测速度,然而实测恰恰相反)
第五个参数 minDist定义了两个圆心之间的最小距离;
第六个参数param1是Canny边缘检测的高阈值,低阈值被自动置为高阈值的一半;
第七个参数param2是累加平面对是否是圆的判定阈值;
第八和第九个参数定义了检测到的圆的半径的最大值和最小值;
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat img = imread("D:/test02.jpg");
if (img.empty()) {
cout << "could not find src1" << endl;
return -1;
}
imshow("原图", img);
Mat gray, dst;
//灰度化
cvtColor(img, gray, CV_BGR2GRAY);
//灰度转bgr
cvtColor(gray, dst, CV_GRAY2BGR);
vector<Vec3f>circles;
HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 15, 100, 155);
for (size_t i = 0; i < circles.size(); i++) {
Point circleCenter(circles[i][0], circles[i][1]);
int radius = circles[i][2];
circle(dst, circleCenter, radius, Scalar(0, 0, 255), 3); //做圆
}
imshow("霍夫圆检测后的图", dst);
waitKey();
return 0;
}
4、参考
https://www.cnblogs.com/skyfsm/p/6881686.html
https://blog.csdn.net/leonardohaig/article/details/87907462
https://blog.csdn.net/qq_39861376/article/details/82119472
https://blog.csdn.net/weixin_38416696/article/details/90755605