影像處理之霍夫變換(直線檢測演算法)

來源:互聯網
上載者:User

標籤:des   style   http   java   color   使用   

影像處理之霍夫變換(直線檢測演算法)

霍夫變換是映像變換中的經典手段之中的一個,主要用來從映像中分離出具有某種同樣特徵的幾何

形狀(如,直線,圓等)。霍夫變換尋找直線與圓的方法相比與其他方法能夠更好的降低噪

聲幹擾。經典的霍夫變換經常使用來檢測直線,圓,橢圓等。

 

霍夫變換演算法思想:

以直線檢測為例,每一個像素座標點經過變換都變成都直線特質有貢獻的統一度量,一個簡單

的範例例如以下:一條直線在映像中是一系列離散點的集合,通過一個直線的離散極座標公式,

能夠表達出直線的離散點幾何等式例如以下:

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 valueint 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 spaceint 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;}@Overridepublic 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 valueint 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 spaceint 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;}}
轉載文章請務必註明出自本部落格!!





聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.