绪:
如何检测和识别图像中的直线和圆?
一个非常有效的方法就是霍夫变换;
是图像中识别各种几何形状的基本算法之一;
本文介绍一下opencv中的霍夫线变换和霍夫圆变换的应用。

方法/步骤
-
1
霍夫线变换:
霍夫线变换是一种在图像中寻找直线的方法;
OpenCV中支持三种霍夫线变换,分别为标准霍夫线变换、多尺度霍夫线变换、累计概率霍夫线变换。
在OpenCV中可以调用HoughLines来进行标准霍夫线变换和多尺度霍夫线变换;
调用HoughLinesP函数进行累积概率霍夫线变换。
-
2
霍夫直线思想:
我们都知道,
二维坐标轴上表示一条直线的方程式y = a*x + b,
我们想求出一条直线就得想方设法求出其中的a和b的值。
如果用极坐标来表示就是:rho=xcos(theta)+ysin(theta);
其中,
theta就是直线与水平线所成的角度,
而rho就是圆的半径;
同样地,这两个参数也是表征一条直线的重要参数,
确定他们俩了,也就确定一条直线了。
【注】:
OpenCV里,只需调用HoughLines就可得到表征一条直线的这两个参数值!
-
3
霍夫直线检测示例一:HoughLines
#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat srcImage = imread("0.jpg");
imshow("srcImg", srcImage);
//边缘检测
Mat midImage, dstImage;
Canny(srcImage, midImage, 50, 200, 3);
cvtColor(midImage, dstImage, CV_GRAY2BGR);
//定义矢量结构存放检测出来的直线
vector<Vec2f> lines;
HoughLines(midImage, lines, 1, CV_PI / 180, 150, 0, 0);
//第五个参数表阈值,阈值越大检测越精准速度越快直线越少
//lines是包含rho和theta的,而不包括直线上的点,
//所以下面需要根据得到的rho和theta来建立一条直线
//依次画出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0]; //就是圆的半径r
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, CV_AA);
imshow("边缘检测后的图", midImage);
imshow("最终效果图", dstImage);
}
waitKey();
return 0;
}
【问题】:
你会发现,怎么图中一些很明显的的直线都没检测出来啊?
原因是,我们阈值写的有点高了,只有那些有足够的把握认为是直线的直线才可能检测出来。
如果把阈值改为150,直线检测效果就变成这样子了。
显然多了很多直线,把那些“可能是直线”的直线都当做是直线了。
所以,阈值的选择很重要,就看你是要精确查找还是模糊查找了。
【注】:角度theta用的单位不是我们所说的度数(70度、80度),而是数学上的π/2,π/3。
-
4
霍夫直线检测示例二:HoughLinesP
//与HoughLines不同的是,
//HoughLinesP得到lines的是含有直线上点的坐标的,
//所以下面进行划线时就不再需要自己求出两个点来确定唯一的直线了
#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat srcImage = imread("2.jpg");
imshow("Src Pic", srcImage);
Mat midImage, dstImage;
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, CV_AA);
imshow("边缘检测后的图", midImage);
imshow("最终效果图", dstImage);
}
waitKey();
return 0;
}
-
5
霍夫圆变换:
对直线来说,一条直线由参数极径极角(rho,theta)表示;
对圆来说,需要三个参数来表示一个圆;(center_x,center_y,r);其对应一条三维空间的曲线;
那么与二维的霍夫线变换同样的道理,
对于多个边缘点越多这些点对应的三维空间曲线交于一点,那么他们经过的共同圆上的点就越多,
类似的我们也就可以用同样的阈值的方法来判断一个圆是否被检测到, 这就是标准霍夫圆变换的原理,
但也正是在三维空间的计算量大大增加的原因, 标准霍夫圆变化很难被应用到实际中;
出于对运算效率的考虑, OpenCV实现的是一个比标准霍夫圆变换更为灵活的检测方法: 霍夫梯度法, 也叫2-1霍夫变换(21HT),
原理:依据是圆心一定是在圆上的每个点的模向量上, 这些圆上点模向量的交点就是圆心, 霍夫梯度法的第一步就是找到这些圆心, 这样三维的累加平面就又转化为二维累加平面.;
第二步根据所有候选中心的边缘非0像素对其的支持程度来确定半径.;
-
6
霍夫圆变换示例:
#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat srcImage = imread("test5.jpg");
Mat midImage, dstImage;
imshow("srcImg", srcImage);
cvtColor(srcImage, midImage, CV_BGR2GRAY);
GaussianBlur(midImage, midImage, Size(9, 9), 2, 2);
//霍夫圆变换
vector<Vec3f> circles;
HoughCircles(midImage, circles, CV_HOUGH_GRADIENT, 1.5, 10, 200, 150, 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(srcImage, center, 3, Scalar(0, 255, 0), -1, 8, 0);
//绘制圆轮廓
circle(srcImage, center, radius, Scalar(155, 50, 255), 3, 8, 0);
}
imshow("【效果图】", srcImage);
waitKey(0);
return 0;
}
END
文章评论