转载自:http://blog.csdn.net/zhaocj/article/details/40047397
标准霍夫变换本质上是把图像映射到它的参数空间上,它需要计算所有的M个边缘点,这样它的运算量和所需内存空间都会很大。如果在输入图像中只是处理m(m<M)个边缘点,则这m个边缘点的选取是具有一定概率性的,因此该方法被称为概率霍夫变换(Probabilistic Hough Transform)。该方法还有一个重要的特点就是能够检测出线端,即能够检测出图像中直线的两个端点,确切地定位图像中的直线。
HoughLinesP函数就是利用概率霍夫变换来检测直线的。它的一般步骤为:
1、随机抽取图像中的一个特征点,即边缘点,如果该点已经被标定为是某一条直线上的点,则继续在剩下的边缘点中随机抽取一个边缘点,直到所有边缘点都抽取完了为止;
2、对该点进行霍夫变换,并进行累加和计算;
3、选取在霍夫空间内值最大的点,如果该点大于阈值的,则进行步骤4,否则回到步骤1;
4、根据霍夫变换得到的最大值,从该点出发,沿着直线的方向位移,从而找到直线的两个端点;
5、计算直线的长度,如果大于某个阈值,则被认为是好的直线输出,回到步骤1。
HoughLinesP函数的原型为:
void HoughLinesP(InputArray image,OutputArray lines, double rho, double theta, int threshold, double minLineLength=0,double maxLineGap=0 )
image为输入图像,要求是8位单通道图像
lines为输出的直线向量,每条线用4个元素表示,即直线的两个端点的4个坐标值
rho和theta分别为距离和角度的分辨率
threshold为阈值,即步骤3中的阈值
minLineLength为最小直线长度,在步骤5中要用到,即如果小于该值,则不被认为是一条直线
maxLineGap为最大直线间隙,在步骤4中要用到,即如果有两条线段是在一条直线上,但它们之间因为有间隙,所以被认为是两个线段,如果这个间隙大于该值,则被认为是两条线段,否则是一条。
HoughLinesP函数是在sources/modules/imgproc/src/hough.cpp文件中被定义的:
- void cv::HoughLinesP( InputArray _image, OutputArray _lines,
- double rho, double theta, int threshold,
- double minLineLength, double maxGap )
- {
- Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);
- Mat image = _image.getMat();
- CvMat c_image = image;
- CvSeq* seq = cvHoughLines2( &c_image, storage, CV_HOUGH_PROBABILISTIC,
- rho, theta, threshold, minLineLength, maxGap );
- seqToMat(seq, _lines);
- }
从HoughLinesP函数可以看出,该函数会调用cvHoughLines2函数。它通过参数CV_HOUGH_PROBABILISTIC,最终调用了icvHoughLinesProbabilistic函数:
- static void
- icvHoughLinesProbabilistic( CvMat* image,
- float rho, float theta, int threshold,
- int lineLength, int lineGap,
- CvSeq *lines, int linesMax )
- {
-
- cv::Mat accum, mask;
- cv::vector<float> trigtab;
-
- cv::MemStorage storage(cvCreateMemStorage(0));
-
- CvSeq* seq;
- CvSeqWriter writer;
- int width, height;
- int numangle, numrho;
- float ang;
- int r, n, count;
- CvPoint pt;
- float irho = 1 / rho;
- CvRNG rng = cvRNG(-1);
- const float* ttab;
- uchar* mdata0;
-
- CV_Assert( CV_IS_MAT(image) && CV_MAT_TYPE(image->type) == CV_8UC1 );
-
- width = image->cols;
- height = image->rows;
-
- numangle = cvRound(CV_PI / theta);
- numrho = cvRound(((width + height) * 2 + 1) / rho);
-
- accum.create( numangle, numrho, CV_32SC1 );
-
- mask.create( height, width, CV_8UC1 );
-
- trigtab.resize(numangle*2);
-
- accum = cv::Scalar(0);
-
- for( ang = 0, n = 0; n < numangle; ang += theta, n++ )
- {
- trigtab[n*2] = (float)(cos(ang) * irho);
- trigtab[n*2+1] = (float)(sin(ang) * irho);
- }
-
- ttab = &trigtab[0];
- mdata0 = mask.data;
-
- cvStartWriteSeq( CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), storage, &writer );
-
-
-
- for( pt.y = 0, count = 0; pt.y < height; pt.y++ )
- {
-
- const uchar* data = image->data.ptr + pt.y*image->step;
- uchar* mdata = mdata0 + pt.y*width;
- for( pt.x = 0; pt.x < width; pt.x++ )
- {
- if( data[pt.x] )
- {
- mdata[pt.x] = (uchar)1;
- CV_WRITE_SEQ_ELEM( pt, writer ); 把该坐标位置写入序列
- }
- else
- mdata[pt.x] = 0;
- }
- }
-
- seq = cvEndWriteSeq( &writer );
- count = seq->total;
-
-
-
- for( ; count > 0; count-- )
- {
-
-
- int idx = cvRandInt(&rng) % count;
-
- int max_val = threshold-1, max_n = 0;
-
- CvPoint* point = (CvPoint*)cvGetSeqElem( seq, idx );
-
- CvPoint line_end[2] = {{0,0}, {0,0}};
- float a, b;
-
- int* adata = (int*)accum.data;
- int i, j, k, x0, y0, dx0, dy0, xflag;
- int good_line;
- const int shift = 16;
-
- i = point->y;
- j = point->x;
-
-
-
- *point = *(CvPoint*)cvGetSeqElem( seq, count-1 );
-
-
-
-
- if( !mdata0[i*width + j] )
- continue;
-
-
-
- for( n = 0; n < numangle; n++, adata += numrho )
- {
-
- r = cvRound( j * ttab[n*2] + i * ttab[n*2+1] );
- r += (numrho - 1) / 2;
-
- int val = ++adata[r];
-
- if( max_val < val )
- {
- max_val = val;
- max_n = n;
- }
- }
-
-
-
- if( max_val < threshold )
- continue;
-
-
-
-
- a = -ttab[max_n*2+1];
- b = ttab[max_n*2];
-
- x0 = j;
- y0 = i;
-
- if( fabs(a) > fabs(b) )
- {
- xflag = 1;
-
- dx0 = a > 0 ? 1 : -1;
- dy0 = cvRound( b*(1 << shift)/fabs(a) );
-
- y0 = (y0 << shift) + (1 << (shift-1));
- }
- else
- {
- xflag = 0;
-
- dy0 = b > 0 ? 1 : -1;
- dx0 = cvRound( a*(1 << shift)/fabs(b) );
-
- x0 = (x0 << shift) + (1 << (shift-1));
- }
-
- for( k = 0; k < 2; k++ )
- {
-
- int gap = 0, x = x0, y = y0, dx = dx0, dy = dy0;
-
- if( k > 0 )
- dx = -dx, dy = -dy;
-
-
-
-
- for( ;; x += dx, y += dy )
- {
- uchar* mdata;
- int i1, j1;
-
- if( xflag )
- {
- j1 = x;
- i1 = y >> shift;
- }
- else
- {
- j1 = x >> shift;
- i1 = y;
- }
-
- if( j1 < 0 || j1 >= width || i1 < 0 || i1 >= height )
- break;
-
- mdata = mdata0 + i1*width + j1;
-
-
-
-
-
-
- if( *mdata )
- {
- gap = 0;
-
- line_end[k].y = i1;
- line_end[k].x = j1;
- }
-
- else if( ++gap > lineGap )
- break;
- }
- }
-
-
- good_line = abs(line_end[1].x - line_end[0].x) >= lineLength ||
- abs(line_end[1].y - line_end[0].y) >= lineLength;
-
- for( k = 0; k < 2; k++ )
- {
- int x = x0, y = y0, dx = dx0, dy = dy0;
-
- if( k > 0 )
- dx = -dx, dy = -dy;
-
-
-
- for( ;; x += dx, y += dy )
- {
- uchar* mdata;
- int i1, j1;
-
- if( xflag )
- {
- j1 = x;
- i1 = y >> shift;
- }
- else
- {
- j1 = x >> shift;
- i1 = y;
- }
-
- mdata = mdata0 + i1*width + j1;
-
-
-
-
-
- if( *mdata )
- {
-
- if( good_line )
- {
-
- adata = (int*)accum.data;
- for( n = 0; n < numangle; n++, adata += numrho )
- {
- r = cvRound( j1 * ttab[n*2] + i1 * ttab[n*2+1] );
- r += (numrho - 1) / 2;
- adata[r]--;
- }
- }
-
- *mdata = 0;
- }
-
- if( i1 == line_end[k].y && j1 == line_end[k].x )
- break;
- }
- }
-
- if( good_line )
- {
- CvRect lr = { line_end[0].x, line_end[0].y, line_end[1].x, line_end[1].y };
-
- cvSeqPush( lines, &lr );
-
- if( lines->total >= linesMax )
- return;
- }
- }
- }
下面就给出应用HoughLinesP函数检测直线段的应用程序:
- #include "opencv2/core/core.hpp"
- #include "opencv2/highgui/highgui.hpp"
- #include "opencv2/imgproc/imgproc.hpp"
-
- #include <iostream>
- using namespace cv;
- using namespace std;
-
- int main( int argc, char** argv )
- {
- Mat src, edge,color_edge;
- src=imread("building.jpg");
- if( !src.data )
- return -1;
-
- Canny(src,edge,50,200,3);
- cvtColor( edge, color_edge, CV_GRAY2BGR );
- vector<Vec4i> lines;
- HoughLinesP(edge, lines, 1, CV_PI/180, 80, 30, 10 );
- for( size_t i = 0; i < lines.size(); i++ )
- {
- Vec4i l = lines[i];
- line( color_edge, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 2);
- }
-
- namedWindow( "lines", CV_WINDOW_AUTOSIZE );
- imshow( "lines", color_edge );
- waitKey(0);
-
- return 0;
- }
下图为输出的图像: