霍夫变换是一种在图像中寻找直线、圆及其他形状的方法。原始的霍夫变化是一种直线变换,即在二值图像中寻找直线的一种相对快速方法,变换可以推广到其他普通的情况,而不仅仅是简单的直线。在这篇博文中,我们先对霍夫变换的线段检测讨论下。
(1)霍夫变换的线段检测理论
如下图所示,在直角坐标系中有一条直线l,原点到该直线的垂直距离是ρ,垂线与X轴的夹角θ,则这条直线是唯一的,且其方程为:
而这条直线用极坐标表示为(ρ,θ),可见直角坐标系中的一条直线对应着极坐标下的一点,这种线到点的变换就是Hough变换。
在直角坐标系中,过任意一点(x0,y0)的直线系,如下图所示,满足
其中:
而这些直线在极坐标系中所对应的点(ρ,θ)构成一条正弦曲线,反之,在极坐标系中在这条正弦曲线上的点均对应着直角坐标系中过(x0,y0)的一条直线,如图所示:
设平面上有若干点,过每点的直线系分别对应于极坐标系上的一条正弦曲线,若这些正弦曲线有共同的交点(ρ’,θ’),如图所示,这些点共线,且这些点对应的直线方程为:
这就是Hough变换检测直线的原理。
在OpenCV中给出了相关的基于霍夫变换的线段检测函数cvHoughLines2(),下面简要介绍下这个函数的各个参数意义。
CvSeq* cvHoughLines2(
CvArr* image,//输入图像,必须为8位的二值图像
void* line_storage,//指向保存结果位置的指针
int method,//CV_HOUGH_STANDARD、PROBABILISTIC等
double rho,
double theta,//rho,theta是设置直线的分辨率,单位分别为像素和弧度
int threshold,//一条直线在累计平面中必须要达到的值
double param1=0,
double param2=0
)
上面这个函数返回的结果是一个指向内存块的指针,所以在画出检测到的直线时,要遍历这个序列。下面给出这个程序的示例。
(2)程序示例及运行结果
#include
#include
using namespace std;
int main()
{
const char *pSrcWindow = "原图";
const char *pDstWindow = "Hough图";
IplImage *pSrcImage = cvLoadImage("1.png", CV_LOAD_IMAGE_UNCHANGED);
IplImage *pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);
//通过canny检测得到二值图,因为cvHoughLines2()中第一个参数输入图像必须是8位二值图像
IplImage *pCannyImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
cvCanny(pGrayImage, pCannyImage, 30, 90, 3);
//设置参数数值
CvMemStorage *line_storage = cvCreateMemStorage();
double rho = 1;
double theta = CV_PI / 180;
int threshold = 50;
double param1 = 50;
double param2 = 10;
//调用函数cvHoughLines2()函数,返回检测到的线段序列
CvSeq* LineSeq = cvHoughLines2(pCannyImage, line_storage, CV_HOUGH_PROBABILISTIC, rho, theta, threshold, param1, param2);
//创建输出图像,并将图像从灰度图转换到RGB空间
IplImage *pDstImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);
cvCvtColor(pCannyImage, pDstImage, CV_GRAY2BGR);
//画出检测到的线段,因为在函数cvHoughLines2()返回得到的是一个序列seq,下面其实是这个序列的遍历;
for (int i = 0; i < LineSeq->total; ++i)
{
CvPoint* p = (CvPoint*)cvGetSeqElem(LineSeq, i);
cvLine(pDstImage, p[0], p[1], CV_RGB(255, 0, 0), 2);
}
cvNamedWindow(pSrcWindow, CV_WINDOW_AUTOSIZE);
cvNamedWindow(pDstWindow, CV_WINDOW_AUTOSIZE);
cvShowImage(pSrcWindow, pSrcImage);
cvShowImage(pDstWindow, pDstImage);
cvWaitKey();
cvReleaseMemStorage(&line_storage);
cvReleaseImage(&pSrcImage);
cvReleaseImage(&pGrayImage);
cvReleaseImage(&pCannyImage);
cvReleaseImage(&pDstImage);
cvDestroyWindow(pSrcWindow);
cvDestroyWindow(pDstWindow);
return 0;
}