Hough Transform直线检测

霍夫变换(Hough Transform)是Paul Hough于1962年提出来的,一开始是用于检测图像中的直线的,后来还扩展到检测圆、检测任意形状的物体等。关于霍夫变换的博客多得不胜枚举,而且很多都是很厉害,解释的非常详细而且通俗。现在我就写一下我自己的理解,个人笔记,不一定准确。

一、原理理解部分

首先,我们关于平面中的一条直线有如下表达

在笛卡尔坐标中表示为

这里,我们知道平面中一条直线会经过无数个离散点,而经过某个点的直线会有N多条:

如上图所示,二维平面有P1~P7共7个点,然后就来拟合下,假设就拟合出上面L1~L5共5条直线,如果按照投票来算的话,那肯定是L1最有可能存在,因为直线L1经过图上的4个点,而其他直线基本就经过2个点,所以假设图像只有这几个点的话,那最有可能存在的直线就是L1啦。此时,如果我们知道直线L1的两个参数left ( w,b 
ight )那么这条直线基本就确定了。然而,我们应该是不知道的吧,至少算法不是向我们人眼一样,一眼就觉得P1、P2、P5和P6可以拟合一条直线,直接取其中两个点计算斜率和截距就得到直线了。我们需要遍历斜率和截距的所有可能值,然后计算图上的点是否在某个斜率和截距表示的直线上,如果是的话,那么这条直线的投票数量加1,最后票数最多的直线的参数就是所要拟合的直线的参数了。

要遍历left ( w,b 
ight )的话,我们就把斜截式表示为:

然后假设斜率w的取值为[0,10],那就遍历w和图上的点的坐标left ( x,y 
ight ),根据上式计算截距b,对应的[0,10]投票数加1,最后票数最多的left ( w,b 
ight )就可以拟合一条直线了。

我们知道,斜截式可以表示绝大部分的直线,但是有一种情况是斜截式表示不了的,就是直线垂直x轴的情况,此时斜率为无穷,好像用斜截式表示不了吧。所以就想到用极坐标的形式来表示,但是实际上应该不能算是极坐标吧,因为依然还是笛卡尔坐标,只不过用了角度和距离来表示罢了,看起来有点类似极坐标的表示罢了(挺多博客都直接说是极坐标表示,我觉得应该不准确吧)。这里还是先上图再推出公式的表达:

图中直线过了点left ( x,y 
ight ),原点到直线的距离是可以计算的,这个距离用公式表达就是:

并且原点到直线的距离是唯一的,也就是说,如果角度θ确定了,那么无论取直线上的哪个点都是可以取得一个固定的距离ρ,不在同一直线上的点计算出来的ρ是不同的,所以在遍历角度的时候,就统计一次θ确定下不同ρ得到的票数,然后改变θ,再次统计不同ρ得到的票数,这样遍历完所有θ后我们就有很多组(θ,ρ)的组合以及他们得到的票数,当票数超过一定阈值或者取最大值的作为直线的参数,得到检测到的直线。

二、OpenCV实现

OpenCV提供的实现霍夫变换的函数有两个,一个是标准霍夫变换HoughLines,一个是概率霍夫变换HoughLinesP,HoughLines的函数原型为:

void HoughLines( InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 );

其参数意义如下:

image:输入图像,灰度图

lines:直线集合,每个直线包含两个值,分别是ρ和θ

rho:距离精度,以像素为单位

theta:角度精度

threshold:识别阈值,也就是累积超过这个值的才会被认为是直线的参数

srn:对于多尺度霍夫变换,它是距离精度rho的除数,粗距离的精度为rho,精细的距离精度为rho/srn;

stn:对于多尺度霍夫变换,它是角度精度theta的除数,粗角度精度为theta,精细的角度精度为theta/srn;

当srn和stn都设置为0的时候,使用标准的霍夫变换。实现的例子(来自OpenCV官网例子修改)如下:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <iostream>

using namespace cv;
using namespace std;

void help()
{
	cout << 
	    "
This program demonstrates line finding with the Hough transform.
"
	    "Usage:
"
	    "./houghlines <image_name>, Default is pic1.jpg
" << endl;
}

int main(int argc, char** argv)
{
	const char* filename = argc >= 2 ? argv[1] : "D:/building.jpg";

	Mat src = imread(filename, 0);
	if (src.empty())
	{
		help();
		cout << "can not open " << filename << endl;
		return -1;
	}

	Mat dst, cdst;
	Canny(src, dst, 50, 200, 3);
	cvtColor(dst, cdst, CV_GRAY2BGR);

	vector<Vec2f> lines;
	HoughLines(dst, lines, 1, CV_PI / 180, 250);

	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 = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));
		line(cdst, pt1, pt2, Scalar(0, 0, 255), 1, CV_AA);
	}
    
    imshow("source", src);
	imshow("detected lines", cdst);

	waitKey();

	return 0;
}

原图和检测图如下:

HoughLinesP的函数原型如下:

void HoughLinesP( InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 );

前面的参数和标准霍夫变换是一样的,后面两个参数的意义如下:

minLineLength:最小线长,线长小于这个值的会被忽略

maxLineGap:同一条直线上,连接各点的最大允许间隔例子如下,同样来自OpenCV官网例子修改:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <iostream>

using namespace cv;
using namespace std;

void help()
{
	cout << "
This program demonstrates line finding with the Hough transform.
"
		"Usage:
"
		"./houghlines <image_name>, Default is pic1.jpg
" << endl;
}

int main(int argc, char** argv)
{
	const char* filename = argc >= 2 ? argv[1] : "D:/building.jpg";

	Mat src = imread(filename, 0);
	if (src.empty())
	{
		help();
		cout << "can not open " << filename << endl;
		return -1;
	}

	Mat dst, cdst;
	Canny(src, dst, 50, 200, 3);
	cvtColor(dst, cdst, CV_GRAY2BGR);

	vector<Vec4i> lines;
	HoughLinesP(dst, lines, 1, CV_PI / 180, 250, 50, 10);
	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i l = lines[i];
		line(cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 1, CV_AA);
	}
    
	imshow("source", src);
	imshow("detected lines", cdst);

	waitKey();

	return 0;
}

检测结果如下:

莫唱当年长恨歌,

人间亦自有银河。

石壕村里夫妻别,

泪比长生殿上多。

  -- 袁枚 《马嵬》

上善若水,为而不争。
原文地址:https://www.cnblogs.com/Bearoom/p/11721769.html