图像处理之霍夫变换(直线检测算法)
霍夫变换是图像变换中的经典手段之一,主要用来从图像中分离出具有某种相同特征的几何
形状(如,直线,圆等)。霍夫变换寻找直线与圆的方法相比与其它方法可以更好的减少噪
声干扰。经典的霍夫变换常用来检测直线,圆,椭圆等。
霍夫变换算法思想:
以直线检测为例,每个像素坐标点经过变换都变成都直线特质有贡献的统一度量,一个简单
的例子如下:一条直线在图像中是一系列离散点的集合,通过一个直线的离散极坐标公式,
可以表达出直线的离散点几何等式如下:
X *cos(theta) + y * sin(theta) = r 其中角度theta指r与X轴之间的夹角,r为到直线几何垂
直距离。任何在直线上点,x, y都可以表达,其中 r, theta是常量。该公式图形表示如下:
![](https://img-my.csdn.net/uploads/201207/07/1341642751_7461.png)
然而在实现的图像处理领域,图像的像素坐标P(x, y)是已知的,而r, theta则是我们要寻找
的变量。如果我们能绘制每个(r, theta)值根据像素点坐标P(x, y)值的话,那么就从图像笛卡
尔坐标系统转换到极坐标霍夫空间系统,这种从点到曲线的变换称为直线的霍夫变换。变换
通过量化霍夫参数空间为有限个值间隔等分或者累加格子。当霍夫变换算法开始,每个像素
坐标点P(x, y)被转换到(r, theta)的曲线点上面,累加到对应的格子数据点,当一个波峰出现
时候,说明有直线存在。同样的原理,我们可以用来检测圆,只是对于圆的参数方程变为如
下等式:
(x –a ) ^2 + (y-b) ^ 2 = r^2其中(a, b)为圆的中心点坐标,r圆的半径。这样霍夫的参数空间就
变成一个三维参数空间。给定圆半径转为二维霍夫参数空间,变换相对简单,也比较常用。
编程思路解析:
1. 读取一幅带处理二值图像,最好背景为黑色。
2. 取得源像素数据
3. 根据直线的霍夫变换公式完成霍夫变换,预览霍夫空间结果
4. 寻找最大霍夫值,设置阈值,反变换到图像RGB值空间(程序难点之一)
5. 越界处理,显示霍夫变换处理以后的图像
关键代码解析:
直线的变换角度为[0 ~ PI]之间,设置等份为500为PI/500,同时根据参数直线参数方程的取值
范围为[-r, r]有如下霍夫参数定义:
-
- int centerX = width / 2;
- int centerY = height / 2;
- double hough_interval = PI_VALUE/(double)hough_space;
-
- int max = Math.max(width, height);
- int max_length = (int)(Math.sqrt(2.0D) * max);
- hough_1d = new int[2 * hough_space * max_length];
实现从像素RGB空间到霍夫空间变换的代码为:
-
- int[][] image_2d = convert1Dto2D(inPixels);
- for (int row = 0; row < height; row++) {
- for (int col = 0; col < width; col++) {
- int p = image_2d[row][col] & 0xff;
- if(p == 0) continue;
-
-
-
-
-
- for(int cell=0; cell < hough_space; cell++ ) {
- max = (int)((col - centerX) * Math.cos(cell * hough_interval) + (row - centerY) * Math.sin(cell * hough_interval));
- max += max_length;
- if (max < 0 || (max >= 2 * max_length)) {
- continue;
- }
- hough_2d[cell][max] +=1;
- }
- }
- }
寻找最大霍夫值计算霍夫阈值的代码如下:
-
- int max_hough = 0;
- for(int i=0; i<hough_space; i++) {
- for(int j=0; j<2*max_length; j++) {
- hough_1d[(i + j * hough_space)] = hough_2d[i][j];
- if(hough_2d[i][j] > max_hough) {
- max_hough = hough_2d[i][j];
- }
- }
- }
- System.out.println("MAX HOUGH VALUE = " + max_hough);
-
-
- int hough_threshold = (int)(threshold * max_hough);
从霍夫空间反变换回像素数据空间代码如下:
-
- int hough_threshold = (int)(threshold * max_hough);
- for(int row = 0; row < hough_space; row++) {
- for(int col = 0; col < 2*max_length; col++) {
- if(hough_2d[row][col] < hough_threshold)
- continue;
- int hough_value = hough_2d[row][col];
- boolean isLine = true;
- for(int i=-1; i<2; i++) {
- for(int j=-1; j<2; j++) {
- if(i != 0 || j != 0) {
- int yf = row + i;
- int xf = col + j;
- if(xf < 0) continue;
- if(xf < 2*max_length) {
- if (yf < 0) {
- yf += hough_space;
- }
- if (yf >= hough_space) {
- yf -= hough_space;
- }
- if(hough_2d[yf][xf] <= hough_value) {
- continue;
- }
- isLine = false;
- break;
- }
- }
- }
- }
- if(!isLine) continue;
-
-
- double dy = Math.sin(row * hough_interval);
- double dx = Math.cos(row * hough_interval);
- if ((row <= hough_space / 4) || (row >= 3 * hough_space / 4)) {
- for (int subrow = 0; subrow < height; ++subrow) {
- int subcol = (int)((col - max_length - ((subrow - centerY) * dy)) / dx) + centerX;
- if ((subcol < width) && (subcol >= 0)) {
- image_2d[subrow][subcol] = -16776961;
- }
- }
- } else {
- for (int subcol = 0; subcol < width; ++subcol) {
- int subrow = (int)((col - max_length - ((subcol - centerX) * dx)) / dy) + centerY;
- if ((subrow < height) && (subrow >= 0)) {
- image_2d[subrow][subcol] = -16776961;
- }
- }
- }
- }
- }
霍夫变换源图如下:
霍夫变换以后,在霍夫空间显示如下:(白色表示已经找到直线信号)
![](https://img-my.csdn.net/uploads/201207/07/1341643279_6500.png)
最终反变换回到像素空间效果如下:
![](https://img-my.csdn.net/uploads/201207/07/1341643451_7244.png)
一个更好的运行监测直线的结果(输入为二值图像):
![](https://img-my.csdn.net/uploads/201207/07/1341643681_8786.png)
摘自:http://blog.csdn.net/jia20003/article/details/7724530