OpenCV4入门系列教程61:霍夫变换

索引地址:系列教程索引地址

上一篇:OpenCV4入门系列教程60:边缘检测

霍夫变换

霍夫变换是一种特征检测(feature extraction),被广泛应用在图像分析(image analysis)、计算机视觉(computer vision)以及数位影像处理(digital image processing)。霍夫变换是用来辨别找出物件中的特征,例如:线条。他的算法流程大致如下,给定一个物件、要辨别的形状的种类,算法会在参数空间(parameter space)中执行投票来决定物体的形状,而这是由累加空间(accumulator space)里的局部最大值(local maximum)来决定。

现在广泛使用的霍夫变换是由RichardDuda和PeterHart在公元1972年发明,并称之为广义霍夫变换(generalizedHoughtransform),广义霍夫变换和更早前1962年的PaulHough的专利有关。经典的霍夫变换是侦测图片中的直线,之后,霍夫变换不仅能识别直线,也能够识别任何形状,常见的有圆形、椭圆形。1981年,因为DanaH.Ballard的一篇期刊论文"Generalizing the Hough transform to detect arbitrary shapes",让霍夫变换开始流行于计算机视觉界。

直线检测

函数原型:

1
2
3
4
5
6
7
8
9
10
void cv::HoughLines     (   InputArray      image, //源图像
OutputArray lines,//输出对象
double rho,//以像素为单位的累加器的距离分辨率。
double theta,//以弧度表示的累加器的角度分辨率
int threshold,//蓄能器阈值参数。 只返回那些获得足够选票的行(>texttt{阈值})。
double srn = 0,//对于多尺度Hough变换,它是距离分辨率Rho的除数。粗累加器距离分辨率为rho,精确累加器分辨率为rho/srn。如果srn=0和stn都=0,则使用经典的Hough变换。否则,这两个参数都应该是正的。
double stn = 0,//对于多尺度Hough变换,它是距离分辨率θ的除数。
double min_theta = 0,//对于标准和多尺度霍夫变换,最小角度检查线路。 必须在0到max_theta之间。
double max_theta = CV_PI //对于标准和多尺度霍夫变换,最大角度检查线路。 必须落在min_theta和CV_PI之间。
) ;

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//--------------------------------------【程序说明】-------------------------------------------
// 程序说明:《OpenCV3编程入门》OpenCV3版书本配套示例程序61
// 程序描述:HoughLines函数用法示例
// 2014年11月 Created by @浅墨_毛星云
// 2014年12月 Revised by @浅墨_毛星云
//------------------------------------------------------------------------------------------------

//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main()
{
//【1】载入原始图和Mat变量定义
Mat srcImage = imread("lena.jpg"); //工程目录下应该有一张名为1.jpg的素材图
Mat midImage, dstImage; //临时变量和目标图的定义

//【2】进行边缘检测和转化为灰度图
Canny(srcImage, midImage, 50, 200, 3); //进行一此canny边缘检测
cvtColor(midImage, dstImage, COLOR_GRAY2BGR); //转化边缘检测后的图为灰度图

//【3】进行霍夫线变换
vector<Vec2f> lines; //定义一个矢量结构lines用于存放得到的线段矢量集合
HoughLines(midImage, lines, 1, CV_PI / 180, 150, 0, 0);

//【4】依次在图中绘制出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0], theta = lines[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a * rho, y0 = b * rho;
pt1.x = round(x0 + 1000 * (-b));
pt1.y = round(y0 + 1000 * (a));
pt2.x = round(x0 - 1000 * (-b));
pt2.y = round(y0 - 1000 * (a));
line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA);
}

//【5】显示原始图
imshow("Original", srcImage);

//【6】边缘检测后的图
imshow("Canny result", midImage);

//【7】显示效果图
imshow("Result", dstImage);

waitKey(0);

return 0;
}

测试效果:

lines

直线检测P

函数原型:

1
2
3
4
5
6
7
8
void cv::HoughLinesP    (   InputArray      image,//输入图片
OutputArray lines,//输出结果
double rho,//以像素为单位的累加器的距离分辨率。
double theta,//以弧度表示的累加器的角度分辨率。
int threshold,//蓄能器阈值参数。 只返回那些获得足够选票的行(>texttt{阈值})。
double minLineLength = 0,//最小线长。 短于它的线段被拒绝。
double maxLineGap = 0 //同一行上的点之间的最大允许间隙来连接它们。
);

功能和houghlines一样,只是算法不同。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//--------------------------------------【程序说明】-------------------------------------------
// 程序说明:《OpenCV3编程入门》OpenCV3版书本配套示例程序62
// 程序描述:HoughLinesP函数用法示例
// 2014年11月 Created by @浅墨_毛星云
// 2014年12月 Revised by @浅墨_毛星云
//------------------------------------------------------------------------------------------------

//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-------------------------------------------------------------------------------------------------
int main()
{
//【1】载入原始图和Mat变量定义
Mat srcImage = imread("lena.jpg"); //工程目录下应该有一张名为1.jpg的素材图
Mat midImage, dstImage; //临时变量和目标图的定义

//【2】进行边缘检测和转化为灰度图
Canny(srcImage, midImage, 50, 200, 3); //进行一此canny边缘检测
cvtColor(midImage, dstImage, COLOR_GRAY2BGR); //转化边缘检测后的图为灰度图

//【3】进行霍夫线变换
vector<Vec4i> lines; //定义一个矢量结构lines用于存放得到的线段矢量集合
HoughLinesP(midImage, lines, 1, CV_PI / 180, 80, 50, 10);

//【4】依次在图中绘制出每条线段
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);
}

//【5】显示原始图
imshow("Original", srcImage);

//【6】边缘检测后的图
imshow("Canny result", midImage);

//【7】显示效果图
imshow("Result", dstImage);

waitKey(0);

return 0;
}

测试效果:

linesp

相比于houghlines,检测结果更好。

可以看一下官方实例:HoughLinesP

霍夫圆检测

函数原型:

1
2
3
4
5
6
7
8
9
10
void cv::HoughCircles   (   InputArray      image,//输入图片
OutputArray circles,//输出结果
int method,//检测方式,有HOUGH_GRADIENT和HOUGH_GRADIENT_ALT
double dp,//累加器分辨率与图像分辨率的反比。 例如,如果dp=1,则累加器具有与输入图像相同的分辨率。 如果dp=2,则累加器有一半大的宽度和高度。 对于HOUGH_GRADIENT_ALT,建议的值是dp=1.5,除非需要检测一些小的非常圆。
double minDist,//检测到的圆的中心之间的最小距离。 如果参数太小,除了真圆之外,还可能错误地检测到多个邻居圆。 如果太大,可能会漏掉一些圆圈。
double param1 = 100,//第一种方法特定的参数。 在HOUGH_GRADIENT和HOUGH_GRADIENT_ALT的情况下,它是两个传递给Canny边缘检测器的较高阈值(较低的一个较小两倍)。 请注意,HOUGH_GRADIENT_ALT使用Scharr算法计算图像导数,因此阈值通常较高,例如300幅或暴露和对比图像。
double param2 = 100,//第二种方法特定的参数。 在HOUGH_GRADIENT的情况下,它是检测阶段圆中心的累加器阈值。 越小,可能检测到的假圆越多。 循环,对应较大的累加器值,会先返回。 在HOUGH_GRADIENT_ALT算法的情况下,这是圆的“完美”度量。 越接近1,形状越好的圆算法选择。 在大多数情况下,0.9应该是好的。 如果你想更好地检测小圆圈,你可能会把它降低到0.85,0.8甚至更少。 但也要尽量限制搜索范围[最小半径,最大半径],以避免许多假圆。
int minRadius = 0,//结果过滤下限半径值
int maxRadius = 0 //结果过滤上限半径值
);

检测圆就需要有圆的图片,从百度上面找了一张:

circle

有圆有直线

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//--------------------------------------【程序说明】-------------------------------------------
// 程序说明:《OpenCV3编程入门》OpenCV2版书本配套示例程序63
// 程序描述:HoughCircles函数用法示例
// 2014年06月 Created by @浅墨_毛星云
// 2014年11月 Revised by @浅墨_毛星云
//------------------------------------------------------------------------------------------------

//---------------------------------【头文件、命名空间包含部分】-------------------------------
// 描述:包含程序所使用的头文件和命名空间
//----------------------------------------------------------------------------------------------
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main()
{
//【1】载入原始图、Mat变量定义
Mat srcImage = imread("timg.jpg"); //工程目录下应该有一张名为1.jpg的素材图
Mat midImage, dstImage; //临时变量和目标图的定义

//【2】显示原始图
imshow("Original", srcImage);

//【3】转为灰度图并进行图像平滑
cvtColor(srcImage, midImage, COLOR_BGR2GRAY); //转化边缘检测后的图为灰度图
GaussianBlur(midImage, midImage, Size(9, 9), 2, 2);

//【4】进行霍夫圆变换
vector<Vec3f> circles;
HoughCircles(midImage, circles, HOUGH_GRADIENT, 1.5, 10, 200, 100, 0, 0);

//【5】依次在图中绘制出圆
for (size_t i = 0; i < circles.size(); i++)
{
//参数定义
Point center(round(circles[i][0]), round(circles[i][1]));
int radius = round(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);
}

//【6】显示效果图
imshow("Result", srcImage);

waitKey(0);

return 0;
}

测试效果:

circle

可以看出只有最外圈的那个圆是对的,可以考虑非极大值抑制。

下一篇:OpenCV4入门系列教程62:颜色圆检测