概率霍夫线变换的原理详解

C佳 2014-04-14 09:16:50

请问谁能告诉我概率霍夫线变换的原理?我在网上搜了很多文章都只着重介绍传统霍夫线变换原理,概率霍夫线变换只讲了一点,还是看不明白。虽然有代码,但我是初学者,还是看不懂其中的原理。请高手指点!
附代码:
// 此函数用来对二值图像进行直线检测
/*******************************************************

此函数的功能为对二值图像用hough变换进行直线检测
它是由OpenCV中的源码改编成的,方法为随机hough变换的改进

参数意义:
img —— 输入二值图像的指针
threshold —— 一条直线上的点数和
lineLength —— 用定义规则搜寻的直线长度
lineGap —— 间隔规则的点数长度
lineMax —— 检测直线的条数极值
lines_total —— 检测出来的直线条数

*******************************************************/
int * HoughProbabalistic( CvArr* img, int threshold, int lineLength,
int lineGap, int lineMax, int * lines_total )
{

int* result = 0;

int * lines = new int [lineMax*4];

CvMat stub, *image = (CvMat*)img;
image = cvGetMat( image, &stub ); // 将输入的Iplimage结构转为CvMat


CvMat* accum = 0;
CvMat* mask = 0;

float rho = 1;
double theta = 3.1415926535 / 180.0;

int width = image->cols;
int height = image->rows;
const int numangle = 180; // 定义角度数
const int numrho = ((width + height) * 2 + 1); // 定义hough图像中的长度;
double ang;
int r, n, count;
int pt[2]; // 表示一个点,其中pt[0]表示x坐标,pt[1]表示y坐标
const float* ttab;
uchar* mdata0;


accum = cvCreateMat( numangle, numrho, CV_32SC1 ); // 用来存储计算hough的图像
mask = cvCreateMat( height, width, CV_8UC1 ); // 用来标识图像中的非零点(边缘)
float trigtab[numangle*2]; // 用来存储 cos&sin 0-180
cvZero( accum ); // 把accum初始化为0


for( ang = 0, n = 0; n < numangle; ang += theta, n++ )
{
trigtab[n*2] = (float)(cos(ang) * rho);
trigtab[n*2+1] = (float)(sin(ang) * rho);
}
ttab = trigtab;
mdata0 = mask->data.ptr;
*lines_total = 0;


// stage 1. 收集所有的非零点,将它们存入数组中,并在mask中标记为1
for( pt[1] = 0, count = 0; pt[1] < height; pt[1]++ )
{
const uchar* data = image->data.ptr + pt[1]*image->step;
uchar* mdata = mdata0 + pt[1]*width;
for( pt[0] = 0; pt[0] < width; pt[0]++ )
{
if( data[pt[0]] )
{
mdata[pt[0]] = (uchar)1;
count++;
}
else
mdata[pt[0]] = 0;
}
}

int *seq = new int [count*2];

for( pt[1] = 0, count = 0; pt[1] < height; pt[1]++ )
{
const uchar* data = image->data.ptr + pt[1]*image->step;
for( pt[0] = 0; pt[0] < width; pt[0]++ )
{
if( data[pt[0]] )
{
seq[count*2] = pt[0];
seq[count*2+1] = pt[1];
count++;
}
}
}


// stage 2. 以随机顺序处理所有的非零点
for( ; count > 0; count-- )
{
// 选择随机点
int idx = rand();
idx = idx % count;
int max_val = threshold-1, max_n = 0;
////////////////////////////////////////
int pt[2];
pt[0] = seq[idx*2];
pt[1] = seq[idx*2 + 1];
int line_end[2][2] = {{0,0}, {0,0}}; // 表示直线两端点的坐标,如line_end[0][1]表示第一个点的y坐标
float a, b;
int* adata = accum->data.i;
int i, j, k, x0, y0, dx0, dy0, xflag;
int good_line;
const int shift = 16;

i = pt[1];
j = pt[0];

// 通过重新赋值删除此点
pt[0] = seq[ count*2 - 2 ];
pt[1] = seq[ count*2 - 1 ];

// 检查此点是否已经被排除 (例如属于别的直线)
if( !mdata0[i*width + j] )
continue;

// 对此点逐行更新累加器, 看能否超过阈值
for( n = 0; n < numangle; n++, adata += numrho )
{
r = (int) floor( j * ttab[n*2] + i * ttab[n*2+1] + 0.5 );
r += (numrho - 1) / 2; //所有r都加上图像的行数和列数,让r的结果永远为正
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]; // -sin(max_n)
b = ttab[max_n*2]; // cos(max_n)
x0 = j;
y0 = i;
if( fabs(a) > fabs(b) ) /* 在方向上要摒除斜率不近似垂直的线 */
{ /* 在方向上要摒除斜率不近似垂直的线 */
/* 在方向上要摒除斜率不近似垂直的线 */
continue;
/*
xflag = 1;
dx0 = a > 0 ? 1 : -1;
dy0 = (int) floor( b*(1 << shift)/fabs(a) + 0.5 ); // <<为左移符号,相当于乘以2的n(此处为16)次方
y0 = (y0 << shift) + (1 << (shift-1));
*/
}
else
{
xflag = 0;
dy0 = b > 0 ? 1 : -1;
dx0 = (int) floor( a*(1 << shift)/fabs(b) + 0.5 );
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;
int base = i1*width + j1;

// 对于非零点:
// 升级线的端点,
// 重令gap为0
if( mdata[base] )
{
gap = 0;
line_end[k][1] = i1;
line_end[k][0] = j1;
}
else if( mdata[base+1] || mdata[base-1] )
{
gap = 0;
line_end[k][1] = i1;
line_end[k][0] = j1;
}
else if( ++gap > lineGap ) /* 对于零点,要考虑双线或三线的共有点情况以得到更准确的线 */
break; /* 对于零点,要考虑双线或三线的共有点情况以得到更准确的线 */
/* 对于零点,要考虑双线或三线的共有点情况以得到更准确的线 */
}
}

// 判断此线是否是满足长度lineLength的直线
good_line = abs(line_end[1][0] - line_end[0][0]) >= lineLength ||
abs(line_end[1][1] - line_end[0][1]) >= lineLength;




// 重新遍历此线来:
// 置线上的点mask为0
// 对good_line来减去此线在累加器上的作用
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 = accum->data.i;
for( n = 0; n < numangle; n++, adata += numrho )
{
r = (int) floor( j1 * ttab[n*2] + i1 * ttab[n*2+1] + 0.5 );
r += (numrho - 1) / 2;
adata[r]--;
}
// *mdata = 0;
}
*mdata = 0;
}


if( *(mdata-1) )
{
if( good_line )
{
*(mdata-1) = 0;
}
}

if( *(mdata+1) )
{
if( good_line )
{
*(mdata+1) = 0;
}
}

if( i1 == line_end[k][1] && j1 == line_end[k][0] )
break;
}
}



// 把此线端点存入lines结构

if( good_line && fabs( line_end[0][1] - line_end[1][1] ) / fabs(line_end[0][0] - line_end[1][0] + 0.5 ) >= 3 )
{
lines[ lines_total[0]*4 ] = line_end[0][0];
lines[ lines_total[0]*4 + 1 ] = line_end[0][1];
lines[ lines_total[0]*4 + 2 ] = line_end[1][0];
lines[ lines_total[0]*4 + 3 ] = line_end[1][1];
*lines_total += 1;
if( *lines_total >= lineMax )
{
// AfxGetMainWnd() ->SendMessage(WM_CLOSE,0,0);
// AfxGetMainWnd()->PostMessage(WM_COMMAND,ID_APP_EXIT) ;
exit(1);
}
}
}

// cvReleaseMat( &image );
cvReleaseMat( &accum );
cvReleaseMat( &mask );
delete []seq;
result = lines;

return result;

}
...全文
5116 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
shiter 2016-06-10
  • 打赏
  • 举报
回复
1)随机获取边缘图片上的前景点,映射到级坐标系画曲线; 2)当极坐标系里面有交点达到最小投票数,将该点对应x-y坐标系的直线L找出来; 3)搜索边缘图片上前景点,在直线L上的点(且点与点之间距离小于maxLineGap的)连成线段,然后这些点全部删除,并且记录该线段的参数,就是起始点和终止点啦~~~~~~~~~~~~~~~~~~~(当然这里线段长度要满足最小长度的,否则就不用记录了) 4)重复1),2),3) 其使用方法见代码:
yanjun4376 2016-06-09
  • 打赏
  • 举报
回复
为什么要移位啊 ,能解释下吗
NewThinker_wei 2015-04-18
  • 打赏
  • 举报
回复
不错,大致流程清楚了,mark一下,有功夫了再研究
大螃蟹吖 2014-06-11
  • 打赏
  • 举报
回复
点集就是图像空间中的边缘点,代码中mask
大螃蟹吖 2014-06-11
  • 打赏
  • 举报
回复
概率霍夫变换的步骤: 1。从点集中随机选取一个像素点,对应的累加器加1 2。从点集中删除该点 3。更新累加器 4。若更新之后的累加器值大于阈值(检测到直线),则删除集合中位于该直线上的所有点 重复以上步骤,直到点集为空

5,530

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 模式及实现
社区管理员
  • 模式及实现社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧