霍夫变换检测直线原理及C/C++实现

霍夫变换检测直线的原理及C/C++实现

  • 霍夫变换原理概述
  • 霍夫直线检测原理
  • C/C++代码及细节提要

霍夫变换原理概述

在图像处理中,霍夫变换是经典算法之一,常被用来检测图像中的直线,圆等特征图形。本节将讨论霍夫变换的理论基础。

1、基础理论

假设有一个样本集合
{ ( x i , y j ) } \{(x_i,y_j)\} {(xi,yj)}
我们想从这个样本集合中提取出符合一定条件的子集(称为目标集合)
{ ( x i , y j ) ∣ f ( x , y ) = s } \{(x_i,y_j)|f(x,y)=s\} {(xi,yj)f(x,y)=s}
这个子集中的任意一个元素均满足 f ( x , y ) = s f(x,y)=s f(x,y)=s的条件,即集合中所有元素经 f ( ) f() f()运算后都能得到相同的结果 s s s,可以理解为这个集合中所有的元素都拥有相同的特征 s s s

有了特征 s s s和转换函数 f ( ) f() f()后,从样本集合中提取出目标集合就变得简单了。
首先遍历样本集合,将样本元素代入转换函数,得到一个中间值 p p p
p = f ( x i , y j ) p=f(x_i,y_j) p=f(xi,yj)
然后对比 p p p s s s,如果有
p = s p=s p=s
则将 ( x i , y j ) (x_i,y_j) (xi,yj)收入到目标集合中。
当完成样本集合的遍历后,统计目标集合中元素的个数,如果目标集合中元素个数超过一定数量(减小干扰)则可以判定为样本集合中存在要提取的目标集合。

2、拓展

上文的方法只能提取集合中符合 f ( x , y ) = s f(x,y)=s f(x,y)=s这一个目标集合,但在实际应用中我们往往知道样本集合中一定有某(几)个特征集合,也知道它们的特征函数 f ( ) f() f()
{ ( x i , y j ) ∣ f ( x , y ) = X } \{(x_i,y_j)|f(x,y)=X\} {(xi,yj)f(x,y)=X}
我们希望的是能得到这些集合的特征参数 X X X,例如下面这张图片,已经知道它里面一定有一条直线,现在希望的是找到能表征这条直线的参数。
在这里插入图片描述
那么如何寻找特征参数 X X X
可以建立一个所有 X X X可能取值的集合
{ X 1 , X 2 . . . X n } \{X_1,X_2...X_n\} {X1,X2...Xn}
再为这个集合中的每一个元素建立一个计分板
{ N X 1 , N X 2 . . . N X n } \{N_{X1},N_{X2}...N_{Xn}\} {NX1,NX2...NXn}
然后遍历样本集合,将样本集合中的每一个元素代入特征函数得到一个特征值
f ( x i , y j ) = X m f(x_i,y_j)=X_m f(xi,yj)=Xm
X m ∈ { X 1 , X 2 . . . X n } X_m\in\{X_1,X_2...X_n\} Xm{X1,X2...Xn}
并让 X m X_m Xm的计分加一,当结束了样本集合的遍历后,统计计分板,假如 N X k N_{Xk} NXk大于某个阈值,即可认为样本集合中存在满足特征函数 f ( ) f() f()的子集,且其特征值为 X k X_k Xk。这样一来就能满足提取集合特征值的要求了。

霍夫直线检测原理

  • 本节以下图为测试图片
    在这里插入图片描述
1、原理描述

从上文论述中可以总结出霍夫变换检测直线的主要步骤。

  • 获取样本集合,在图像中就是像素点的坐标集合,测试图片中线段颜色为黑色,所以第一步要得到测试图片中黑色像素的坐标集合。
  • 寻找直线的特征函数,直线的方程形式有很多,例如常见的点斜式,但点斜式存在斜率无穷大的情况,所以一般选择直线的极坐标方程
    p = x ∗ c o s ( θ ) + y ∗ s i n ( θ ) p=x*cos(\theta)+y*sin(\theta) p=xcos(θ)+ysin(θ)
    θ 是 弧 度 \theta是弧度 θ
    一组极坐标参数 ( p , θ ) (p,\theta) (p,θ)可以唯一的表示一条直线,如下图
    在这里插入图片描述
  • 建立计分板并初始化
  • 遍历像素集合,将像素坐标 ( x , y ) (x,y) (x,y)代入极坐标方程中得到参数 ( p , θ ) (p,\theta) (p,θ)并投票计分
  • 遍历计分板,找出分数大于某个阈值的 ( p , θ ) (p,\theta) (p,θ)对,结果即检测到的直线的参数,可使用这些参数去在数学概念上还原这些直线。
2、C/C++代码及细节提要

下面将展示一个简单的霍夫变换实现直线检测的算法,图片读取和显示使用了openCV3的接口,算法中使用了openCV的Mat结构,如有移植需要可自行更改。
程序中需要注意的地方:

  • 为了方便遍历,使用角度 a n g l e angle angle而不是弧度 θ \theta θ,在做运算时需要转化
  • 使用二维数组作为计分板, p p p a n g l e angle angle作为索引,由于极角和极径有正有负,所以需要进行范围调整和对 p p p取绝对值
    极 角 : [ 0 , 180 ] 极角:[0,180] :[0,180]
    极 径 : [ 0 , 2 ∗ l ] 极径:[0,2*l] :[0,2l]
    l 为 图 片 对 角 线 长 度 l为图片对角线长度 l线

//直线参数
typedef struct hline_t
{
	int p;//极径
	int theta;//极角,角度
}hline;


void hough_line_v(Mat &img,int threshold, hline* lines,int *num)
{
	int row, col;
	int i,k;
	//参数空间的参数极角angle(角度),极径p;
	int angle,p;

	//累加器
	int **socboard;
	int *buf;
	int w, h;
	w = img.cols;
	h = img.rows;
	int Size;
	int offset;

//申请累加器空间并初始化
	Size= w*w + h*h;
	Size = 2*sqrt(Size)+100;
	offset = Size / 2;
	socboard = (int **)malloc(Size * sizeof(int*));
	if (!socboard)
	{
		printf("mem err\n");
		return;
	}
	
	for (i = 0; i < Size; i++)
	{
		socboard[i] = (int *)malloc(181 * sizeof(int));
		if (socboard[i] == NULL)
		{
			printf("buf err\n");
			return;
		}
		memset(socboard[i], 0, 181 * sizeof(int));
	}

	//遍历图像并投票
	Vec3b src_data;
	p = 0;
	for (row = 0; row< img.rows; row++)
	{
		for (col = 0; col< img.cols; col++)
		{
		//获取像素点
			src_data = img.at <Vec3b>(row, col);

			//检测黑线
			if (src_data[0] == 0 && src_data[1] == 0 && src_data[2] == 0)
			{
				
				for (angle = 0; angle < 181; angle++)
				{
					p = col * cos(angle * PI / 180.0) + row * sin(angle * PI / 180.0)+offset;
					
					//错误处理
					if (p <0)
					{
						printf("at (%d,%d),angle:%d,p:%d\n", col, row, angle, p);
						printf("warrning!");
						printf("size:%d\n", Size/2);
						continue;
					}
					//投票计分
					socboard[p][angle]++;
					
				}
			}	
		}
	}

	//遍历计分板,选出符合阈值条件的直线
	int count = 0;
	int Max = 0;
	int kp, kt;
	kp = 0;
	kt = 0;
	for (i = 0; i < Size; i++)//p
	{
		for (k = 0; k < 181; k++)//angle
		{
			if (socboard[i][k] > Max)
			{
				Max = socboard[i][k];
				kp = i-offset;
				kt = k;
			}
				
			if (socboard[i][k] >= threshold)
			{
				printf("count:%d\n",count);
				lines[count].p = i-Size/2;
				lines[count].theta = k;
				count++;
			}
		}
	}
	*num = count;
	//释放资源
	for (int e = 0; e < Size; e++)
	{
		free(socboard[e]);
	}
	free(socboard);
}

  • 运行结果
    运行结果

  • 如有错误之处,欢迎指出修正
  • 9
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
霍夫变换直线检测是一种常用的图像处理算法,用检测图像中的直线。下面是一个使用C语言实现的简单霍夫变换直线检测的示例: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define MAX_VOTES 1000 typedef struct { int x; int y; } Point; typedef struct { float rho; float theta; } Line; void houghTransform(int width, int height, unsigned char* image, int threshold, Line* lines, int* numLines) { int maxRho = sqrt(width * width + height * height); int numThetas = 180; int* accumulator = (int*)calloc(maxRho * numThetas, sizeof(int)); // Voting for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (image[y * width + x] > 0) { for (int theta = 0; theta < numThetas; theta++) { float angle = theta * M_PI / numThetas; float rho = x * cos(angle) + y * sin(angle); int rhoIndex = (int)(rho + maxRho / 2); accumulator[rhoIndex * numThetas + theta]++; } } } } // Finding lines *numLines = 0; for (int rhoIndex = 0; rhoIndex < maxRho; rhoIndex++) { for (int theta = 0; theta < numThetas; theta++) { if (accumulator[rhoIndex * numThetas + theta] > threshold) { lines[*numLines].rho = rhoIndex - maxRho / 2; lines[*numLines].theta = theta * M_PI / numThetas; (*numLines)++; if (*numLines >= MAX_VOTES) { return; } } } } free(accumulator); } int main() { int width = 640; int height = 480; unsigned char* image = (unsigned char*)malloc(width * height * sizeof(unsigned char)); // Load image data here Line lines[MAX_VOTES]; int numLines = 0; int threshold = 100; houghTransform(width, height, image, threshold, lines, &numLines); printf("Detected %d lines:\n", numLines); for (int i = 0; i < numLines; i++) { printf("Line %d: rho = %.2f, theta = %.2f\n", i+1, lines[i].rho, lines[i].theta); } free(image); return 0; } ``` 这个程序实现了一个简单的霍夫变换直线检测算法。它首先对输入的图像进行霍夫变换,然后根据设定的阈值找到符合条件的直线,并输出检测到的直线的参数(rho和theta)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值