亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

Opencv2.4.9函數HoughLinesP分析

2020-01-26 13:34:34
字體:
來源:轉載
供稿:網友

標準霍夫變換本質上是把圖像映射到它的參數空間上,它需要計算所有的M個邊緣點,這樣它的運算量和所需內存空間都會很大。如果在輸入圖像中只是處理m(m<M)個邊緣點,則這m個邊緣點的選取是具有一定概率性的,因此該方法被稱為概率霍夫變換(Probabilistic Hough Transform)。該方法還有一個重要的特點就是能夠檢測出線端,即能夠檢測出圖像中直線的兩個端點,確切地定位圖像中的直線。

HoughLinesP函數就是利用概率霍夫變換來檢測直線的。它的一般步驟為:

1、隨機抽取圖像中的一個特征點,即邊緣點,如果該點已經被標定為是某一條直線上的點,則繼續在剩下的邊緣點中隨機抽取一個邊緣點,直到所有邊緣點都抽取完了為止;

2、對該點進行霍夫變換,并進行累加和計算;

3、選取在霍夫空間內值最大的點,如果該點大于閾值的,則進行步驟4,否則回到步驟1;

4、根據霍夫變換得到的最大值,從該點出發,沿著直線的方向位移,從而找到直線的兩個端點;

5、計算直線的長度,如果大于某個閾值,則被認為是好的直線輸出,回到步驟1。

HoughLinesP函數的原型為:

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

image為輸入圖像,要求是8位單通道圖像

lines為輸出的直線向量,每條線用4個元素表示,即直線的兩個端點的4個坐標值

rho和theta分別為距離和角度的分辨率

threshold為閾值,即步驟3中的閾值

minLineLength為最小直線長度,在步驟5中要用到,即如果小于該值,則不被認為是一條直線

maxLineGap為最大直線間隙,在步驟4中要用到,即如果有兩條線段是在一條直線上,但它們之間因為有間隙,所以被認為是兩個線段,如果這個間隙大于該值,則被認為是兩條線段,否則是一條。

HoughLinesP函數是在sources/modules/imgproc/src/hough.cpp文件中被定義的:

void cv::HoughLinesP( InputArray _image, OutputArray _lines,      double rho, double theta, int threshold,      double minLineLength, double maxGap ){ Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE); Mat image = _image.getMat(); CvMat c_image = image; CvSeq* seq = cvHoughLines2( &c_image, storage, CV_HOUGH_PROBABILISTIC,     rho, theta, threshold, minLineLength, maxGap ); seqToMat(seq, _lines);}

從HoughLinesP函數可以看出,該函數會調用cvHoughLines2函數。它通過參數CV_HOUGH_PROBABILISTIC,最終調用了icvHoughLinesProbabilistic函數:

static voidicvHoughLinesProbabilistic( CvMat* image,       float rho, float theta, int threshold,       int lineLength, int lineGap,       CvSeq *lines, int linesMax ){ //accum為累加器矩陣,mask為掩碼矩陣 cv::Mat accum, mask; cv::vector<float> trigtab; //用于存儲事先計算好的正弦和余弦值 //開辟一段內存空間 cv::MemStorage storage(cvCreateMemStorage(0)); //用于存儲特征點坐標,即邊緣像素的位置 CvSeq* seq;  CvSeqWriter writer; int width, height; //圖像的寬和高 int numangle, numrho; //角度和距離的離散數量 float ang; int r, n, count; CvPoint pt; float irho = 1 / rho; //距離分辨率的倒數 CvRNG rng = cvRNG(-1); //隨機數 const float* ttab; //向量trigtab的地址指針 uchar* mdata0; //矩陣mask的地址指針 //確保輸入圖像的正確性 CV_Assert( CV_IS_MAT(image) && CV_MAT_TYPE(image->type) == CV_8UC1 );  width = image->cols; //提取出輸入圖像的寬 height = image->rows; //提取出輸入圖像的高 //由角度和距離分辨率,得到角度和距離的離散數量 numangle = cvRound(CV_PI / theta); numrho = cvRound(((width + height) * 2 + 1) / rho); //創建累加器矩陣,即霍夫空間 accum.create( numangle, numrho, CV_32SC1 ); //創建掩碼矩陣,大小與輸入圖像相同 mask.create( height, width, CV_8UC1 ); //定義trigtab的大小,因為要存儲正弦和余弦值,所以長度為角度離散數的2倍 trigtab.resize(numangle*2); //累加器矩陣清零 accum = cv::Scalar(0); //避免重復計算,事先計算好所需的所有正弦和余弦值 for( ang = 0, n = 0; n < numangle; ang += theta, n++ ) {  trigtab[n*2] = (float)(cos(ang) * irho);  trigtab[n*2+1] = (float)(sin(ang) * irho); } //賦值首地址 ttab = &trigtab[0]; mdata0 = mask.data; //開始寫入序列 cvStartWriteSeq( CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), storage, &writer );  // stage 1. collect non-zero image points //收集圖像中的所有非零點,因為輸入圖像是邊緣圖像,所以非零點就是邊緣點 for( pt.y = 0, count = 0; pt.y < height; pt.y++ ) {  //提取出輸入圖像和掩碼矩陣的每行地址指針  const uchar* data = image->data.ptr + pt.y*image->step;  uchar* mdata = mdata0 + pt.y*width;  for( pt.x = 0; pt.x < width; pt.x++ )  {   if( data[pt.x] ) //是邊緣點   {    mdata[pt.x] = (uchar)1; //掩碼的相應位置置1    CV_WRITE_SEQ_ELEM( pt, writer ); 把該坐標位置寫入序列   }   else //不是邊緣點    mdata[pt.x] = 0; //掩碼的相應位置清0  } } //終止寫序列,seq為所有邊緣點坐標位置的序列 seq = cvEndWriteSeq( &writer ); count = seq->total; //得到邊緣點的數量  // stage 2. process all the points in random order //隨機處理所有的邊緣點 for( ; count > 0; count-- ) {  // choose random point out of the remaining ones  //步驟1,在剩下的邊緣點中隨機選擇一個點,idx為不大于count的隨機數  int idx = cvRandInt(&rng) % count;  //max_val為累加器的最大值,max_n為最大值所對應的角度  int max_val = threshold-1, max_n = 0;  //由隨機數idx在序列中提取出所對應的坐標點  CvPoint* point = (CvPoint*)cvGetSeqElem( seq, idx );  //定義直線的兩個端點  CvPoint line_end[2] = {{0,0}, {0,0}};  float a, b;  //累加器的地址指針,也就是霍夫空間的地址指針  int* adata = (int*)accum.data;  int i, j, k, x0, y0, dx0, dy0, xflag;  int good_line;  const int shift = 16;  //提取出坐標點的橫、縱坐標  i = point->y;  j = point->x;   // "remove" it by overriding it with the last element  //用序列中的最后一個元素覆蓋掉剛才提取出來的隨機坐標點  *point = *(CvPoint*)cvGetSeqElem( seq, count-1 );   // check if it has been excluded already (i.e. belongs to some other line)  //檢測這個坐標點是否已經計算過,也就是它已經屬于其他直線  //因為計算過的坐標點會在掩碼矩陣mask的相對應位置清零  if( !mdata0[i*width + j] ) //該坐標點被處理過   continue; //不做任何處理,繼續主循環   // update accumulator, find the most probable line  //步驟2,更新累加器矩陣,找到最有可能的直線  for( n = 0; n < numangle; n++, adata += numrho )  {   //由角度計算距離   r = cvRound( j * ttab[n*2] + i * ttab[n*2+1] );   r += (numrho - 1) / 2;   //在累加器矩陣的相應位置上數值加1,并賦值給val   int val = ++adata[r];   //更新最大值,并得到它的角度   if( max_val < val )   {    max_val = val;    max_n = n;   }  }   // if it is too "weak" candidate, continue with another point  //步驟3,如果上面得到的最大值小于閾值,則放棄該點,繼續下一個點的計算  if( max_val < threshold )   continue;   // from the current point walk in each direction  // along the found line and extract the line segment  //步驟4,從當前點出發,沿著它所在直線的方向前進,直到達到端點為止  a = -ttab[max_n*2+1]; //a=-sinθ  b = ttab[max_n*2]; //b=cosθ  //當前點的橫、縱坐標值  x0 = j;  y0 = i;  //確定當前點所在直線的角度是在45度~135度之間,還是在0~45或135度~180度之間  if( fabs(a) > fabs(b) ) //在45度~135度之間  {   xflag = 1; //置標識位,標識直線的粗略方向   //確定橫、縱坐標的位移量   dx0 = a > 0 ? 1 : -1;    dy0 = cvRound( b*(1 << shift)/fabs(a) );   //確定縱坐標   y0 = (y0 << shift) + (1 << (shift-1));  }  else //在0~45或135度~180度之間  {   xflag = 0; //清標識位   //確定橫、縱坐標的位移量   dy0 = b > 0 ? 1 : -1;   dx0 = cvRound( a*(1 << shift)/fabs(b) );   //確定橫坐標   x0 = (x0 << shift) + (1 << (shift-1));  }  //搜索直線的兩個端點  for( k = 0; k < 2; k++ )  {   //gap表示兩條直線的間隙,x和y為搜索位置,dx和dy為位移量   int gap = 0, x = x0, y = y0, dx = dx0, dy = dy0;   //搜索第二個端點的時候,反方向位移   if( k > 0 )    dx = -dx, dy = -dy;    // walk along the line using fixed-point arithmetics,   // stop at the image border or in case of too big gap   //沿著直線的方向位移,直到到達圖像的邊界或大的間隙為止   for( ;; x += dx, y += dy )   {    uchar* mdata;    int i1, j1;    //確定新的位移后的坐標位置    if( xflag )    {     j1 = x;     i1 = y >> shift;    }    else    {     j1 = x >> shift;     i1 = y;    }    //如果到達了圖像的邊界,停止位移,退出循環    if( j1 < 0 || j1 >= width || i1 < 0 || i1 >= height )     break;    //定位位移后掩碼矩陣位置    mdata = mdata0 + i1*width + j1;     // for each non-zero point:    // update line end,    // clear the mask element    // reset the gap    //該掩碼不為0,說明該點可能是在直線上    if( *mdata )     {     gap = 0; //設置間隙為0     //更新直線的端點位置     line_end[k].y = i1;     line_end[k].x = j1;    }    //掩碼為0,說明不是直線,但仍繼續位移,直到間隙大于所設置的閾值為止    else if( ++gap > lineGap ) //間隙加1     break;   }  }  //步驟5,由檢測到的直線的兩個端點粗略計算直線的長度  //當直線長度大于所設置的閾值時,good_line為1,否則為0  good_line = abs(line_end[1].x - line_end[0].x) >= lineLength ||     abs(line_end[1].y - line_end[0].y) >= lineLength;  //再次搜索端點,目的是更新累加器矩陣和更新掩碼矩陣,以備下一次循環使用  for( k = 0; k < 2; k++ )  {   int x = x0, y = y0, dx = dx0, dy = dy0;    if( k > 0 )    dx = -dx, dy = -dy;    // walk along the line using fixed-point arithmetics,   // stop at the image border or in case of too big gap   for( ;; x += dx, y += dy )   {    uchar* mdata;    int i1, j1;     if( xflag )    {     j1 = x;     i1 = y >> shift;    }    else    {     j1 = x >> shift;     i1 = y;    }     mdata = mdata0 + i1*width + j1;     // for each non-zero point:    // update line end,    // clear the mask element    // reset the gap    if( *mdata )    {     //if語句的作用是清除那些已經判定是好的直線上的點對應的累加器的值,避免再次利用這些累加值     if( good_line ) //在第一次搜索中已經確定是好的直線     {      //得到累加器矩陣地址指針      adata = (int*)accum.data;      for( n = 0; n < numangle; n++, adata += numrho )      {       r = cvRound( j1 * ttab[n*2] + i1 * ttab[n*2+1] );       r += (numrho - 1) / 2;       adata[r]--; //相應的累加器減1      }     }     //搜索過的位置,不管是好的直線,還是壞的直線,掩碼相應位置都清0,這樣下次就不會再重復搜索這些位置了,從而達到減小計算邊緣點的目的     *mdata = 0;    }    //如果已經到達了直線的端點,則退出循環    if( i1 == line_end[k].y && j1 == line_end[k].x )     break;   }  }  //如果是好的直線  if( good_line )  {   CvRect lr = { line_end[0].x, line_end[0].y, line_end[1].x, line_end[1].y };   //把兩個端點壓入序列中   cvSeqPush( lines, &lr );   //如果檢測到的直線數量大于閾值,則退出該函數   if( lines->total >= linesMax )    return;  } }}

下面就給出應用HoughLinesP函數檢測直線段的應用程序:

#include "opencv2/core/core.hpp"#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp" #include <iostream>using namespace cv;using namespace std; int main( int argc, char** argv ){ Mat src, edge,color_edge; src=imread("building.jpg"); if( !src.data )  return -1;   Canny(src,edge,50,200,3); cvtColor( edge, color_edge, CV_GRAY2BGR ); vector<Vec4i> lines; HoughLinesP(edge, lines, 1, CV_PI/180, 80, 30, 10 ); for( size_t i = 0; i < lines.size(); i++ ) {  Vec4i l = lines[i];  line( color_edge, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 2); }  namedWindow( "lines", CV_WINDOW_AUTOSIZE ); imshow( "lines", color_edge ); waitKey(0);  return 0;}

下圖為輸出的圖像:

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久国产精品久久久久| 久久在线免费视频| 亚洲成人激情在线观看| 中文在线不卡视频| 亚洲人午夜精品免费| 国产精品第1页| 欧美国产欧美亚洲国产日韩mv天天看完整| 欧美成人免费一级人片100| 韩国福利视频一区| 97成人精品区在线播放| 欧美性猛xxx| 精品一区精品二区| 亚洲精品videossex少妇| 亚州国产精品久久久| 欧美午夜宅男影院在线观看| 日韩电视剧在线观看免费网站| 国内精品国产三级国产在线专| 国产美女搞久久| 久久香蕉国产线看观看av| 日韩av不卡在线| 日韩欧美一区二区三区久久| 日本精品久久中文字幕佐佐木| 亚洲成人免费在线视频| 国产z一区二区三区| 91精品国产网站| 久久精品91久久久久久再现| 亚洲成人网在线观看| 琪琪第一精品导航| 九九九久久国产免费| 欧美成人亚洲成人| 成人精品一区二区三区| 国产精品亚洲一区二区三区| 亚洲第一视频在线观看| 亚洲美腿欧美激情另类| 欧美激情奇米色| 在线观看日韩欧美| 一区三区二区视频| 久久九九有精品国产23| 中文字幕最新精品| 热久久视久久精品18亚洲精品| 亚洲精品福利视频| 欧美日韩在线视频一区二区| 91免费国产网站| 中文字幕日本欧美| 欧美精品一本久久男人的天堂| 日产精品99久久久久久| 38少妇精品导航| 国产精品久久久久久久久借妻| 亚洲图片在线综合| 成人乱色短篇合集| 欧美性xxxx极品hd欧美风情| 亚洲成人黄色在线| 第一福利永久视频精品| 亚洲视频在线看| 亚洲老板91色精品久久| 视频在线观看一区二区| 成人国产精品日本在线| 久久精视频免费在线久久完整在线看| 国产精品电影观看| 欧美日韩一区二区在线播放| 国产精品日韩在线观看| 91视频8mav| 国产亚洲一区二区精品| 欧美亚洲另类激情另类| 日韩精品中文字幕久久臀| 亚洲欧美综合区自拍另类| 久久久久久欧美| 精品久久久久久中文字幕一区奶水| 久久午夜a级毛片| xxxx欧美18另类的高清| 亚洲男人的天堂在线播放| 亚洲欧美日韩直播| 26uuu另类亚洲欧美日本老年| 欧美特黄级在线| 亚洲人午夜精品| 日韩视频中文字幕| 亚洲精品视频在线播放| 欧美高清激情视频| 亚洲欧美日韩第一区| 色偷偷偷综合中文字幕;dd| 精品日韩美女的视频高清| 日韩电影网在线| 欧美专区国产专区| 亚洲香蕉av在线一区二区三区| 亚洲伊人久久大香线蕉av| 国产成人+综合亚洲+天堂| 亚洲久久久久久久久久| 日韩在线视频线视频免费网站| 欧美激情综合亚洲一二区| 日韩高清电影免费观看完整版| 亚洲国产成人在线播放| 日韩美女在线看| 国色天香2019中文字幕在线观看| 91精品国产成人www| 日韩精品在线观看视频| 久久999免费视频| 久久久久国色av免费观看性色| 欧美国产亚洲精品久久久8v| 欧美精品精品精品精品免费| 精品久久久久久久久久久久久| 精品久久久久久中文字幕大豆网| 久久九九精品99国产精品| 精品国产欧美一区二区五十路| 久久久久久午夜| 亚洲欧美一区二区激情| 日本精品一区二区三区在线播放视频| 亚洲国产成人精品久久| 成人黄在线观看| 欧美日韩精品中文字幕| 亚洲电影第1页| 亚洲黄色av网站| 国产成人福利夜色影视| 久久综合久久八八| www.欧美精品| 久久国产精品免费视频| 国产一区二区欧美日韩| 国产99在线|中文| 欧美日韩亚洲一区二| 国产精品 欧美在线| 精品一区二区三区四区在线| 国产成人精品在线视频| 亚洲美女激情视频| 国产精品影院在线观看| 国产区精品在线观看| 亚洲欧美激情视频| 久久九九全国免费精品观看| 欧美日韩亚洲精品一区二区三区| 久久成人综合视频| 中文字幕在线看视频国产欧美在线看完整| 日本久久中文字幕| 日韩电影免费观看中文字幕| 午夜精品一区二区三区在线| 91探花福利精品国产自产在线| 91高潮在线观看| 性亚洲最疯狂xxxx高清| 欧美性xxxxxxx| 一区二区三区www| 久久精品成人欧美大片| 91在线无精精品一区二区| 欧美成人午夜激情视频| 怡红院精品视频| 日韩欧美国产网站| 成人妇女淫片aaaa视频| 欧美性感美女h网站在线观看免费| 欧美壮男野外gaytube| 久久成年人视频| 日韩电影中文 亚洲精品乱码| 欧美在线亚洲在线| 欧美大片大片在线播放| 精品亚洲男同gayvideo网站| 日韩中文字幕在线视频播放| 国内精品久久久久影院 日本资源| 亚洲国产一区二区三区在线观看| 国产精品欧美日韩一区二区| 亚洲免费视频一区二区| 一区二区三区在线播放欧美| 黄网动漫久久久| 久久人人爽国产| 国产精品亚洲美女av网站| 国产成人精品视频在线观看| 自拍偷拍亚洲在线| 亚洲网站视频福利| 欧美国产日产韩国视频| 久久免费视频这里只有精品|