天天看點

Hough變換檢測直線的Java實作

轉載: hough變換是如何檢測出直線和圓的? ——钰

參考: java hough變換(霍夫變換)檢測直線 ——epleone

對于平面中的一條直線,在笛卡爾坐标系中,常見的有點斜式,兩點式兩種表示方法。然而在hough變換中,考慮的是另外一種表示方式:使用(r,theta)來表示一條直線。其中r為該直線到原點的距離,theta為該直線的垂線與x軸的夾角。如下圖所示。

Hough變換檢測直線的Java實作

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個點,如何判斷這三個點在不在一個直線上,如果在,這條直線是的位置為?

Hough變換檢測直線的Java實作

這個例子中,對于每個點均求過該點的6條直線的(r,theta)坐标,共求了3*6個(r,theta)坐标。可以發現在theta=60時,三個點的r都近似為80.7,由此可判定這三個點都在直線(80.7,60)上。

通過 r0theta 坐标系可以更直覺表示這種關系,如下圖:圖中三個點的(r,theta)曲線彙集在一起,該交點就是同時經過這三個點的直線。

Hough變換檢測直線的Java實作

在實際的直線檢測情況中,如果超過一定數目的點擁有相同的(r,theta)坐标,那麼就可以判定此處有一條直線。在r0theta 坐标系圖中,明顯的交彙點就标示一條檢測出的直線。

如下圖,可以判定出平面上的點共構成了兩條直線,即檢測出兩條直線。

Hough變換檢測直線的Java實作

代碼如下:

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

效果如下,輸入為一張經過邊緣檢測的圖檔:

Hough變換檢測直線的Java實作

輸入為:

Hough變換檢測直線的Java實作