Hough变换原理及应用

来源:互联网 发布:售电软件哪家好 编辑:程序博客网 时间:2024/05/16 10:13

霍夫变换原理介绍可参见冈萨勒斯的《数字图像处理》第二版476页。

在xy平面上的一点(x0,y0)要转换为极坐标下的(θ,ρ):


x0 = ρcosθ

y0 = ρsinθ

那么

x0cosθ = ρcosθcosθ

y0sinθ = ρsinθsinθ

可得

x0cosθ + y0sinθ = ρ

那么xy坐标平面到极坐标θρ平面的转换公式为:

xcosθ + ysinθ = ρ

可以看出xy平面上的一点在极坐标θρ平面上的投影为一条正弦曲线,那么n个点对应n条正弦曲线,这n条曲线如果有m条曲线线交于一点(θ0,ρ0),那么这m条曲线对应在xy平面下的m个点就在同一直线上,而θ0就是这条直线的法线与x轴的夹角,ρ0为原点到直线的距离。

如果理解了霍夫变换的原理,接下来就可以看一下变换源码了。

下面是OpenCV自带的标准霍夫变换函数:

static voidicvHoughLinesStandard( const CvMat* img, float rho, float theta,                       int threshold, CvSeq *lines, int linesMax ){    cv::AutoBuffer<int> _accum, _sort_buf;    cv::AutoBuffer<float> _tabSin, _tabCos;    const uchar* image;    int step, width, height;    int numangle, numrho;    int total = 0;    int i, j;    float irho = 1 / rho;    double scale;    CV_Assert( CV_IS_MAT(img) && CV_MAT_TYPE(img->type) == CV_8UC1 );    image = img->data.ptr;    step = img->step;    width = img->cols;    height = img->rows;    numangle = cvRound(CV_PI / theta);    numrho = cvRound(((width + height) * 2 + 1) / rho);    _accum.allocate((numangle+2) * (numrho+2));    _sort_buf.allocate(numangle * numrho);    _tabSin.allocate(numangle);    _tabCos.allocate(numangle);    int *accum = _accum, *sort_buf = _sort_buf;    float *tabSin = _tabSin, *tabCos = _tabCos;    memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) );    float ang = 0;    for(int n = 0; n < numangle; ang += theta, n++ )    {        tabSin[n] = (float)(sin((double)ang) * irho);        tabCos[n] = (float)(cos((double)ang) * irho);    }    // stage 1. fill accumulator    for( i = 0; i < height; i++ )        for( j = 0; j < width; j++ )        {            if( image[i * step + j] != 0 )                for(int n = 0; n < numangle; n++ )                {                    int r = cvRound( j * tabCos[n] + i * tabSin[n] );                    r += (numrho - 1) / 2;                    accum[(n+1) * (numrho+2) + r+1]++;                }        }    // stage 2. find local maximums    for(int r = 0; r < numrho; r++ )        for(int n = 0; n < numangle; n++ )        {            int base = (n+1) * (numrho+2) + r+1;            if( accum[base] > threshold &&                accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&                accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )                sort_buf[total++] = base;        }    // stage 3. sort the detected lines by accumulator value    icvHoughSortDescent32s( sort_buf, total, accum );    // stage 4. store the first min(total,linesMax) lines to the output buffer    linesMax = MIN(linesMax, total);    scale = 1./(numrho+2);    for( i = 0; i < linesMax; i++ )    {        CvLinePolar line;        int idx = sort_buf[i];        int n = cvFloor(idx*scale) - 1;        int r = idx - (n+1)*(numrho+2) - 1;        line.rho = (r - (numrho - 1)*0.5f) * rho;        line.angle = n * theta;        cvSeqPush( lines, &line );    }}

步骤为:

1、将θ离散值的sin、cos的计算结果存在数组中,便于下一步使用。

2、将原图中灰度值不为0的点变换到θρ空间的正弦曲线,而θρ空间相当于一个累加器。

3、将累加器的值进行排序,使用的是最大邻域值法,可以看出邻域很小,邻域小计算速度就快。

4、输出累加器中值最大的几个值。

参见上面的源码写出自己的代码与之比较。

原图:


源码:

<pre name="code" class="cpp">#include "stdafx.h"#include <cv.h>#include <opencv2\highgui\highgui.hpp>using namespace cv;int _tmain(int argc, _TCHAR* argv[]){Mat src = imread("E:\\VC++Projects\\road.jpg");Mat gray = Mat::zeros(src.size(),CV_8UC1);cvtColor(src, gray, CV_BGR2GRAY);// 求得x和y方向的一阶微分Mat sobelx;Mat sobely;Sobel(gray, sobelx, CV_32F, 1, 0, 3);Sobel(gray, sobely, CV_32F, 0, 1, 3);gray.release();// 求得梯度和方向Mat norm;Mat dir;cartToPolar(sobelx, sobely, norm, dir);sobelx.release();sobely.release();// 转换为8位单通道图像double normMax;minMaxLoc(norm, NULL, &normMax);Mat grad;norm.convertTo(grad, CV_8UC1, 255.0/normMax, 0);// OpenCV自带大津阈值函数处理Mat cvOstu;threshold(grad, cvOstu, 0, 255, CV_THRESH_BINARY|CV_THRESH_OTSU);norm.release();dir.release();grad.release();//vector<Vec2f> lines;//HoughLines( cvOstu, lines, 1, CV_PI/180, 100 );//for( size_t i = 0; i < 10/*lines.size()*/; i++ )//{//float rho = lines[i][0];//float theta = lines[i][1];//double a = cos(theta), b = sin(theta);//double x0 = a*rho, y0 = b*rho;//Point pt1(cvRound(x0 + 1000*(-b)),//cvRound(y0 + 1000*(a)));//Point pt2(cvRound(x0 - 1000*(-b)),//cvRound(y0 - 1000*(a)));//line( src, pt1, pt2, Scalar(0,0,255), 1, 8 );//}vector<Vec4i> lines2;HoughLinesP( cvOstu, lines2, 1, CV_PI/180, 80, 30, 10 );for( size_t i = 0; i < 10/*lines2.size()*/; i++ ){line( src, Point(lines2[i][0], lines2[i][1]),Point(lines2[i][2], lines2[i][3]), Scalar(0,255,0), 1, 8 );}namedWindow("src");imshow("src", src);namedWindow("binary");imshow("binary", cvOstu);waitKey(0);return 0;}

使用标准霍夫变换结果:



使用概率霍夫变换的结果:


看到上面的结果,会发现虽然找出了直线,但是可能结果并不是想要的,比如在几条直线过于接近(θ,ρ的差值很小)的只显示一条。那么必须扩大最大值的搜素邻域,当然扩大搜索邻域会增加搜索时间。根据自己的要求,采用合适的方法!


0 0
原创粉丝点击