轉載: hough變換是如何檢測出直線和圓的? ——钰
參考: java hough變換(霍夫變換)檢測直線 ——epleone
對于平面中的一條直線,在笛卡爾坐标系中,常見的有點斜式,兩點式兩種表示方法。然而在hough變換中,考慮的是另外一種表示方式:使用(r,theta)來表示一條直線。其中r為該直線到原點的距離,theta為該直線的垂線與x軸的夾角。如下圖所示。
2 如果坐标系中有多個點,又怎樣識别出哪些點在一條直線上呢?
使用hough變換來檢測直線的思想就是:為每一個點假設n個方向的直線,通常n=180,此時檢測的直線的角度精度為1°,分别計算這n條直線的(r,theta)坐标,得到n個坐标點。如果要判斷的點共有N個,最終得到的(r,theta)坐标有N*n個。有關這N*n個(r,theta)坐标,其中theta是離散的角度,共有180個取值。
最重要的地方來了,如果多個點在一條直線上,那麼必有這多個點在theta=某個值theta_i時,這多個點的r近似相等于r_i。也就是說這多個點都在直線(r_i,theta_i)上。
3 下面拿個例子說明:
如果空間中有3個點,如何判斷這三個點在不在一個直線上,如果在,這條直線是的位置為?
這個例子中,對于每個點均求過該點的6條直線的(r,theta)坐标,共求了3*6個(r,theta)坐标。可以發現在theta=60時,三個點的r都近似為80.7,由此可判定這三個點都在直線(80.7,60)上。
通過 r0theta 坐标系可以更直覺表示這種關系,如下圖:圖中三個點的(r,theta)曲線彙集在一起,該交點就是同時經過這三個點的直線。
在實際的直線檢測情況中,如果超過一定數目的點擁有相同的(r,theta)坐标,那麼就可以判定此處有一條直線。在r0theta 坐标系圖中,明顯的交彙點就标示一條檢測出的直線。
如下圖,可以判定出平面上的點共構成了兩條直線,即檢測出兩條直線。
代碼如下:
package houghTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
public class HoughLine {
/*
* to save the hough transform's results
*/
class h{
int ro;
int angle;
public h(int r,int a){
this.ro = r;
this.angle = a;
}
}
public static void main(String[] args) throws IOException {
HoughLine HL = new HoughLine();
HL.hough();
}
public void hough() throws IOException{
//Image im = this.clone();
//im.sobel();
//im.IterBinary();
File src = new File("e:/test/as.png");
BufferedImage im = ImageIO.read(src);
int h = im.getHeight();
int w = im.getWidth();
int ImageData[] = im.getRGB(, , w, h, null, , w);
int data[] = new int[w*h];
for(int i=; i<ImageData.length; i++) {
data[i] = ImageData[i] & ;
}
int ro = (int)Math.sqrt(h*h+w*w);
int theta = ;
int[][] hist = new int[ro][theta];
for(int k=;k<theta;k++){
for(int i=;i<h;i++){
for(int j=;j<w;j++){
if(data[j+i*w] != ){
int rho=(int)(j*Math.cos(k*Math.PI/(theta*))+i*Math.sin(k*Math.PI/(theta*)));
hist[rho][k]++;
}
}
}
}
ArrayList<h> index = maxIndex(hist,); //找到大于最大值*0.7的二維直方圖的點
for(int k =;k<index.size();k++){
double resTheta = index.get(k).angle*Math.PI/(theta*);
for(int i=;i<h;i++){
for(int j=;j<w;j++){
int rho = (int)(j*Math.cos(resTheta)+i*Math.sin(resTheta));
if(data[j+i*w] != && rho == index.get(k).ro){
data[j+i*w] = setRed(); //在直線上的點設為紅色
}else{
data[j+i*w] = (<<)|(data[j+i*w]<<)|(data[j+i*w]<<)|(data[j+i*w]);
}
}
}
}
writeImage("e:/test/as_2.png", data, w, h);
//this.gray = false;
}
public void writeImage(String desImageName, int[] imageData, int width, int height)
{
FileOutputStream fos = null;
try {
fos = new FileOutputStream(desImageName);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BufferedImage img = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
img.setRGB(, , width, height, imageData, , width);
img.flush();
try {
ImageIO.write(img, "jpg", fos);
} catch (IOException e) {
e.printStackTrace();
}
}
private ArrayList<houghTransform.HoughLine.h> maxIndex(int[][] hist, int i) {
ArrayList<h> in = new ArrayList<h>();
int max = ;
for(int i1=;i1<hist.length;i1++){
for(int j1=;j1<hist[i1].length;j1++){
if(max < hist[i1][j1]){
max = hist[i1][j1];
}
}
}
System.out.println(max);
for(int i1=;i1<hist.length;i1++){
for(int j1=;j1<hist[i1].length;j1++){
if(hist[i1][j1] > max*(i/))
in.add(new h(i1,j1));
}
}
return in;
}
private int setRed() {
return ;
}
}
效果如下,輸入為一張經過邊緣檢測的圖檔:
輸入為: