opencv之霍夫变换

设已知一黑白图像上画了一条直线,要求出这条直线所在的位置。我们知道,直线的方程可以用y=k*x+b 来表示,其中kb是参数,分别是斜率和截距。也就是说,我们将原始图像需要检测的直线,表示成y = k*x + b, 只要找出唯一的kb即可检测出该直线。该直线在原始图中是一系列离散点的集合,过该直线上某一点(x0,y0)的所有直线的参数都会满足方程y0=kx0+b。即点(x0,y0)确定了一族直线。而方程y0=kx0+b在参数k--b平面上是一条直线,(你也可以是方程b=-x0*k+y0对应的直线).即点(x0,y0)在参数空间确定了一条直线。这样,图像x--y平面上的一个前景像素点就对应到参数平面上的一条直线。因此,图像x-y内需检测直线上的N个点,在参数平面会有N条直线。而图像x-y内的直线有唯一一个kb,因此,相应的参数平面N条直线必然有唯一一个交点。我们举个例子说明解决前面那个问题的原理。设图像上的直线是y=x, 我们先取上面的三个点:A(0,0), B(1,1), C(22)。可以求出,过A点的直线的参数要满足方程b=0, B点的直线的参数要满足方程1=k+b, C点的直线的参数要满足方程2=2k+b, 这三个方程就对应着参数平面上的三条直线,而这三条直线会相交于一点(k=1,b=0)。 同理,原图像上直线y=x上的其它点((3,3),(4,4)) 对应参数平面上的直线也会通过点(k=1,b=0)。这个性质就为我们解决问题提供了方法,就是把图像平面上的点对应到参数平面上的线,最后通过统计特性来解决问题。假如图像平面上有两条直线,那么最终在参数平面上就会看到两个峰值点,依此类推。

简而言之,Hough变换思想为:在原始图像坐标系下的一个点对应了参数坐标系中的一条直线,同样参数坐标系的一条直线对应了原始坐标系下的一个点,然后,原始坐标系下呈现直线的所有点,它们的斜率和截距是相同的,所以它们在参数坐标系下对应于同一个点。这样在将原始坐标系下的各个点投影到参数坐标系下之后,看参数坐标系下有没有聚集点,这样的聚集点就对应了原始坐标系下的直线。

在实际应用中,y=k*x+b形式的直线方程没有办法表示x=c形式的直线(这时候,直线的斜率为无穷大)。所以实际应用中,利用极坐标的方式,将直线方程表示成:ρ=x*cos(ξ)+y*sin(ξ)。其中ρ表示直角坐标系中原点到直线的距离,ξ表示x轴与直线法线的夹角,取逆时针方向。这样,图像平面上的一个点就对应到ρξ平面上的一条曲线上,其它的还是一样。

在实现的图像处理领域,图像的像素坐标P(x, y)是已知的,而ρ,ξ 则是我们要寻找的变量。如果我们能根据像素点坐标P(x, y)值绘制每个(ρ,ξ)值的话,那么就从图像笛卡尔坐标系统转换到极坐标霍夫空间系统,这种从点到曲线的变换称为直线的霍夫变换。变换通过量化霍夫参数空间为有限个值间隔等分或者累加格子。当霍夫变换算法开始,每个像素坐标点P(x, y)被转换到(r, theta)的曲线点上面,累加到对应的格子数据点,当一个波峰出现时候,说明有直线存在。同样的原理,我们可以用来检测圆,只是对于圆的参数方程变为如下等式:

(x –a ) ^2 + (y-b) ^ 2 = r^2其中(a, b)为圆的中心点坐标,r圆的半径。这样霍夫的参数空间就变成一个三维参数空间。给定圆半径转为二维霍夫参数空间,变换相对简单,也比较常用。

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;


int main()
{
	Mat img;
	img=imread("C://Users//huashuo111//Desktop//楼房.jpg");
	namedWindow("原图",1);
	imshow("原图",img);
	Mat img1;
	
	Canny(img,img1,50,150);
	Mat dst;
	cvtColor(img1,dst,CV_GRAY2BGR);//不把灰度图转化为RGB图,后面的画彩色线条不可执行(只可灰色)
 
	vector<Vec2f>lines;
	HoughLines(img1,lines,1,CV_PI/180,150);
	for(size_t i=0;i<lines.size();i++)
	{
		float rho=lines[i][0];
		float theta=lines[i][1];
		double a=cos(theta);
		double b=sin(theta);
		double x0=rho*a;
		double y0=rho*b;
		Point pt1,pt2;
		pt1.x=x0-1000*b;
		pt1.y=y0+1000*a;
		pt2.x=x0+1000*b;
		pt2.y=y0-1000*a;//保证在与法线垂直的线上,且经过(x0,y0)
		line(dst,pt1,pt2,Scalar(55,100,195),1,8,0);
	}
	
	imshow("效果图",dst);

	imwrite("C://Users//huashuo111//Desktop//楼房线.jpg",dst);
	
	
	waitKey();
	return 0;
}

原图:


霍夫变换:



累计概率霍夫变换(PPHT)


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


#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;


int main()
{
	Mat img;
	img=imread("C://Users//huashuo111//Desktop//楼房.jpg");
	namedWindow("原图",1);
	imshow("原图",img);
	Mat img1;
	
	Canny(img,img1,50,150);
	Mat dst;
	cvtColor(img1,dst,CV_GRAY2BGR);//不把灰度图转化为RGB图,后面的画彩色线条不可执行(只可灰色)
 
	vector<Vec4i>lines;
	HoughLinesP(img1,lines,1,CV_PI/180,80,50,10);//lines存储了每一条直线起始点
	for(size_t i=0;i<lines.size();i++)
	{
		Point pt1,pt2;
		pt1.x=lines[i][0];
		pt1.y=lines[i][1];
		pt2.x=lines[i][2];
		pt2.y=lines[i][3];
		line(dst,pt1,pt2,Scalar(55,100,195),1,8,0);
	}
	
	imshow("效果图",dst);

	imwrite("C://Users//huashuo111//Desktop//楼房线.jpg",dst);
	
	
	waitKey();
	return 0;
}


效果图:



注意观察和经典霍夫变换效果图的区别,累计概率霍夫变换效果图直线起始点基本上吻合实际。


霍夫圆变换

霍夫圆变换的基本原理和上面讲的霍夫线变化大体上是很类似的,只是点对应的二维极径极角空间被三维的圆心点x, y还有半径r空间取代。说“大体上类似”的原因是,如果完全用相同的方法的话,累加平面会被三维的累加容器所代替:在这三维中,一维是x,一维是y,另外一维是圆的半径r。这就意味着需要大量的内存而且执行效率会很低,速度会很慢。

 

对直线来说, 一条直线能由参数极径极角表示. 而对圆来说, 我们需要三个参数来表示一个圆, 也就是:



C++: void HoughCircles(InputArray image,OutputArray circles, int method, double dp, double minDist, double param1=100,double param2=100, int minRadius=0, int maxRadius=0 )

  • 第二个参数,InputArray类型的circles,经过调用HoughCircles函数后此参数存储了检测到的圆的输出矢量,每个矢量由包含了3个元素的浮点矢量(x, y, radius)表示。
  • 第三个参数,int类型的method,即使用的检测方法,目前OpenCV中就霍夫梯度法一种可以使用,它的标识符为CV_HOUGH_GRADIENT,在此参数处填这个标识符即可。
  • 第四个参数,double类型的dp,用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。上述文字不好理解的话,来看例子吧。例如,如果dp= 1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。
  • 第五个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。
  • 第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。
  • 第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了。
  • 第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值。
  • 第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值。

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;


int main()
{
	Mat img;
	img=imread("C://Users//huashuo111//Desktop//圆.jpg");
	namedWindow("原图",1);
	imshow("原图",img);
	Mat img1;
	cvtColor(img,img1,CV_BGR2GRAY);
	GaussianBlur(img1,img1,Size(5,5),2,2);
	//Canny(img1,img1,50,150);
 vector<Vec3f>circles;
	HoughCircles(img1,circles,CV_HOUGH_GRADIENT,1.5,10,200,180,0,0);
	for(size_t i=0;i<circles.size();i++)
	{
		Point center=Point(cvRound(circles[i][0]),cvRound(circles[i][1]));
		int radius=circles[i][2];
		circle(img,center,3,Scalar(0,255,0),-1,8,0);
		circle(img,center,radius,Scalar(0,0,255),1,8,0);

	}
	
	
	imshow("效果图",img);

	imwrite("C://Users//huashuo111//Desktop//检测圆.jpg",img);
	
	
	waitKey();
	return 0;
}


原图:

效果图:







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值