图像处理之霍夫变换(直线检测算法)

简介: 图像处理之霍夫变换(直线检测算法)霍夫变换是图像变换中的经典手段之一,主要用来从图像中分离出具有某种相同特征的几何形状(如,直线,圆等)。霍夫变换寻找直线与圆的方法相比与其它方法可以更好的减少噪声干扰。

图像处理之霍夫变换(直线检测算法)

霍夫变换是图像变换中的经典手段之一,主要用来从图像中分离出具有某种相同特征的几何

形状(如,直线,圆等)。霍夫变换寻找直线与圆的方法相比与其它方法可以更好的减少噪

声干扰。经典的霍夫变换常用来检测直线,圆,椭圆等。

 

霍夫变换算法思想:

以直线检测为例,每个像素坐标点经过变换都变成都直线特质有贡献的统一度量,一个简单

的例子如下:一条直线在图像中是一系列离散点的集合,通过一个直线的离散极坐标公式,

可以表达出直线的离散点几何等式如下:

X *cos(theta) + y * sin(theta)  = r 其中角度theta指r与X轴之间的夹角,r为到直线几何垂

直距离。任何在直线上点,x, y都可以表达,其中 r, theta是常量。该公式图形表示如下:

然而在实现的图像处理领域,图像的像素坐标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]有如下霍夫参数定义:

 // prepare for hough transform
 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空间到霍夫空间变换的代码为:

// start hough transform now....
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; // which means background color
    	
    	// since we does not know the theta angle and r value, 
    	// we have to calculate all hough space for each pixel point
    	// then we got the max possible theta and r pair.
    	// r = x * cos(theta) + y * sin(theta)
    	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; // start from zero, not (-max_length)
    		if (max < 0 || (max >= 2 * max_length)) {// make sure r did not out of scope[0, 2*max_lenght]
                continue;
            }
    		hough_2d[cell][max] +=1;
    	}
    }
}

寻找最大霍夫值计算霍夫阈值的代码如下:

// find the max hough value
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);

// transfer back to image pixels space from hough parameter space
int hough_threshold = (int)(threshold * max_hough);

从霍夫空间反变换回像素数据空间代码如下:

	    // transfer back to image pixels space from hough parameter space
	    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) // discard it
	    			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;
	    		
	    		// transform back to pixel data now...
	            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;
	                  }
	                }
	              }
	    	}
	    }
霍夫变换源图如下:

霍夫变换以后,在霍夫空间显示如下:(白色表示已经找到直线信号)


最终反变换回到像素空间效果如下:


一个更好的运行监测直线的结果(输入为二值图像):


完整的霍夫变换源代码如下:

package com.gloomyfish.image.transform;

import java.awt.image.BufferedImage;

import com.process.blur.study.AbstractBufferedImageOp;

public class HoughLineFilter extends AbstractBufferedImageOp {
	public final static double PI_VALUE = Math.PI;
	private int hough_space = 500;
	private int[] hough_1d;
	private int[][] hough_2d;
	private int width;
	private int height;
	
	private float threshold;
	private float scale;
	private float offset;
	
	public HoughLineFilter() {
		// default hough transform parameters
		//	scale = 1.0f;
		//	offset = 0.0f;
		threshold = 0.5f;
		scale = 1.0f;
		offset = 0.0f;
	}
	
	public void setHoughSpace(int space) {
		this.hough_space = space;
	}
	
	public float getThreshold() {
		return threshold;
	}

	public void setThreshold(float threshold) {
		this.threshold = threshold;
	}

	public float getScale() {
		return scale;
	}

	public void setScale(float scale) {
		this.scale = scale;
	}

	public float getOffset() {
		return offset;
	}

	public void setOffset(float offset) {
		this.offset = offset;
	}

	@Override
	public BufferedImage filter(BufferedImage src, BufferedImage dest) {
		width = src.getWidth();
        height = src.getHeight();

        if ( dest == null )
            dest = createCompatibleDestImage( src, null );

        int[] inPixels = new int[width*height];
        int[] outPixels = new int[width*height];
        getRGB( src, 0, 0, width, height, inPixels );
        houghTransform(inPixels, outPixels);
        setRGB( dest, 0, 0, width, height, outPixels );
        return dest;
	}

	private void houghTransform(int[] inPixels, int[] outPixels) {
        // prepare for hough transform
	    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];
	    
	    // define temp hough 2D array and initialize the hough 2D
	    hough_2d = new int[hough_space][2*max_length];
	    for(int i=0; i<hough_space; i++) {
	    	for(int j=0; j<2*max_length; j++) {
	    		hough_2d[i][j] = 0;
	    	}
	    }
	    
	    // start hough transform now....
	    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; // which means background color
	        	
	        	// since we does not know the theta angle and r value, 
	        	// we have to calculate all hough space for each pixel point
	        	// then we got the max possible theta and r pair.
	        	// r = x * cos(theta) + y * sin(theta)
	        	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; // start from zero, not (-max_length)
	        		if (max < 0 || (max >= 2 * max_length)) {// make sure r did not out of scope[0, 2*max_lenght]
	                    continue;
	                }
	        		hough_2d[cell][max] +=1;
	        	}
	        }
	    }
	    
		// find the max hough value
		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);
		
		// transfer back to image pixels space from hough parameter space
		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) // discard it
	    			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;
	    		
	    		// transform back to pixel data now...
	            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;
	                  }
	                }
	              }
	    	}
	    }
	    
	    // convert to hough 1D and return result
	    for (int i = 0; i < this.hough_1d.length; i++)
	    {
	      int value = clamp((int)(scale * this.hough_1d[i] + offset)); // scale always equals 1
	      this.hough_1d[i] = (0xFF000000 | value + (value << 16) + (value << 8));
	    }
	    
	    // convert to image 1D and return
	    for (int row = 0; row < height; row++) {
	    	for (int col = 0; col < width; col++) {
	        	outPixels[(col + row * width)] = image_2d[row][col];
	        }
	    }
	}
	
	public BufferedImage getHoughImage() {
		BufferedImage houghImage = new BufferedImage(hough_2d[0].length, hough_space, BufferedImage.TYPE_4BYTE_ABGR);
		setRGB(houghImage, 0, 0, hough_2d[0].length, hough_space, hough_1d);
		return houghImage;
	}
	
	public static int clamp(int value) {
	      if (value < 0)
	    	  value = 0;
	      else if (value > 255) {
	    	  value = 255;
	      }
	      return value;
	}
	
	private int[][] convert1Dto2D(int[] pixels) {
		int[][] image_2d = new int[height][width];
		int index = 0;
		for(int row = 0; row < height; row++) {
			for(int col = 0; col < width; col++) {
				index = row * width + col;
				image_2d[row][col] = pixels[index];
			}
		}
		return image_2d;
	}

}
转载文章请务必注明出自本博客!!

学习图像处理,点击视频教程《数字图像处理-基础入门》




目录
相关文章
|
4月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GRU网络的MQAM调制信号检测算法matlab仿真,对比LSTM
本研究基于MATLAB 2022a,使用GRU网络对QAM调制信号进行检测。QAM是一种高效调制技术,广泛应用于现代通信系统。传统方法在复杂环境下性能下降,而GRU通过门控机制有效提取时间序列特征,实现16QAM、32QAM、64QAM、128QAM的准确检测。仿真结果显示,GRU在低SNR下表现优异,且训练速度快,参数少。核心程序包括模型预测、误检率和漏检率计算,并绘制准确率图。
140 65
基于GRU网络的MQAM调制信号检测算法matlab仿真,对比LSTM
|
18天前
|
机器学习/深度学习 监控 算法
面向办公室屏幕监控系统的改进型四叉树屏幕变化检测算法研究
本文提出一种改进型四叉树数据结构模型,用于优化办公室屏幕监控系统。通过动态阈值调节、变化优先级索引及增量更新策略,显著降低计算复杂度并提升实时响应能力。实验表明,该算法在典型企业环境中将屏幕变化检测效率提升40%以上,同时减少资源消耗。其应用场景涵盖安全审计、工作效能分析及远程协作优化等,未来可结合深度学习实现更智能化的功能。
27 0
|
3月前
|
机器学习/深度学习 存储 算法
基于MobileNet深度学习网络的活体人脸识别检测算法matlab仿真
本内容主要介绍一种基于MobileNet深度学习网络的活体人脸识别检测技术及MQAM调制类型识别方法。完整程序运行效果无水印,需使用Matlab2022a版本。核心代码包含详细中文注释与操作视频。理论概述中提到,传统人脸识别易受非活体攻击影响,而MobileNet通过轻量化的深度可分离卷积结构,在保证准确性的同时提升检测效率。活体人脸与非活体在纹理和光照上存在显著差异,MobileNet可有效提取人脸高级特征,为无线通信领域提供先进的调制类型识别方案。
|
3月前
|
机器学习/深度学习 数据采集 算法
基于yolov2和googlenet网络的疲劳驾驶检测算法matlab仿真
本内容展示了基于深度学习的疲劳驾驶检测算法,包括算法运行效果预览(无水印)、Matlab 2022a 软件版本说明、部分核心程序(完整版含中文注释与操作视频)。理论部分详细阐述了疲劳检测原理,通过对比疲劳与正常状态下的特征差异,结合深度学习模型提取驾驶员面部特征变化。具体流程包括数据收集、预处理、模型训练与评估,使用数学公式描述损失函数和推理过程。课题基于 YOLOv2 和 GoogleNet,先用 YOLOv2 定位驾驶员面部区域,再由 GoogleNet 分析特征判断疲劳状态,提供高准确率与鲁棒性的检测方法。
|
4月前
|
机器学习/深度学习 人工智能 运维
[ICDE2024]多正常模式感知的频域异常检测算法MACE
[ICDE2024]多正常模式感知的频域异常检测算法MACE
|
1月前
|
算法 机器人 数据安全/隐私保护
基于双向RRT算法的三维空间最优路线规划matlab仿真
本程序基于双向RRT算法实现三维空间最优路径规划,适用于机器人在复杂环境中的路径寻找问题。通过MATLAB 2022A测试运行,结果展示完整且无水印。算法从起点和终点同时构建两棵随机树,利用随机采样、最近节点查找、扩展等步骤,使两棵树相遇以形成路径,显著提高搜索效率。相比单向RRT,双向RRT在高维或障碍物密集场景中表现更优,为机器人技术提供了有效解决方案。
|
21天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN-LSTM时间卷积神经网络时间序列预测算法matlab仿真
本内容展示了一种基于粒子群优化(PSO)与时间卷积神经网络(TCN)的时间序列预测方法。通过 MATLAB2022a 实现,完整程序运行无水印,核心代码附详细中文注释及操作视频。算法利用 PSO 优化 TCN 的超参数(如卷积核大小、层数等),提升非线性时间序列预测性能。TCN 结构包含因果卷积层与残差连接,结合 LSTM 构建混合模型,经多次迭代选择最优超参数,最终实现更准确可靠的预测效果,适用于金融、气象等领域。
|
18天前
|
算法 数据安全/隐私保护
基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密
本项目实现了一种基于Logistic Map混沌序列的数字信息加解密算法,使用MATLAB2022A开发并包含GUI操作界面。支持对文字、灰度图像、彩色图像和语音信号进行加密与解密处理。核心程序通过调整Logistic Map的参数生成伪随机密钥序列,确保加密的安全性。混沌系统的不可预测性和对初值的敏感依赖性是该算法的核心优势。示例展示了彩色图像、灰度图像、语音信号及文字信息的加解密效果,运行结果清晰准确,且完整程序输出无水印。
基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密
|
18天前
|
算法
基于PSO粒子群优化的多无人机路径规划matlab仿真,对比WOA优化算法
本程序基于粒子群优化(PSO)算法实现多无人机路径规划,并与鲸鱼优化算法(WOA)进行对比。使用MATLAB2022A运行,通过四个无人机的仿真,评估两种算法在能耗、复杂度、路径规划效果及收敛曲线等指标上的表现。算法原理源于1995年提出的群体智能优化,模拟鸟群觅食行为,在搜索空间中寻找最优解。环境建模采用栅格或几何法,考虑避障、速度限制等因素,将约束条件融入适应度函数。程序包含初始化粒子群、更新速度与位置、计算适应度值、迭代优化等步骤,最终输出最优路径。
|
28天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN时间卷积神经网络时间序列预测算法matlab仿真
本内容介绍了一种基于PSO(粒子群优化)改进TCN(时间卷积神经网络)的时间序列预测方法。使用Matlab2022a运行,完整程序无水印,附带核心代码中文注释及操作视频。TCN通过因果卷积层与残差连接处理序列数据,PSO优化其卷积核权重等参数以降低预测误差。算法中,粒子根据个体与全局最优位置更新速度和位置,逐步逼近最佳参数组合,提升预测性能。

热门文章

最新文章