OpenCV 【二十二】霍夫線變換/霍夫圓變換

目錄

1. 霍夫線變換¶

1.1 原理

1.2 霍夫線性變換

1.3 標準霍夫線變換和統計概率霍夫線變換¶

1.4 代碼

1.5運行結果

2 .霍夫圓變換¶

2.1 原理¶

2.2霍夫圓變換¶

2.3代碼

2.4 運行結果


1. 霍夫線變換

1.1 原理

  1. 衆所周知, 一條直線在圖像二維空間可由兩個變量表示. 例如:

    1. 在 笛卡爾座標系: 可由參數: (m,b) 斜率和截距表示.
    2. 在 極座標系: 可由參數: (r,\theta) 極徑和極角表示
    Line variables

    對於霍夫變換, 我們將用 極座標系 來表示直線. 因此, 直線的表達式可爲:

    y = \left ( -\dfrac{\cos \theta}{\sin \theta} \right ) x + \left ( \dfrac{r}{\sin \theta} \right )

    化簡得: r = x \cos \theta + y \sin \theta

  2. 一般來說對於點 (x_{0}, y_{0}), 我們可以將通過這個點的一族直線統一定義爲:

    r_{\theta} = x_{0} \cdot \cos \theta  + y_{0} \cdot \sin \theta

    這就意味着每一對 (r_{\theta},\theta) 代表一條通過點 (x_{0}, y_{0}) 的直線.

  3. 如果對於一個給定點 (x_{0}, y_{0}) 我們在極座標對極徑極角平面繪出所有通過它的直線, 將得到一條正弦曲線. 例如, 對於給定點 x_{0} = 8 and y_{0} = 6 我們可以繪出下圖 (在平面 \theta - r):

    Polar plot of a the family of lines of a point

    只繪出滿足下列條件的點 r > 0 and 0< \theta < 2 \pi.

  4. 我們可以對圖像中所有的點進行上述操作. 如果兩個不同點進行上述操作後得到的曲線在平面 \theta - r 相交, 這就意味着它們通過同一條直線. 例如, 接上面的例子我們繼續對點: x_{1} = 9y_{1} = 4 和點 x_{2} = 12y_{2} = 3 繪圖, 得到下圖:

    Polar plot of the family of lines for three points

    這三條曲線在 \theta - r 平面相交於點 (0.925, 9.6), 座標表示的是參數對 (\theta, r) 或者是說點 (x_{0}, y_{0}), 點 (x_{1}, y_{1}) 和點 (x_{2}, y_{2}) 組成的平面內的的直線.

  5. 那麼以上的材料要說明什麼呢? 這意味着一般來說, 一條直線能夠通過在平面 \theta - r 尋找交於一點的曲線數量來 檢測. 越多曲線交於一點也就意味着這個交點表示的直線由更多的點組成. 一般來說我們可以通過設置直線上點的 閾值 來定義多少條曲線交於一點我們才認爲 檢測 到了一條直線.

  6. 這就是霍夫線變換要做的. 它追蹤圖像中每個點對應曲線間的交點. 如果交於一點的曲線的數量超過了 閾值, 那麼可以認爲這個交點所代表的參數對 (\theta, r_{\theta}) 在原圖像中爲一條直線.

1.2 霍夫線性變換

  1. 霍夫線變換是一種用來尋找直線的方法.

  2. 是用霍夫線變換之前, 首先要對圖像進行邊緣檢測的處理,也即霍夫線變換的直接輸入只能是邊緣二值圖像.

1.3 標準霍夫線變換和統計概率霍夫線變換

OpenCV實現了以下兩種霍夫線變換:

  1. 標準霍夫線變換

  • 原理在上面的部分已經說明了. 它能給我們提供一組參數對 (\theta, r_{\theta}) 的集合來表示檢測到的直線

  • 在OpenCV 中通過函數 HoughLines 來實現

  1. 統計概率霍夫線變換

  • 這是執行起來效率更高的霍夫線變換. 它輸出檢測到的直線的端點 (x_{0}, y_{0}, x_{1}, y_{1})

  • 在OpenCV 中它通過函數 HoughLinesP 來實現

 

1.4 代碼

  1. 這個程序是用來做什麼的?

    • 加載一幅圖片

    • 對圖片進行 標準霍夫線變換 或是 統計概率霍夫線變換.

    • 分別在兩個窗口顯示原圖像和繪出檢測到直線的圖像.

  2. 我們將要說明的例程能從 這裏 下載。 一個更高級的版本 (能同時演示標準霍夫線變換和統計概率霍夫線變換並帶有活動條來改變變換的閾值) 能從 這裏 下載。

//src = imread("C:\\Users\\guoqi\\Desktop\\ch7\\4.jpg", 1);
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
​
using namespace cv;
using namespace std;
​
​
int main(int argc, char** argv)
{
    Mat src = imread("C:\\Users\\guoqi\\Desktop\\ch7\\9.jpg", 0);
​
​
    Mat dst, cdst;
    Canny(src, dst, 50, 200, 3);
    cvtColor(dst, cdst, CV_GRAY2BGR);
​
#if 0
    vector<Vec2f> lines;
    HoughLines(dst, lines, 1, CV_PI / 180, 100, 0, 0);
​
    for (size_t i = 0; i < lines.size(); i++)
    {
        float rho = lines[i][0], theta = lines[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        pt1.x = cvRound(x0 + 1000 * (-b));
        pt1.y = cvRound(y0 + 1000 * (a));
        pt2.x = cvRound(x0 - 1000 * (-b));
        pt2.y = cvRound(y0 - 1000 * (a));
        line(cdst, pt1, pt2, Scalar(0, 0, 255), 3, CV_AA);
    }
#else
    vector<Vec4i> lines;
    HoughLinesP(dst, lines, 1, CV_PI / 180, 50, 50, 10);
    for (size_t i = 0; i < lines.size(); i++)
    {
        Vec4i l = lines[i];
        line(cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 1, CV_AA);
    }
#endif
    imshow("source", src);
    imshow("detected lines", cdst);
​
    waitKey();
​
    return 0;
}

1.5運行結果

2 .霍夫圓變換

2.1 原理

2.2霍夫圓變換

  • 霍夫圓變換的基本原理和上個教程中提到的霍夫線變換類似, 只是點對應的二維極徑極角空間被三維的圓心點x, y還有半徑r空間取代.

  • 對直線來說, 一條直線能由參數極徑極角 (r, \theta) 表示. 而對圓來說, 我們需要三個參數來表示一個圓, 如上文所說現在原圖像的邊緣圖像的任意點對應的經過這個點的所有可能圓是在三維空間有下面這三個參數來表示了,其對應一條三維空間的曲線. 那麼與二維的霍夫線變換同樣的道理, 對於多個邊緣點越多這些點對應的三維空間曲線交於一點那麼他們經過的共同圓上的點就越多,類似的我們也就可以用同樣的閾值的方法來判斷一個圓是否被檢測到, 這就是標準霍夫圓變換的原理, 但也正是在三維空間的計算量大大增加的原因, 標準霍夫圓變化很難被應用到實際中:

    C : ( x_{center}, y_{center}, r )

    這裏的 (x_{center}, y_{center}) 表示圓心的位置 (下圖中的綠點) 而 r 表示半徑, 這樣我們就能唯一的定義一個圓了, 見下圖:

  • Result of detecting circles with Hough Transform

  • 出於上面提到的對運算效率的考慮, OpenCV實現的是一個比標準霍夫圓變換更爲靈活的檢測方法: 霍夫梯度法, 也叫2-1霍夫變換(21HT), 它的原理依據是圓心一定是在圓上的每個點的模向量上, 這些圓上點模向量的交點就是圓心, 霍夫梯度法的第一步就是找到這些圓心, 這樣三維的累加平面就又轉化爲二維累加平面. 第二步根據所有候選中心的邊緣非0像素對其的支持程度來確定半徑. 21HT方法最早在Illingworth的論文The Adaptive Hough Transform中提出並詳細描述, 也可參照Yuen在1990年發表的A Comparative Study of Hough Transform Methods for Circle Finding, Bradski的《學習OpenCV》一書則對OpenCV中具體對算法的具體實現有詳細描述並討論了霍夫梯度法的侷限性.

2.3代碼

  1. 這個例程是用來幹嘛的?

    • 加載一幅圖像並對其模糊化以降噪

    • 對模糊化後的圖像執行霍夫圓變換 .

    • 在窗體中顯示檢測到的圓.

  2. 下面要講解的例程代碼能從 這裏 下載. 一個更高級的版本 (能同時演示標準霍夫圓變換和統計概率霍夫圓變換並帶有可改變閾值的滑動條) 能從 這裏 找到.

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
​
using namespace cv;
using namespace std;
​
/** @function main */
int main(int argc, char** argv)
{
    Mat src, src_gray;
​
    /// Read the image
    src = imread("C:\\Users\\guoqi\\Desktop\\ch7\\10.jpg", 1);
​
    if (!src.data)
    {
        return -1;
    }
​
    /// Convert it to gray
    cvtColor(src, src_gray, CV_BGR2GRAY);
​
    /// Reduce the noise so we avoid false circle detection
    GaussianBlur(src_gray, src_gray, Size(9, 9), 2, 2);
​
    vector<Vec3f> circles;
​
    /// Apply the Hough Transform to find the circles
    HoughCircles(src_gray, circles, CV_HOUGH_GRADIENT, 1, src_gray.rows / 8, 200, 100, 0, 0);
​
    /// Draw the circles detected
    for (size_t i = 0; i < circles.size(); i++)
    {
        Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);
        // circle center
        circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0);
        // circle outline
        circle(src, center, radius, Scalar(0, 0, 255), 3, 8, 0);
    }
​
    /// Show your results
    namedWindow("Hough Circle Transform Demo", CV_WINDOW_AUTOSIZE);
    imshow("Hough Circle Transform Demo", src);
​
    waitKey(0);
    return 0;
}

2.4 運行結果

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章