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

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

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

形状(如,直线,圆等)。霍夫变换寻找直线与圆的方法相比与其他方法能够更好的降低噪

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

霍夫变换算法思想:

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

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

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

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;
	}

}

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

图像处理之霍夫变换(直线检測算法),布布扣,bubuko.com

时间: 2024-07-29 01:36:48

图像处理之霍夫变换(直线检測算法)的相关文章

OpenCV图像处理篇之边缘检測算子

3种边缘检測算子 灰度或结构等信息的突变位置是图像的边缘,图像的边缘有幅度和方向属性.沿边缘方向像素变化缓慢,垂直边缘方向像素变化剧烈.因此,边缘上的变化能通过梯度计算出来. 一阶导数的梯度算子 对于二维的图像.梯度定义为一个向量. Gx对于x方向的梯度,Gy相应y方向的梯度,向量的幅值本来是 mag(f)?=?(Gx2?+?Gy2)1/2,为简化计算,一般用mag(f)=|Gx|+|Gy|近似,幅值同一时候包括了x而后y方向的梯度信息.梯度的方向为 α?=?arctan(Gx/Gy) . 因为

图像边缘检測小结

边缘是图像中灰度发生急剧变化的区域边界. 图像灰度的变化情况能够用图像灰度分布的梯度来表示,数字图像中求导是利用差分近似微分来进行的,实际上经常使用空域微分算子通过卷积来完毕. 一阶导数算子 1)  Roberts算子 Roberts算子是一种斜向偏差分的梯度计算方法.梯度的大小代表边缘的强度.梯度的方向与边缘的走向垂直.Roberts操作实际上是求旋转 \pm" title="\pm" >45度两个方向上微分值的和. Roberts算子定位精度高,在水平和垂直方向的效

目标检測的图像特征提取之(一)HOG特征

1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检測的特征描写叙述子.它通过计算和统计图像局部区域的梯度方向直方图来构成特征.Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检測中获得了极大的成功.须要提醒的是,HOG+SVM进行行人检測的方法是法国研究人员Dalal在2005的CVPR上提出的,而现在尽管有非常多行人检測算法不断提出,但基本都是以HOG+SVM的思路为主. (

HLS图像处理系列——肤色检測

本博文採用Xilinx HLS 2014.4工具.实现一个肤色检測的模块.当中,本文重点是构建HLS图像处理函数. 新建HLSproject的步骤,本博文不再详述. 本project新建之后,仅仅加入了五个文件,例如以下图所看到的.当中,top.cpp中的主函数终于会综合生成HLS硬件图像处理模块.test.cpp是測试文件,调用測试图片.測试top.cpp的图像处理函数功能. top.cpp的源代码例如以下: #include "top.h" #include "imgpr

【OpenCV新手教程之十七】OpenCV重映射 &amp;amp; SURF特征点检測合辑

本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/30974513 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本号: 2.4.9 本篇文章中,我们一起探讨了OpenCV

OpenCV特征点检測------Surf(特征点篇)

Surf(Speed Up Robust Feature) Surf算法的原理                                                                           1.构建Hessian矩阵构造高斯金字塔尺度空间 事实上surf构造的金字塔图像与sift有非常大不同,就是由于这些不同才加快了其检測的速度. Sift採用的是DOG图像.而surf採用的是Hessian矩阵行列式近似值图像.Hessian矩阵是Surf算法的核心,为了方

【CImg】霍夫变换——直线检测

霍夫变换——直线检测 此处膜拜大神(学到很多):http://blog.csdn.net/jia20003/article/details/7724530 这个博客更了很多图像处理算法的底层实现解析,都很详细易懂,先mark ========================我是分割线============================= 霍夫变换:CV中常用的识别几何图形的方法,其中最简单的应用就是直线检测 主要原理是对于边缘的每一个像素点(x0,y0),把可能经过它的所有直线y=kx+b,

OpenCV2马拉松第17圈——边缘检測(Canny边缘检測)

计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g 收入囊中 利用OpenCV Canny函数进行边缘检測 掌握Canny算法基本理论 分享Java的实现 葵花宝典 在此之前,我们先阐述一下canny检測的算法.总共分为4部分. (1)处理噪声 一般用高斯滤波.OpenCV使用例如以下核 (2)计算梯度幅值 先用例如以下Sobel算子计算出水平和竖直梯度 我在OpenCV2马拉松第14圈--边缘检測(Sobel,prewitt,ro

人脸检測中几种框框大小的选择~

人脸检測应用极为广泛,内部细节也偏多,尤其是涉及到几种类型的框,这几种框的大小之前有着千丝万缕的联系,对检測性能的好坏影响程度大小不一.本篇文章基于自己在人脸检測方面的经验,说说对这些框之间关系的一些理解. 如今大部分人脸检測效果都已adaboost+LBP(各种改进)的方式实现,adaboost由N个强分类器组成,每一个强分类器由M个弱分类器组成,而每一个弱分类器事实上就是一个特征. 本文以LBP特征为例,人脸检測共涉及到例如以下几类框: 1. LBP特征矩形框大小(极为重要) 2. 检測框大