【OpenCV】影像變換(四-1)-霍夫變換線段檢測

王大偉啊發表於2015-04-26

霍夫變換是一種在影像中尋找直線、圓及其他形狀的方法。原始的霍夫變化是一種直線變換,即在二值影像中尋找直線的一種相對快速方法,變換可以推廣到其他普通的情況,而不僅僅是簡單的直線。在這篇博文中,我們先對霍夫變換的線段檢測討論下。

(1)霍夫變換的線段檢測理論

如下圖所示,在直角座標系中有一條直線l,原點到該直線的垂直距離是ρ,垂線與X軸的夾角θ,則這條直線是唯一的,且其方程為:

這裡寫圖片描述
這裡寫圖片描述

而這條直線用極座標表示為(ρ,θ),可見直角座標系中的一條直線對應著極座標下的一點,這種線到點的變換就是Hough變換。

在直角座標系中,過任意一點(x0,y0)的直線系,如下圖所示,滿足
這裡寫圖片描述
其中:
這裡寫圖片描述

這裡寫圖片描述

而這些直線在極座標系中所對應的點(ρ,θ)構成一條正弦曲線,反之,在極座標系中在這條正弦曲線上的點均對應著直角座標系中過(x0,y0)的一條直線,如圖所示:
這裡寫圖片描述

設平面上有若干點,過每點的直線系分別對應於極座標系上的一條正弦曲線,若這些正弦曲線有共同的交點(ρ’,θ’),如圖所示,這些點共線,且這些點對應的直線方程為:
這裡寫圖片描述
這裡寫圖片描述

這就是Hough變換檢測直線的原理。

在OpenCV中給出了相關的基於霍夫變換的線段檢測函式cvHoughLines2(),下面簡要介紹下這個函式的各個引數意義。

CvSeq* cvHoughLines2(
    CvArr* image,//輸入影像,必須為8位的二值影像
    void* line_storage,//指向儲存結果位置的指標
    int method,//CV_HOUGH_STANDARD、PROBABILISTIC等
    double rho,
    double theta,//rho,theta是設定直線的解析度,單位分別為畫素和弧度
    int threshold,//一條直線在累計平面中必須要達到的值
    double param1=0,
    double param2=0
)

上面這個函式返回的結果是一個指向記憶體塊的指標,所以在畫出檢測到的直線時,要遍歷這個序列。下面給出這個程式的示例。

(2)程式示例及執行結果

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;

int main()
{
    const char *pSrcWindow = "原圖";
    const char *pDstWindow = "Hough圖";

    IplImage *pSrcImage = cvLoadImage("1.png", CV_LOAD_IMAGE_UNCHANGED);
    IplImage *pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
    cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);
    //通過canny檢測得到二值圖,因為cvHoughLines2()中第一個引數輸入影像必須是8位二值影像
    IplImage *pCannyImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
    cvCanny(pGrayImage, pCannyImage, 30, 90, 3);

    //設定引數數值
    CvMemStorage *line_storage = cvCreateMemStorage();
    double rho = 1;
    double theta = CV_PI / 180;
    int threshold = 50;
    double param1 = 50;
    double param2 = 10;
    //呼叫函式cvHoughLines2()函式,返回檢測到的線段序列
    CvSeq* LineSeq = cvHoughLines2(pCannyImage, line_storage, CV_HOUGH_PROBABILISTIC, rho, theta, threshold, param1, param2);

    //建立輸出影像,並將影像從灰度圖轉換到RGB空間
    IplImage *pDstImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);
    cvCvtColor(pCannyImage, pDstImage, CV_GRAY2BGR);

    //畫出檢測到的線段,因為在函式cvHoughLines2()返回得到的是一個序列seq,下面其實是這個序列的遍歷;
    for (int i = 0; i < LineSeq->total; ++i)
    {
        CvPoint* p = (CvPoint*)cvGetSeqElem(LineSeq, i);
        cvLine(pDstImage, p[0], p[1], CV_RGB(255, 0, 0), 2);
    }

    cvNamedWindow(pSrcWindow, CV_WINDOW_AUTOSIZE);
    cvNamedWindow(pDstWindow, CV_WINDOW_AUTOSIZE);
    cvShowImage(pSrcWindow, pSrcImage);
    cvShowImage(pDstWindow, pDstImage);

    cvWaitKey();

    cvReleaseMemStorage(&line_storage);
    cvReleaseImage(&pSrcImage);
    cvReleaseImage(&pGrayImage);
    cvReleaseImage(&pCannyImage);
    cvReleaseImage(&pDstImage);

    cvDestroyWindow(pSrcWindow);
    cvDestroyWindow(pDstWindow);

    return 0;
}

執行的結果如下:
這裡寫圖片描述

相關文章