Opencv2.4学习::霍夫变换(1)线变换

霍夫变换


特点:

  • 用于识别几何形状
  • 不受旋转角度影响

 线变换基本原理

对于上述不理解的,可以看下面:

对于某直线,可以过原点作其法向量,假设在 ρ为原点到直线距离 和 θ已知的情况下

因此,该直线的   截距=ρ/sin θ   ,     斜率=  -1/sin θ

可以写出直线方程:(-1/tan θ)*x + ρ/sin θ  = y

变形后,可得:x*cos θ +y*sin θ = ρ                        (1)

 

 那么,反过来,当我们固定一个点(x1,y1)时,由 ρ 和 θ 的不同组合,即可得到通过点(x1,y1)的不同直线。如下图:

 由式(1),我们将x,y看作是常量, ρ 和 θ 为变量,那么,可以重新写成:

      x*(1/tan θ) +y= ρ /sin θ

这个变换的意义在于,表示了在直角坐标系中经过点(x,y)的所有直线,在(ρ,θ)坐标系下,为一条直线上不同的点(实际上应该不是直线,这里为了方便描述,用直线代替)

 


 根据前文所述:

表示了在直角坐标系中经过点(x,y)的所有直线,在(ρ,θ)坐标系下,为一条直线上不同的点

 那么,再次反过来看,根据对偶性,则有:

在(ρ,θ)霍夫空间中,经过点(ρ,θ)的所有直线,为直角坐标系中一条直线上不同的点

到这里,情况就比较明朗了:

要做的事情就是

(1)对每个点进行霍夫变换,那么该点在霍夫空间中对应一条直线

(2)如果霍夫空间中的某一条直线与另外一些直线相交于同一点A

(3)那么, 表示在直角坐标系中这些直线所对应的像素点是共线的。

(4)最后,再通过阈值来判断 经过点A的直线是否达到阈值,是,则认为在直角坐标系中存在对应直线


 核心函数

一、标准霍夫变换

C++: void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
  • 第一个參数,输入图像。即源图像,需为8位的单通道二进制图像。能够将随意的源图加载进来后由函数改动成此格式后,再填在这里。
  • 第二个參数。经过调用HoughLines函数后储存了霍夫线变换检測到线条的输出矢量。每一条线由具有两个元素的矢量表示,当中,是离坐标原点((0,0)(也就是图像的左上角)的距离。 是弧度线条旋转角度(0~垂直线,π/2~水平线)。
  • 第三个參数,double类型的rho,以像素为单位的距离精度。还有一种形容方式是直线搜索时的进步尺寸的单位半径。PS:Latex中/rho就表示 
  • 第四个參数,double类型的theta,以弧度为单位的角度精度。还有一种形容方式是直线搜索时的进步尺寸的单位角度。
  • 第五个參数,int类型的threshold。累加平面的阈值參数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才干够被检測通过并返回到结果中。
  • 第六个參数。double类型的srn,有默认值0。对于多尺度的霍夫变换。这是第三个參数进步尺寸rho的除数距离。

    粗略的累加器进步尺寸直接是第三个參数rho,而精确的累加器进步尺寸为rho/srn。

  • 第七个參数,double类型的stn。有默认值0,对于多尺度霍夫变换,srn表示第四个參数进步尺寸的单位角度theta的除数距离。且假设srn和stn同一时候为0,就表示使用经典的霍夫变换。否则,这两个參数应该都为正数。

二、概率霍夫变换【常用

概率霍夫变换(Progressive Probabilistic Hough Transform)的原理很简单,如下所述:

1.随机获取边缘图像上的前景点,映射到极坐标系画曲线;

2.当极坐标系里面有交点达到最小投票数,将该点对应x-y坐标系的直线L找出来;

3.搜索边缘图像上前景点,在直线L上的点(且点与点之间距离小于maxLineGap的)连成线段,然后这些点全部删除,并且记录该线段的参数(起始点和终止点),当然线段长度要满足最小长度;

4.重复1. 2. 3.。

void HoughLinesP(InputArray image,OutputArray lines, double rho, double theta, int threshold, double minLineLength=0,double maxLineGap=0 )
  • image为输入图像,要求是单通道,8位图像
  • lines为输出参数,4个元素表示,即直线的起始和终止端点的4个坐标(x1,y1),(x2,y2)
  • rho为距离分辨率,一般为1
  • theta为角度的分辨率,一般CV_PI/180
  • threshold为阈值,hough变换图像空间值最大点,大于则认为是直线
  • minLineLength为最小直线长度(像素),即如果小于该值,则不输出该结果
  • maxLineGap为直线间隙最大值,如果两条直线间隙大于该值,则被认为是两条线段,否则是一条。

调用代码: 

代码中使用的是概率霍夫变换,当然据说标准霍夫变换另有他用,若遇到,会贴出来的。

#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void main()
{
	Mat srcImage = imread("F:\\opencv_re_learn\\5.jpg");
	if (!srcImage.data){
		cout << "failed to read" << endl;
		system("pause");
		return;
	}
	//灰度图
	Mat srcGray;
	cvtColor(srcImage, srcGray, CV_BGR2GRAY);

	//Canny 边缘检测
	medianBlur(srcGray, srcGray,7);
	Canny(srcGray, srcGray, 180, 100, 3);
	
	////标准霍夫变换
	//vector<Vec2f> lines;
	//输入图像必须为二值化图像
	//HoughLines(srcGray, lines, 1, CV_PI / 180, 100, 0, 0);
	//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(srcImage, pt1, pt2, Scalar(255, 0, 0), 3, CV_AA);
	//}

	//统计概率霍夫变换
	vector<Vec4i> lines;
	//输入图像必须为二值化图像
	HoughLinesP(srcGray, lines, 1, CV_PI / 180, 60, 30, 20);
	for (size_t i = 0; i < lines.size(); i++){
		Vec4i l = lines[i];
		//绘制检测结果
		line(srcImage, Point(l[0], l[1]),
			Point(l[2], l[3]), Scalar(0, 0, 255), 3, CV_AA);
	}
	imshow("src", srcImage);
	imshow("srcGray", srcGray);
	waitKey(0);
}

实现效果:

原图:

先进行中值滤波,把地板上的噪声滤除

然后进行Canny算子的边缘检测,调好参数,可以使手机轮廓显现出来

最后霍夫变换,还是要调参,只要参数设置好,手机的基本直线轮廓就出来了 


目前 另外一种比较新的直线检测算法是LSD算法,不过只在OPENCV3.0或更新的版本中才有,暂不在Opencv2.4中,有机会会尝试一下的。

相关原理参见:

http://www.cnblogs.com/Jessica-jie/p/7512152.html

https://blog.csdn.net/carson2005/article/details/9326847

猜你喜欢

转载自blog.csdn.net/dieju8330/article/details/82857423