1. 程式人生 > >基於OpenCV實現霍夫變換

基於OpenCV實現霍夫變換

霍夫變換概述

霍夫變換(Hough Transform)是影象處理中的一種特徵提取技術,該過程在一個引數空間中通過計算累計結果的區域性最大值得到一個符合該特徵的集合作為霍夫變換的結果。

霍夫線檢測

霍夫線變換是一種尋找直線的方法,在尋找霍夫變換之前,要對影象進行邊緣檢測,即霍夫線的輸入為二值影象。

原理

這裡寫圖片描述

其意思就是,直線在極座標中的表示形式為:r=x*cos(θ)+y*sin(θ),即每一對通過(r,θ)代表了一條通過(x,y)的直線。

這裡寫圖片描述

如果給定一個點(8,6)則r=8*cos(θ)+6*sin(θ)對應與一條正弦曲線。

這裡寫圖片描述

對影象中的所有點都進行上述的操作,如果兩個不同點進行操作得到的曲線在平面(θ,r)上相交,則意味正他們通過一條直線,因此我們可以通過設定直線上點的閾值來定義多少條曲線相交於一點,這要才認為檢測到了一條直線。

OpenCV中霍夫變換

介紹兩種霍夫線變換,標準霍夫線變換和累計概率霍夫線變換。

Standard Hough Line Transform的原型為:

void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )

image: 輸入影象,型別為二進位制影象
lines:lines型別,儲存了向量(p,θ)
rho:以畫素為單位的距離精度
theta:以弧度為單位的角度精度
threshold:閾值,即識別為直線時他在累加平面中必須達到的值
srn:預設為0
stn預設為0,關於更多,請檢視官方文件

Probabilistic Hough Line Transform

累計霍夫變換是對標準霍夫變換的一種改進,他在一定的範圍內進行霍夫變換,計算單獨線段的方向以及範圍,從而減少計算了,縮短了時間。

void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )

不同點在最後的兩個引數。

minLineLength:表示最低線段的長度,比此小的都不能顯示出來
maxLineGap:允許同一行點與點之間連線起來的最大距離

下面對累加霍夫變換進行展示C++程式碼:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int thre, minLineLength, maxLineGap;
Mat src, dst;
void on_HoughLines(int, void *);
int main() {
    Mat ori = imread("road.jpg");
    namedWindow("dst");
    imshow("origin", ori);
    Canny(ori, src, 50, 200,3);
    cvtColor(src, dst, CV_GRAY2BGR);
    imshow("canny image", src);
    thre = 80;
    minLineLength = 50;
    maxLineGap=10;
    createTrackbar("threshold:", "dst", &thre, 300, on_HoughLines);
    createTrackbar("minLine  :", "dst", &minLineLength, 300, on_HoughLines);
    createTrackbar("maxGap   :", "dst", &maxLineGap, 20, on_HoughLines);

    on_HoughLines(thre, 0);
    on_HoughLines(minLineLength, 0);
    on_HoughLines(maxLineGap, 0);
    /*HoughLinesP(src, lines, 1, CV_PI / 180, 50, 60, 10);
    for (size_t i = 0; i < lines.size(); i++) {
        Vec4i l = lines[i];
        line(dst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186,88, 255));
    }*/
    //imshow("dst image", dst);
    waitKey(0);
}
//
void on_HoughLines(int, void *) {
    vector<Vec4i> lines;
    Mat dstImage = dst.clone();
    Mat srcImage = src.clone();
    HoughLinesP(srcImage, lines, 1, CV_PI / 180, thre, minLineLength, maxLineGap);
    for (size_t i = 0; i < lines.size(); i++) {
        Vec4i l = lines[i];
        line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255),2);
    }
    imshow("dst", dstImage);
}

可以調整引數使得檢測到道路的直線段,效果如下:
原圖和canny運算元進行邊緣檢測的結果圖:

這裡寫圖片描述

直線檢測效果圖:

這裡寫圖片描述

霍夫圓檢測

一條直線可以以極座標的方式進行表示(r,θ),一個圓需要三個引數(x,y,r)進行表示,通常用霍夫梯度法來解決圓變換的問題。

霍夫梯度法

霍夫梯度法的原理是首先對影象進行邊緣檢測,用canny,然後考慮邊緣影象中的每一個非0點,考慮其區域性梯度,通過Sobel函式計算x,y方向上的sobel一階導數得到梯度,利用得到的梯度,由斜率制定直線上的每一個點都在累加器中被累加,然後從累加器中這些點鐘選擇候選的中心畫圓。

opencv中的霍夫圓檢測:

C++: void HoughCircles(InputArray image, OutputArray circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0, int maxRadius=0 )

image: 輸入影象,型別為二進位制影象
circles:經呼叫後儲存檢測到的圓的輸出向量(x,y,radius)
method:檢測的方法,位霍夫梯度法
dp:用來檢測原型的累加器影象的解析度與輸入影象之比的道說,dp=1,累加器和輸入影象具有相同的解析度。
minDist:檢測到的圓圓心的最小距離,即讓演算法能夠區分不同圓直徑的距離
param1:預設100,表示傳遞給canny邊緣運算元的高閾值
param2:表示在檢測階段圓心的累計其閾值,他越小,就越可以檢測到更多根本不存在的預案,越大的話,檢測的預案就更加接近完美圓
minRadius:表示半徑的最小值
maxRadiux:表示半徑的最大值

C++版本的示例:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
    Mat src = imread("planet_glow.jpg");
    namedWindow("origin");
    imshow("origin", src);
    //轉出灰度圖,並進行平滑,因為要進行邊緣檢測,最好進行平滑
    Mat gray;
    cvtColor(src, gray, CV_BGR2GRAY);
    imshow("gray", gray);;
    Mat dst;
    medianBlur(gray, dst, 5);;
    imshow("median filter", dst);
    vector<Vec3f> circles;
    HoughCircles(dst, circles, HOUGH_GRADIENT,1,120,100,30,0,0);
    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(src, center, 2, Scalar(0, 255, 0), 2);
        //繪製輪廓
        circle(src, center, radius, Scalar(0, 255, 0), 2);
    }
    imshow("result", src);
    waitKey(0);
}

Python版本:

import cv2
import numpy as np

planets = cv2.imread('planet_glow.jpg')
gray_img = cv2.cvtColor(planets, cv2.COLOR_BGR2GRAY)
img = cv2.medianBlur(gray_img, 5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)

circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,120,
                            param1=100,param2=30,minRadius=0,maxRadius=0)

circles = np.uint16(np.around(circles))

for i in circles[0,:]:
    # draw the outer circle
    cv2.circle(planets,(i[0],i[1]),i[2],(0,255,0),2)
    # draw the center of the circle
    cv2.circle(planets,(i[0],i[1]),2,(0,0,255),3)

cv2.imwrite("planets_circles.jpg", planets)
cv2.imshow("HoughCirlces", planets)
cv2.waitKey()
cv2.destroyAllWindows()

結果如下所示:

這裡寫圖片描述