您的位置:首页 > 其它

数字图像处理常用算法实现-霍夫线检测

2018-03-23 15:33 776 查看

霍夫线检测原理:

笛卡尔坐标的点 <=> 极坐标的曲线

所谓极坐标平面化是指, 将ρ-θ的关系像x-y那样在平面内展开。

公式推导: x-y坐标中的点(x0, y0), 代入极坐标ρ-θ中得



ρ,θ就是一对hough空间的变量表示。若将ρ,θ看成直角坐标空间,一个点(x0, y0)就是一个关于ρ,θ的正弦曲线。同样,直线上的其他点(Xn,Yn)也会构成一组关于ρ,θ的正弦曲线,这样势必存在一个关于ρ,θ相交(即垂直点(r,θ))。于是乎, 一条直线能够通过在极坐标下寻找交于一点的曲线数量来检测,如果越多曲线交于一点,就意味着这个交点表示的直线由更多的点组成。我们可以通过设置直线上点的阈值来定义多少条曲线交于一点我们才认为检测到了一条直线。



template<typename T>
int myRound(T value)
{
return (int)(value + (value >= 0 ? 0.5 : -0.5));
}
/*
函数名:doHoughLinesStandard
函数功能:标准霍夫直线检测的源码
函数参数:
src-->输入的二值图像   lines-->输出的线的坐标(r,θ)
rho-->极坐标r的步长    theta-->极坐标θ的步长
threshold-->交点个数阈值 linesMax-->线条数的最大值
备注:
关于极坐标的维度方面,标准的维度空间范围:θ∈[-π/2,π/2],r∈[-D,D],D=(w^2+h^2)^(1/2).可以参见《数字图像处理第三版p755》
这里所映射的极坐标维度范围:θ∈[0,181],有效为[1,180],r∈[0,(2D+1)+1],有效为[1,(2D+1)],D=(w + h).
*/
void doHoughLinesStandard(Mat &src, vector<Vec2f>&lines,float rho, float theta, int threshold,int linesMax)
{
float irho = 1 / rho;
const int width = src.cols;
const int height = src.rows;
int i, j, n, r;
//将r,theta离散化,形成离散化的hough空间,类似一个mat矩阵/图像,用于统计角点的个数.
//角度的离散值个数(180),θ∈[0,180°].Δθ = 1.
int numangle = (int)(CV_PI / theta);
//numrho本来的范围是[-D,+D],这里放大(D=(w + h)).且有效范围从1开始,故扩展至[1,2D+1].Δr = 1.
int numrho = (int)(((width + height) * 2 + 1) / rho);//这里+1是因为要隔开极坐标空间的左右部分.
//极坐标空间的上下空留一行左右空留一列,为后面的4邻域比较做准备.
int **_accum = new int*[(numangle + 2)];
for (i = 0; i < (numangle + 2); i++) {
_accum[i] = new int[(numrho + 2)];
memset(_accum[i], 0x00, sizeof(int)*(numrho + 2));
}

vector<int> _sort_buf;
//正余弦参数表,以后通过查表的方式来获取值,一种加速手段,以空间换时间。
float* _tabSin = new float[numangle];
float* _tabCos = new float[numangle];
float ang = 0.0f;
for (n = 0; n<numangle; ang += theta, n++) {
_tabSin
= sin(((double)ang)*irho);
_tabCos
= cos(((double)ang)*irho);
}
//step1:fill accumulator
for (i = 0; i<src.rows; i++) {
for (j = 0; j<src.cols; j++) {
//将每个非零点转换为霍夫空间的离散正弦曲线,并统计。
if (src.data[i*((int)(src.step)) + j] != 0) {
for (n = 0; n<numangle; n++) {
r = myRound(j*_tabCos
+ i*_tabSin
);
r += (numrho - 1) / 2;//r的范围[1,(2D+1)]决定这里需要+D
_accum[n + 1][r + 1]++;
}
}
}
}
//stp2:find local maximums
/*霍夫空间,局部最大点,采用四邻域判断、比较,如果不判断局部最大值,同时选用次大值和最大值,
可能会是两个相邻的直线,但实际上就是一条直线。选用最大值,去除离散的近似计算带来的误差,合并近似曲线。
*/
for (r = 0; r<numrho; r++) {
for (n = 0; n<numangle; n++) {
if (_accum[(n + 1)][r + 1] > threshold &&
_accum[(n + 1)][r + 1] > _accum[(n + 1)][r] && _accum[(n + 1)][r + 1] >= _accum[(n + 1)][r + 2] &&
_accum[(n + 1)][r + 1] > _accum[(n)][r + 1] && _accum[(n + 1)][r + 1] >= _accum[(n + 2)][r + 1]) {
_sort_buf.push_back(n + 1); _sort_buf.push_back(r + 1);
}
}
}
//依据霍夫空间分辨率,计算直线的实际r,theta参数
for (i = 0; i<_sort_buf.size() && i<linesMax; i += 2) {
int nx = _sort_buf[i]-1;//得到θ的坐标,单位为度.-1的原因是刚开始平移了1个单位,下同.
int rx = _sort_buf[i + 1]-1;
float rho_res = (rx - (numrho - 1)*0.5f)*rho;//r恢复原来的值须-D.
float angle_res =  nx*theta;//将度转化为弧度
lines.push_back(Vec2f(rho_res, angle_res));
}

if (_accum) {
for (i = 0; i < numangle; i++) {
if (_accum[i]) {
delete[]_accum[i]; _accum[i] = NULL;
}
}
delete[] _accum; _accum = NULL;
}
if (_tabSin) {delete[] _tabSin; _tabSin= NULL;}
if (_tabCos) { delete[] _tabCos; _tabCos = NULL; }
}

void xxHoughTransform()
{
Mat src = imread("hb.jpg", IMREAD_ANYCOLOR | IMREAD_ANYDEPTH);//,IMREAD_GRAYSCALE
Mat midImage, dstImage;
Canny(src, midImage, 50, 180);
cvtColor(midImage, dstImage, CV_GRAY2BGR);
vector<Vec2f> lines;
doHoughLinesStandard(midImage, lines, 1.0, CV_PI / 180,52, 100);
//HoughLines(midImage, lines, 1, CV_PI / 180,50);
cout << lines.size() << endl;
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0], theta = lines[i][1];
cout << rho << "<-->" << theta << endl;
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));
line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, CV_AA);
}
imshow("src000", src);
imshow("canny000", midImage);
imshow("dst000", dstImage);
waitKey();
}


自测效果图如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: