1.概述
霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换的结果。
霍夫变换运用两个坐标空间之间的变换将一个空间中具有相同形状的曲线或直线映射到另一个坐标空间中的一个点上形成峰值。
2. Opencv中的霍夫线变换
霍夫线变换是一种来寻找直线的方法,在使用霍夫变换之前,首先对图像进行边缘检测的处理。霍夫线变换的直接输入只能是边缘二值图像。
Opencv中的霍夫线变换
- 标准霍夫变换(StandardHough Transform ,SHT)。由HougLines函数调用
- 多尺度霍夫变换(Multi-ScaleHough Transform ,MSHT),由HoughLines()调用
- 累计概论互霍夫变换(Progressive Probabilistic Hough Transform ,PPHT)由HoughLinesP函数调用,执行效率更高
3. 原理
3.1 二维直线
极坐标表示: y=(−cosθsinθ)x+rsinθ y = ( − c o s θ s i n θ ) x + r s i n θ
可以简化为: r=xcosθ+ysinθ r = x c o s θ + y s i n θ
3.2 点简化
对于点
(x0,y0)
(
x
0
,
y
0
)
可以简化为
每一对 (r,θ) ( r , θ ) 表示一条通过点 (x0,y0) ( x 0 , y 0 ) 的直线
3.3 交点
一般来说,一条直线能够通过在平面 θ−r θ − r 寻找交于一点的曲线数量来检测。越多的曲线交于一点意味着这个交点表示的直线由更多的点组成
3.4 阈值化
如果交一点的曲线数量超过阈值,那么可以认为这个交点所代表的参数对 (θ,r) ( θ , r ) 在原图像中为一条直线
4.标准霍夫变换:HoughLines()函数
void HoughLines(InputArray image, OutputArray lines,double rho, double theta,int threshold,double srn = 0, double stn = 0)
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//mian()函数
int main()
{
//载入原始图
Mat srcImage = imread("1.jpg");
Mat midImage, dstImage;
//canny检测边缘转化为灰度图
Canny(srcImage, midImage, 50, 200, 3);
cvtColor(midImage, dstImage, CV_GRAY2BGR);
//进行霍夫变换
vector<Vec2f>lines;
HoughLines(midImage, lines, 1, CV_PI / 180, 150, 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(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA);
}
//显示效果图
imshow("【原始图】", srcImage);
//边缘检测之后的图
imshow("【边缘检测后的图】", midImage);
//显示效果图
imshow("【效果图】", dstImage);
waitKey(0);
return 0;
}
5 累计概率霍夫变换:HoughLinesP()函数
此函数在HoughLines的基础上,在末尾加了一个代表Probabilistic概率的P,表示采用累计概率霍夫变换PPHT来找出二值图像中的直线
void HoughLinesP(InputArray image, OutputArray lines,double rho,double theta, int threshold, double minLineLength =0 , double maxLineGap = 0)
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//mian()函数
int main()
{
//载入原始图
Mat srcImage = imread("1.jpg");
Mat midImage, dstImage;
//canny检测边缘转化为灰度图
Canny(srcImage, midImage, 50, 200, 3);
cvtColor(midImage, dstImage, CV_GRAY2BGR);
//进行霍夫变换
vector<Vec4i>lines;
HoughLinesP(midImage, lines, 1, CV_PI / 180, 80, 50, 10);
//一次在图中绘制出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255), 1, LINE_AA);
}
//显示效果图
imshow("【原始图】", srcImage);
//边缘检测之后的图
imshow("【边缘检测后的图】", midImage);
//显示效果图
imshow("【效果图】", dstImage);
waitKey(0);
return 0;
}
6 霍夫圆变换
只要点对应的二维极径角空间被三维的圆心点 x,y x , y 和半径 r r 空间取代。表示一个圆
7 霍夫梯度法的原理
- 首先对图像应用边缘检测,比如用canny边缘检测
- 然后,对边缘图像中的每一个非零点,考虑其局部梯度,即用Sobel()函数计算x和y方向的sobel一阶导数得到了梯度
- 利用得到的梯度,用斜率指定的直线上的每一个点都在累加器被累加。这里的斜率是从一个指定的最小值到指定的最大值的距离
- 同时,标记边缘图像中非0像素的位置
- 然后从二维累加器中这些点选择候选的一个中心,这些中心都大于给定阈值并且大于其所有近邻,这些候选的中心按照累加值降序排列。
- 接下来对每一个中心,考虑所有的非0像素
- 这些像素按照其与中心的距离排序,从最大半径的最小距离算起,选择非0像素最支持的一条半径
- 如果一个中心收到边缘图像非0像素最充分的支持,并且到前期被选择的中心由足够的距离,那么就会被留下来
8. 霍夫梯度法的缺点
- 输出中产生一些噪声
- 如果由同心圆,就只能选择其中的一个
9. 霍夫圆变换:HoughCircles()函数
不需要源图是二值的
void HoughCircles(InputArray image,OutputArray circles,int method, int dp,int minDist,double param1 = 100,double param2 = 100,int minRadius = 0,int maxRadius = 0)
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//mian()函数
int main()
{
//载入原始图
Mat srcImage = imread("1.jpg");
Mat midImage, dstImage;
//转化为灰度图并平滑
cvtColor(srcImage, midImage, CV_BGR2GRAY);
GaussianBlur(midImage, midImage, Size(9, 9), 2, 2);
//进行霍夫圆变换
vector<Vec3f>circles;
HoughCircles(midImage, circles, HOUGH_GRADIENT, 1.5, 10, 200, 100, 0, 0);
//一次在图中绘制出每条线段
for (size_t i = 0; i < circles.size(); i++)
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
circle(dstImage, center, 3, Scalar(0, 255, 0), -1, 8, 0);
circle(dstImage, center, radius, Scalar(155, 50, 255), 3, 8, 0);
}
//显示效果图
imshow("【原始图】", srcImage);
//显示效果图
imshow("【效果图】", srcImage);
waitKey(0);
return 0;
}
10 综合示例
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//原始图,中间图和效果图
Mat g_srcImage, g_dstImage, g_midImage;
vector<Vec4i> g_lines;
int g_nthreshold = 100;
//全局函数声明
static void on_HoughLines(int, void *);
//main()函数
int main()
{
//改变console字体颜色
system("color 3F");
//载入原始图
g_srcImage = imread("1.jpg");
//显示原始图
imshow("【原始图】", g_srcImage);
//创建滚动条
namedWindow("【效果图】", 1);
createTrackbar("值:", "【效果图】", &g_nthreshold, 200, on_HoughLines);
//进行边缘检测和转化为灰度图
Canny(g_srcImage, g_midImage, 50, 200, 3);
cvtColor(g_midImage, g_dstImage, COLOR_GRAY2BGR);
//调用回调函数
on_HoughLines(g_nthreshold, 0);
HoughLinesP(g_midImage, g_lines, 1, CV_PI / 180, 80, 50, 10);
//显示效果图
imshow("【效果图】", g_dstImage);
waitKey(0);
return 0;
}
static void on_HoughLines(int, void *)
{
//定义局部变量
Mat dstImage = g_dstImage.clone();
Mat midImage = g_midImage.clone();
//调用HoughLinesP函数
vector<Vec4i> mylines;
HoughLinesP(midImage, mylines, 1, CV_PI / 180, g_nthreshold + 1, 50, 10);
//循环遍历绘制每一个线段
for (size_t i = 0; i < mylines.size(); i++)
{
Vec4i l = mylines[i];
line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(23, 180, 55), 1, LINE_AA);
}
//显示图像
imshow("【效果图】",dstImage);
}