天天看点

基于Hough变换的直线和圆的检测与提取

学数字图像处理有挺长时间了,正好最近这段时间有空,学习了一下Hough变换,作了几个比较简单的东西,分享出来,希望能和大家一起学习,也希望各位能提出宝贵的意见,共同进步,文采不好,欢迎拍砖,下面正式进入正题。

Hough变换于1962年被Paul Hough提出以来,在数字图像中基本几何形状的检测与提取方面,受到了各界学者的广泛关注。Hough变换因其具有很好地鲁棒性、对图像数据的不完全性、以及检测的准确性等优点被广泛应用生产生活中。举几个最常见例子,在工业生产流水线上,纸张或铝板在出厂的过程中,破损与划痕在所难免,而这些破损与划痕普遍的形状无非就是直线和原点。我们要在减小出厂产品破损率,检测成本做到最低,最好的方法无非就是利用数字图像处理的方法了。又扯远了。。。言归正传,那么什么是Hough变换呢?一听这个Hough变换这个名字,相信很多初学者就被吓蒙了,没事,听我细细道来。

Hough变换就是在两个两个坐标系下进行的变换。

step1:假设直线方程是:y=kx+b,其中s1和s2是直线上的两个点,如图1所示。

step2:就是在进行坐标系变化把y=kx+b变成b=-xk+y。直线上的点s1(x1,y1)在转换坐标系后变为一条直线,s2(x2,y2)这个点在转换坐标系后变成另一条直线,依次类推。在变换坐标系后,Hough发现了一个规律,在变换后所有直线都相交于一个点!于是,Hough提出检测直线的问题可以转化为检测点的问题,交点的位置涵盖了原图中直线的位置信息和方向。

基于Hough变换的直线和圆的检测与提取
基于Hough变换的直线和圆的检测与提取

图1 图2

但是这还不够,我们想一下,当原始图像中有一条垂直于x轴的直线怎么办?斜率k为无穷大啊,没法坐标转化。但是问题还得解决。Hart、Dual提出了将直线换一下表达形式,即将笛卡尔直角坐标系下y=kx+b先变换一下,变换为极坐标系下的p=x*cos(theta)+y*sin(theta);(原谅我这么写,没法打公式啊)

基于Hough变换的直线和圆的检测与提取

。同上,在转换后再进行一步转换坐标系,原图像直线上的每个点都对应转换坐标的一条正弦曲线,许多点一起变换坐标系又能产生一个交点。如下所示:

基于Hough变换的直线和圆的检测与提取
基于Hough变换的直线和圆的检测与提取

图3 图4

这便是标准Hough变换检测直线的基本原理。

圆的检测是Hough变换直线检测的延伸,这里就不给原理了,直接给代码吧。

下面给出用opencv实现的代码:(vs2015+opencv3.1.0)

vs2015下载

http://www.ithome.com/html/win10/164028.htm

opencv下载

https://sourceforge.net/projects/opencvlibrary/files/opencv-win/3.1.0/opencv-3.1.0.exe/download

vs2015+opencv3.1.0的配置如下没有配置的或者配置不对的可以去看看:

http://blog.csdn.net/liu798675179/article/details/51233742

#include "stdafx.h"
#include <opencv2/opencv.hpp>  
using namespace std;
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  
int main()
{
	const char *pstrWindowsSrcTitle = "原图";
	const char *pstrWindowsLineName = "hough变换后图像";

	// 从文件中加载原图  
	IplImage *pSrcImage = cvLoadImage("c://pic//line.jpg", CV_LOAD_IMAGE_UNCHANGED);
	// 灰度图  
	IplImage *pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);

	cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);
	//边缘图  
	cvSmooth(pGrayImage, pGrayImage, CV_GAUSSIAN,5,5);
	//平滑滤波

	IplImage *pCannyImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
	cvCanny(pGrayImage, pCannyImage, 80, 90);
 
	//线段检测(二值图像) 
	CvMemStorage *pcvMStorage = cvCreateMemStorage();
	double fRho = 1;
	double fTheta = CV_PI / 180;
	int nMaxLineNumber = 5;   //最多检测条直线  
	double fMinLineLen = 50;   //最小线段长度  
	double fMinLineGap = 10;   //最小线段间隔  
	CvSeq *pcvSeqLines = cvHoughLines2(pCannyImage, pcvMStorage, CV_HOUGH_PROBABILISTIC, fRho, fTheta, nMaxLineNumber, fMinLineLen, fMinLineGap);
	//圆检测(灰度图)  
	double fMinCircleGap = pGrayImage->height / 10;
	CvSeq *pcvSeqCircles = cvHoughCircles(pGrayImage, pcvMStorage, CV_HOUGH_GRADIENT, 1, fMinCircleGap,100);//每个圆由三个浮点数表示:圆心坐标(x,y)和半径  
	

	//绘制线段(用红色线条表示)  
	IplImage *pColorImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);

	cvCvtColor(pCannyImage, pColorImage, CV_GRAY2BGR);
	int i;
	for (i = 0; i < pcvSeqLines->total; i++)
	{
		CvPoint* line = (CvPoint*)cvGetSeqElem(pcvSeqLines, i);
		cvLine(pColorImage, line[0], line[1], CV_RGB(255, 0, 0), 2);
	}
	//绘制圆形(红色线段表示)
	int j;
	for (j = 0; j < pcvSeqCircles->total; j++)
	{
		float* p = (float*)cvGetSeqElem(pcvSeqCircles, j);

		cvCircle(pColorImage, cvPoint(cvRound(p[0]), cvRound(p[1])), cvRound(p[2]), CV_RGB(255, 0, 0), 2);
	}

	cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);
	cvShowImage(pstrWindowsSrcTitle, pSrcImage);
	cvNamedWindow(pstrWindowsLineName, CV_WINDOW_AUTOSIZE);
	cvShowImage(pstrWindowsLineName, pColorImage);

	cvWaitKey(0);

	cvReleaseMemStorage(&pcvMStorage);
	cvDestroyWindow(pstrWindowsSrcTitle);
	cvDestroyWindow(pstrWindowsLineName);
	cvReleaseImage(&pSrcImage);
	cvReleaseImage(&pGrayImage);
	cvReleaseImage(&pCannyImage);
	cvReleaseImage(&pColorImage);
	return 0;
}
           

实验结果如下:

基于Hough变换的直线和圆的检测与提取
基于Hough变换的直线和圆的检测与提取

继续阅读