霍夫直線變換和霍夫圓變換的原理和實現
一、霍夫直線的原理
(1)本部分大部分學習來自《OpenCV3程式設計入門》,另外有一些自己的理解
如上圖所以,將一條直線由截距是表示為在極座標系下
化簡為
(2)對於一個點(x0,y0)來說,可以通過這個點的一族直線統一定義為
每一對 代表一條通過點(x0,y0)的直線。
(3)如果對於一個給定點(x0,y0),我們在極座標對極徑極角平面繪製出所有通過它的直線,將會得到一條正弦曲線。例如x0=8,y0=6的曲線如下所示:
上圖是r>0 theta(0,2*PI)
(4)對影象中所有的點進行上述操作,如果兩個點在一條直線上,那麼兩條正弦曲線將會交於一點
上圖所有的三個點在一條直線上
(5)一條直線能夠通過在平面theta-r尋找交於一點的曲線數量來檢測, 越多的 曲線交於一點就意味著這個交點表示的直線由更多的點組成,通過設定交於一點的曲線數的閾值來決定是否檢測到一條直線。
(6)霍夫變換即追蹤影象中每個點對應曲線間的交點,如果 交點數量超過了閾值,那麼可以認為這個交點所代表的引數為原影象中的一條直線。
二、霍夫直線的程式碼
#include<opencv2/opencv.hpp> #include<iostream> int main(){ cv::Mat src=cv::imread("../2.jpg"); cv::Mat dst,mid; cv::Canny(src,mid,50,200,3); cv::cvtColor(mid,dst,cv::COLOR_GRAY2BGR); std::vector<cv::Vec2f>lines; cv::HoughLines(mid,lines,1,CV_PI/180,100,0,0); for(size_t t=0;t<lines.size();t++){ float rho=lines[t][0],theta=lines[t][1]; cv::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)); cv::line(dst,pt1,pt2,cv::Scalar(55,10,195),1,cv::LINE_AA); }; cv::imshow("src",src); cv::imshow("mid",mid); cv::imshow("dst",dst); cv::waitKey(0); return 0; }
三、霍夫圓的原理
霍夫變換檢測圓形的原理跟檢測直線的原理是一樣的。圓的表示式為 (x-a)2+(y-b)2=r2 , 把問題轉換成在求解經過畫素點最多的 (a,b,r) 引數對。這裡會發現(a,b,r)的引數空間特別大,計算量特別大。我們一般使用霍夫梯度法來解決圓的變換。
如上圖所示,如果我們對一個圓求梯度,那麼圓上所有的點的梯度的方向均朝向圓心
基於此有如下原理:
(1)首先對影象應用邊緣檢測,比如用canny邊緣檢測
(2)使用sobel運算元計算所有畫素的梯度
(3)遍歷canny之後的所有非0的畫素點,沿著梯度方向畫線,每個點有是一個累加器,有一個線經過該點,累加器加1,對所有累加器進行排序,根據閾值找到所有可能的圓心
(4)計算canny影象中所有的非0畫素點距離圓心的距離,距離從小到大排序,選取合適的半徑
(5)對選取的半徑設定累加器,對於滿足半徑r的累加器+1
(6)統計所有可能的半徑
四、霍夫圓的程式碼
#include<iostream>
#include<opencv2/opencv.hpp>
int main(){
cv::Mat src=cv::imread("../3.png");
cv::Mat gray;
cv::namedWindow("src",cv::WINDOW_AUTOSIZE);
cv::imshow("src",src);
cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);
cv::namedWindow("gray",cv::WINDOW_AUTOSIZE);
cv::imshow("gray",gray);
std::vector<cv::Vec3f>circles;
cv::HoughCircles(gray,circles,cv::HOUGH_GRADIENT,2,10,200,100,85,0);
for(size_t t=0;t<circles.size();t++){
cv::Point center(circles[t][0],circles[t][1]);
int r=circles[t][2];
cv::circle(src,center,2,cv::Scalar(0,0,255),-1,8);
cv::circle(src,center,r,cv::Scalar(0,255,0),3,8,0);
}
cv::namedWindow("效果",cv::WINDOW_AUTOSIZE);
cv::imshow("效果",src);
cv::waitKey(0);
return 0;
}