OpenCV2马拉松第22圈——Hough变换直线检測原理与实现
生活随笔
收集整理的這篇文章主要介紹了
OpenCV2马拉松第22圈——Hough变换直线检測原理与实现
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
計(jì)算機(jī)視覺討論群162501053 轉(zhuǎn)載請(qǐng)注明:http://blog.csdn.net/abcd1992719g/article/details/27220445
收入囊中
葵花寶典 先看一下我實(shí)現(xiàn)的效果圖
以下,我們進(jìn)入Hough變換的原理解說。
看上圖,我們知道,經(jīng)過一點(diǎn)(x0,y0)的直線能夠表示成y0 = mox + b0 反過來看方程,b = –x0m + y0 ,于是我們從原來的坐標(biāo)系轉(zhuǎn)移到了Hough空間,m是橫坐標(biāo),b是縱坐標(biāo)
剛才提到,經(jīng)過(x0,y0)的直線具有的特征是b = –x0m + y0,在Hough空間下也是一條直線, 那么經(jīng)過(x1,y1)的直線具有的特征是b = -x1m + y1,在Hough空間下是還有一條直線。 兩條直線的相交點(diǎn)的(m,b)就是經(jīng)過(x0,y0)(x1,y1)的直線,這個(gè)應(yīng)該能夠理解吧。 于是就有了一個(gè)簡(jiǎn)單的想法,對(duì)于每個(gè)點(diǎn),在Hough空間中都畫出一條直線,對(duì)于每條直線經(jīng)過的點(diǎn),都填充在例如以下的 Hough空間中,看哪交點(diǎn)多,就能確定。我們用一個(gè)二維數(shù)組表示Hough空間,例如以下。最后就變成數(shù)哪些格子的值比較高。
可是,用m和b有局限性。由于m是能夠取到無窮大的,所以這個(gè)特征僅僅在理論上可行...實(shí)際上我們不可能申請(qǐng)一個(gè)無限大的二維數(shù)組。
自然而然,我們想到了極坐標(biāo),在極坐標(biāo)下,就沒有這個(gè)限制了。 在極坐標(biāo)下,我們的直線能夠?qū)懗?#xff1a;
也就是: 經(jīng)過點(diǎn)(x0,y0)的直線: 當(dāng)x0 = 8, y0 = 6,我們有這種圖 我們?cè)谝韵聝H僅考慮?而且?.
我們還有2個(gè)點(diǎn),,?? ? ? ?,?,就能夠繪制出以下的圖形 這3條直線相交于, 也就是說 () = ?是這3個(gè)點(diǎn)?,???共同經(jīng)過的直線!
因此,我們有了算法雛形 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? 初始化H( Hough空間的二維數(shù)組)全為0 ? 遍歷圖片的 (x,y)? For θ = 0 to 360
? ??? ρ = xcos θ + y sin θ
? ??? H(θ, ρ) = H(θ,ρ) + 1
??? end
end ? Find the value(s) of (θ, ρ)where H(θ, ρ)is a local maximum ? Thedetected line in the image is given by ?ρ = xcos θ + y sin θ
看以下的圖片,當(dāng)都一條直線時(shí),Hough空間的某個(gè)區(qū)域就會(huì)非常亮,取局部極大值就能夠
一張更復(fù)雜的圖片
初識(shí)API C++:?void?HoughLines(InputArray?image, OutputArray?lines, double?rho, double?theta, int?threshold, double?srn=0, double?stn=0?)
荷槍實(shí)彈 還是先貼出官方sample #include <cv.h> #include <highgui.h> #include <math.h>using namespace cv;int main(int argc, char** argv) {Mat src, dst, color_dst;if( argc != 2 || !(src=imread(argv[1], 0)).data)return -1;Canny( src, dst, 50, 200, 3 );cvtColor( dst, color_dst, CV_GRAY2BGR );#if 0vector<Vec2f> lines;HoughLines( dst, lines, 1, CV_PI/180, 100 );for( size_t i = 0; i < lines.size(); i++ ){float rho = lines[i][0];float theta = lines[i][1];double a = cos(theta), b = sin(theta);double x0 = a*rho, y0 = b*rho;Point pt1(cvRound(x0 + 1000*(-b)),cvRound(y0 + 1000*(a)));Point pt2(cvRound(x0 - 1000*(-b)),cvRound(y0 - 1000*(a)));line( color_dst, pt1, pt2, Scalar(0,0,255), 3, 8 );} #elsevector<Vec4i> lines;HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );for( size_t i = 0; i < lines.size(); i++ ){line( color_dst, Point(lines[i][0], lines[i][1]),Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 );} #endifnamedWindow( "Source", 1 );imshow( "Source", src );namedWindow( "Detected Lines", 1 );imshow( "Detected Lines", color_dst );waitKey(0);return 0; }
假如我們想檢測(cè)直線,就能夠用第一個(gè)API,由于這個(gè)API返回的是直線的兩個(gè)參數(shù) 假設(shè)想檢測(cè)圖片中的線段,就用第二個(gè)API,由于這個(gè) API返回的是起點(diǎn)和終點(diǎn)
以下看下我自己的實(shí)現(xiàn),首先是弧度及結(jié)構(gòu)體的定義 const double pi = 3.1415926f; const double RADIAN = 180.0/pi; struct line {int theta;int r; };
接下來是變換到Hough空間,填充二維數(shù)組
vector<struct line> houghLine(Mat &img, int threshold) {vector<struct line> lines;int diagonal = floor(sqrt(img.rows*img.rows + img.cols*img.cols));vector< vector<int> >p(360 ,vector<int>(diagonal));for( int j = 0; j < img.rows ; j++ ) { for( int i = 0; i < img.cols; i++ ) {if( img.at<unsigned char>(j,i) > 0){for(int theta = 0;theta < 360;theta++){int r = floor(i*cos(theta/RADIAN) + j*sin(theta/RADIAN));if(r < 0)continue;p[theta][r]++;}}}}//get local maximumfor( int theta = 0;theta < 360;theta++){for( int r = 0;r < diagonal;r++){int thetaLeft = max(0,theta-1);int thetaRight = min(359,theta+1);int rLeft = max(0,r-1);int rRight = min(diagonal-1,r+1);int tmp = p[theta][r];if( tmp > threshold && tmp > p[thetaLeft][rLeft] && tmp > p[thetaLeft][r] && tmp > p[thetaLeft][rRight]&& tmp > p[theta][rLeft] && tmp > p[theta][rRight]&& tmp > p[thetaRight][rLeft] && tmp > p[thetaRight][r] && tmp > p[thetaRight][rRight]){struct line newline;newline.theta = theta;newline.r = r;lines.push_back(newline);}}}return lines; }
最后是畫直線的函數(shù) void drawLines(Mat &img, const vector<struct line> &lines) {for(int i = 0;i < lines.size();i++){vector<Point> points;int theta = lines[i].theta;int r = lines[i].r;double ct = cos(theta/RADIAN);double st = sin(theta/RADIAN);//r = x*ct + y*st//leftint y = int(r/st);if(y >= 0 && y < img.rows){Point p(0, y);points.push_back(p);}//righty = int((r-ct*(img.cols-1))/st);if(y >= 0 && y < img.rows){Point p(img.cols-1, y);points.push_back(p);}//topint x = int(r/ct);if(x >= 0 && x < img.cols){Point p(x, 0);points.push_back(p);}//downx = int((r-st*(img.rows-1))/ct);if(x >= 0 && x < img.cols){Point p(x, img.rows-1);points.push_back(p);}cv::line( img, points[0], points[1], Scalar(0,0,255), 1, CV_AA);} }
完整代碼 #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> #include <vector> #include <cmath> using namespace cv; using namespace std;const double pi = 3.1415926f; const double RADIAN = 180.0/pi; struct line {int theta;int r; };/** r = xcos(theta) + ysin(theta)*/ vector<struct line> houghLine(Mat &img, int threshold) {vector<struct line> lines;int diagonal = floor(sqrt(img.rows*img.rows + img.cols*img.cols));vector< vector<int> >p(360 ,vector<int>(diagonal));for( int j = 0; j < img.rows ; j++ ) { for( int i = 0; i < img.cols; i++ ) {if( img.at<unsigned char>(j,i) > 0){for(int theta = 0;theta < 360;theta++){int r = floor(i*cos(theta/RADIAN) + j*sin(theta/RADIAN));if(r < 0)continue;p[theta][r]++;}}}}//get local maximumfor( int theta = 0;theta < 360;theta++){for( int r = 0;r < diagonal;r++){int thetaLeft = max(0,theta-1);int thetaRight = min(359,theta+1);int rLeft = max(0,r-1);int rRight = min(diagonal-1,r+1);int tmp = p[theta][r];if( tmp > threshold && tmp > p[thetaLeft][rLeft] && tmp > p[thetaLeft][r] && tmp > p[thetaLeft][rRight]&& tmp > p[theta][rLeft] && tmp > p[theta][rRight]&& tmp > p[thetaRight][rLeft] && tmp > p[thetaRight][r] && tmp > p[thetaRight][rRight]){struct line newline;newline.theta = theta;newline.r = r;lines.push_back(newline);}}}return lines; }void drawLines(Mat &img, const vector<struct line> &lines) {for(int i = 0;i < lines.size();i++){vector<Point> points;int theta = lines[i].theta;int r = lines[i].r;double ct = cos(theta/RADIAN);double st = sin(theta/RADIAN);//r = x*ct + y*st//leftint y = int(r/st);if(y >= 0 && y < img.rows){Point p(0, y);points.push_back(p);}//righty = int((r-ct*(img.cols-1))/st);if(y >= 0 && y < img.rows){Point p(img.cols-1, y);points.push_back(p);}//topint x = int(r/ct);if(x >= 0 && x < img.cols){Point p(x, 0);points.push_back(p);}//downx = int((r-st*(img.rows-1))/ct);if(x >= 0 && x < img.cols){Point p(x, img.rows-1);points.push_back(p);}cv::line( img, points[0], points[1], Scalar(0,0,255), 1, CV_AA);} }int main( int, char** argv ) {Mat src,src_gray,edge;src = imread( argv[1] );cvtColor( src, src_gray, CV_BGR2GRAY );blur( src_gray, src_gray, Size(3,3) );Canny( src_gray, edge, 50, 200);vector<struct line> lines = houghLine(edge, 90);drawLines(src, lines);namedWindow("result", 1); imshow("result", src);waitKey();return 0; }
舉一反三
概率Hough變換在基本算法上添加了比較少的改動(dòng)。之前是逐行掃描,如今則是隨機(jī)選點(diǎn)。每當(dāng)累加器的一個(gè)條目達(dá)到指定的最小值,就沿這條直線的方向掃描,并且將通過它的全部點(diǎn)刪除(即使它們還沒有參與投票)。并且該掃描還確定被接受的線段的長(zhǎng)度。為此,該算法定義了兩個(gè)附加參數(shù)。一個(gè)是被接受線段的最小長(zhǎng)度,而還有一個(gè)是被同意以形成連續(xù)的段的最大距離。這個(gè)附加步驟添加了算法的復(fù)雜性,可是復(fù)雜性帶來的效率損失被較少的點(diǎn)會(huì)參與投票過程補(bǔ)償。
我們?cè)賮砜匆豢雌渌螤钤诙S Hough空間的樣子
我們?cè)倏紤]一下噪聲的影響
噪聲使得峰值定位非常難
噪聲程度越厲害,結(jié)果越不準(zhǔn)確
解決噪聲問題的算法:
這個(gè)算法也不復(fù)雜,對(duì)于一個(gè)點(diǎn),我們?cè)?jīng)要遍歷[0,360]的角度,可是如今,這個(gè)角度就直接被我們?nèi)〕鰜砹?#xff0c;速度也有非常大的提升,非常不錯(cuò)的算法。
收入囊中
- Hough變換
- 概率Hough變換
- 自己實(shí)現(xiàn)Hough變換直線檢測(cè)
葵花寶典 先看一下我實(shí)現(xiàn)的效果圖
以下,我們進(jìn)入Hough變換的原理解說。
看上圖,我們知道,經(jīng)過一點(diǎn)(x0,y0)的直線能夠表示成y0 = mox + b0 反過來看方程,b = –x0m + y0 ,于是我們從原來的坐標(biāo)系轉(zhuǎn)移到了Hough空間,m是橫坐標(biāo),b是縱坐標(biāo)
剛才提到,經(jīng)過(x0,y0)的直線具有的特征是b = –x0m + y0,在Hough空間下也是一條直線, 那么經(jīng)過(x1,y1)的直線具有的特征是b = -x1m + y1,在Hough空間下是還有一條直線。 兩條直線的相交點(diǎn)的(m,b)就是經(jīng)過(x0,y0)(x1,y1)的直線,這個(gè)應(yīng)該能夠理解吧。 于是就有了一個(gè)簡(jiǎn)單的想法,對(duì)于每個(gè)點(diǎn),在Hough空間中都畫出一條直線,對(duì)于每條直線經(jīng)過的點(diǎn),都填充在例如以下的 Hough空間中,看哪交點(diǎn)多,就能確定。我們用一個(gè)二維數(shù)組表示Hough空間,例如以下。最后就變成數(shù)哪些格子的值比較高。
可是,用m和b有局限性。由于m是能夠取到無窮大的,所以這個(gè)特征僅僅在理論上可行...實(shí)際上我們不可能申請(qǐng)一個(gè)無限大的二維數(shù)組。
自然而然,我們想到了極坐標(biāo),在極坐標(biāo)下,就沒有這個(gè)限制了。 在極坐標(biāo)下,我們的直線能夠?qū)懗?#xff1a;
也就是: 經(jīng)過點(diǎn)(x0,y0)的直線: 當(dāng)x0 = 8, y0 = 6,我們有這種圖 我們?cè)谝韵聝H僅考慮?而且?.
我們還有2個(gè)點(diǎn),,?? ? ? ?,?,就能夠繪制出以下的圖形 這3條直線相交于, 也就是說 () = ?是這3個(gè)點(diǎn)?,???共同經(jīng)過的直線!
因此,我們有了算法雛形 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? 初始化H( Hough空間的二維數(shù)組)全為0 ? 遍歷圖片的 (x,y)? For θ = 0 to 360
? ??? ρ = xcos θ + y sin θ
? ??? H(θ, ρ) = H(θ,ρ) + 1
??? end
end ? Find the value(s) of (θ, ρ)where H(θ, ρ)is a local maximum ? Thedetected line in the image is given by ?ρ = xcos θ + y sin θ
看以下的圖片,當(dāng)都一條直線時(shí),Hough空間的某個(gè)區(qū)域就會(huì)非常亮,取局部極大值就能夠
一張更復(fù)雜的圖片
初識(shí)API C++:?void?HoughLines(InputArray?image, OutputArray?lines, double?rho, double?theta, int?threshold, double?srn=0, double?stn=0?)
|
|
荷槍實(shí)彈 還是先貼出官方sample #include <cv.h> #include <highgui.h> #include <math.h>using namespace cv;int main(int argc, char** argv) {Mat src, dst, color_dst;if( argc != 2 || !(src=imread(argv[1], 0)).data)return -1;Canny( src, dst, 50, 200, 3 );cvtColor( dst, color_dst, CV_GRAY2BGR );#if 0vector<Vec2f> lines;HoughLines( dst, lines, 1, CV_PI/180, 100 );for( size_t i = 0; i < lines.size(); i++ ){float rho = lines[i][0];float theta = lines[i][1];double a = cos(theta), b = sin(theta);double x0 = a*rho, y0 = b*rho;Point pt1(cvRound(x0 + 1000*(-b)),cvRound(y0 + 1000*(a)));Point pt2(cvRound(x0 - 1000*(-b)),cvRound(y0 - 1000*(a)));line( color_dst, pt1, pt2, Scalar(0,0,255), 3, 8 );} #elsevector<Vec4i> lines;HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );for( size_t i = 0; i < lines.size(); i++ ){line( color_dst, Point(lines[i][0], lines[i][1]),Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 );} #endifnamedWindow( "Source", 1 );imshow( "Source", src );namedWindow( "Detected Lines", 1 );imshow( "Detected Lines", color_dst );waitKey(0);return 0; }
假如我們想檢測(cè)直線,就能夠用第一個(gè)API,由于這個(gè)API返回的是直線的兩個(gè)參數(shù) 假設(shè)想檢測(cè)圖片中的線段,就用第二個(gè)API,由于這個(gè) API返回的是起點(diǎn)和終點(diǎn)
以下看下我自己的實(shí)現(xiàn),首先是弧度及結(jié)構(gòu)體的定義 const double pi = 3.1415926f; const double RADIAN = 180.0/pi; struct line {int theta;int r; };
接下來是變換到Hough空間,填充二維數(shù)組
vector<struct line> houghLine(Mat &img, int threshold) {vector<struct line> lines;int diagonal = floor(sqrt(img.rows*img.rows + img.cols*img.cols));vector< vector<int> >p(360 ,vector<int>(diagonal));for( int j = 0; j < img.rows ; j++ ) { for( int i = 0; i < img.cols; i++ ) {if( img.at<unsigned char>(j,i) > 0){for(int theta = 0;theta < 360;theta++){int r = floor(i*cos(theta/RADIAN) + j*sin(theta/RADIAN));if(r < 0)continue;p[theta][r]++;}}}}//get local maximumfor( int theta = 0;theta < 360;theta++){for( int r = 0;r < diagonal;r++){int thetaLeft = max(0,theta-1);int thetaRight = min(359,theta+1);int rLeft = max(0,r-1);int rRight = min(diagonal-1,r+1);int tmp = p[theta][r];if( tmp > threshold && tmp > p[thetaLeft][rLeft] && tmp > p[thetaLeft][r] && tmp > p[thetaLeft][rRight]&& tmp > p[theta][rLeft] && tmp > p[theta][rRight]&& tmp > p[thetaRight][rLeft] && tmp > p[thetaRight][r] && tmp > p[thetaRight][rRight]){struct line newline;newline.theta = theta;newline.r = r;lines.push_back(newline);}}}return lines; }
最后是畫直線的函數(shù) void drawLines(Mat &img, const vector<struct line> &lines) {for(int i = 0;i < lines.size();i++){vector<Point> points;int theta = lines[i].theta;int r = lines[i].r;double ct = cos(theta/RADIAN);double st = sin(theta/RADIAN);//r = x*ct + y*st//leftint y = int(r/st);if(y >= 0 && y < img.rows){Point p(0, y);points.push_back(p);}//righty = int((r-ct*(img.cols-1))/st);if(y >= 0 && y < img.rows){Point p(img.cols-1, y);points.push_back(p);}//topint x = int(r/ct);if(x >= 0 && x < img.cols){Point p(x, 0);points.push_back(p);}//downx = int((r-st*(img.rows-1))/ct);if(x >= 0 && x < img.cols){Point p(x, img.rows-1);points.push_back(p);}cv::line( img, points[0], points[1], Scalar(0,0,255), 1, CV_AA);} }
完整代碼 #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> #include <vector> #include <cmath> using namespace cv; using namespace std;const double pi = 3.1415926f; const double RADIAN = 180.0/pi; struct line {int theta;int r; };/** r = xcos(theta) + ysin(theta)*/ vector<struct line> houghLine(Mat &img, int threshold) {vector<struct line> lines;int diagonal = floor(sqrt(img.rows*img.rows + img.cols*img.cols));vector< vector<int> >p(360 ,vector<int>(diagonal));for( int j = 0; j < img.rows ; j++ ) { for( int i = 0; i < img.cols; i++ ) {if( img.at<unsigned char>(j,i) > 0){for(int theta = 0;theta < 360;theta++){int r = floor(i*cos(theta/RADIAN) + j*sin(theta/RADIAN));if(r < 0)continue;p[theta][r]++;}}}}//get local maximumfor( int theta = 0;theta < 360;theta++){for( int r = 0;r < diagonal;r++){int thetaLeft = max(0,theta-1);int thetaRight = min(359,theta+1);int rLeft = max(0,r-1);int rRight = min(diagonal-1,r+1);int tmp = p[theta][r];if( tmp > threshold && tmp > p[thetaLeft][rLeft] && tmp > p[thetaLeft][r] && tmp > p[thetaLeft][rRight]&& tmp > p[theta][rLeft] && tmp > p[theta][rRight]&& tmp > p[thetaRight][rLeft] && tmp > p[thetaRight][r] && tmp > p[thetaRight][rRight]){struct line newline;newline.theta = theta;newline.r = r;lines.push_back(newline);}}}return lines; }void drawLines(Mat &img, const vector<struct line> &lines) {for(int i = 0;i < lines.size();i++){vector<Point> points;int theta = lines[i].theta;int r = lines[i].r;double ct = cos(theta/RADIAN);double st = sin(theta/RADIAN);//r = x*ct + y*st//leftint y = int(r/st);if(y >= 0 && y < img.rows){Point p(0, y);points.push_back(p);}//righty = int((r-ct*(img.cols-1))/st);if(y >= 0 && y < img.rows){Point p(img.cols-1, y);points.push_back(p);}//topint x = int(r/ct);if(x >= 0 && x < img.cols){Point p(x, 0);points.push_back(p);}//downx = int((r-st*(img.rows-1))/ct);if(x >= 0 && x < img.cols){Point p(x, img.rows-1);points.push_back(p);}cv::line( img, points[0], points[1], Scalar(0,0,255), 1, CV_AA);} }int main( int, char** argv ) {Mat src,src_gray,edge;src = imread( argv[1] );cvtColor( src, src_gray, CV_BGR2GRAY );blur( src_gray, src_gray, Size(3,3) );Canny( src_gray, edge, 50, 200);vector<struct line> lines = houghLine(edge, 90);drawLines(src, lines);namedWindow("result", 1); imshow("result", src);waitKey();return 0; }
舉一反三
概率Hough變換在基本算法上添加了比較少的改動(dòng)。之前是逐行掃描,如今則是隨機(jī)選點(diǎn)。每當(dāng)累加器的一個(gè)條目達(dá)到指定的最小值,就沿這條直線的方向掃描,并且將通過它的全部點(diǎn)刪除(即使它們還沒有參與投票)。并且該掃描還確定被接受的線段的長(zhǎng)度。為此,該算法定義了兩個(gè)附加參數(shù)。一個(gè)是被接受線段的最小長(zhǎng)度,而還有一個(gè)是被同意以形成連續(xù)的段的最大距離。這個(gè)附加步驟添加了算法的復(fù)雜性,可是復(fù)雜性帶來的效率損失被較少的點(diǎn)會(huì)參與投票過程補(bǔ)償。
我們?cè)賮砜匆豢雌渌螤钤诙S Hough空間的樣子
我們?cè)倏紤]一下噪聲的影響
噪聲使得峰值定位非常難
噪聲程度越厲害,結(jié)果越不準(zhǔn)確
解決噪聲問題的算法:
這個(gè)算法也不復(fù)雜,對(duì)于一個(gè)點(diǎn),我們?cè)?jīng)要遍歷[0,360]的角度,可是如今,這個(gè)角度就直接被我們?nèi)〕鰜砹?#xff0c;速度也有非常大的提升,非常不錯(cuò)的算法。
轉(zhuǎn)載于:https://www.cnblogs.com/mfrbuaa/p/3788388.html
總結(jié)
以上是生活随笔為你收集整理的OpenCV2马拉松第22圈——Hough变换直线检測原理与实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zookeeper源码
- 下一篇: C#传递参数