最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Opencv2.4.9函數(shù)HoughLinesP分析

 更新時(shí)間:2019年01月12日 12:07:18   作者:zhaocj  
這篇文章主要為大家詳細(xì)介紹了Opencv2.4.9函數(shù)HoughLinesP,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

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

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

1、隨機(jī)抽取圖像中的一個(gè)特征點(diǎn),即邊緣點(diǎn),如果該點(diǎn)已經(jīng)被標(biāo)定為是某一條直線上的點(diǎn),則繼續(xù)在剩下的邊緣點(diǎn)中隨機(jī)抽取一個(gè)邊緣點(diǎn),直到所有邊緣點(diǎn)都抽取完了為止;

2、對(duì)該點(diǎn)進(jìn)行霍夫變換,并進(jìn)行累加和計(jì)算;

3、選取在霍夫空間內(nèi)值最大的點(diǎn),如果該點(diǎn)大于閾值的,則進(jìn)行步驟4,否則回到步驟1;

4、根據(jù)霍夫變換得到的最大值,從該點(diǎn)出發(fā),沿著直線的方向位移,從而找到直線的兩個(gè)端點(diǎn);

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

HoughLinesP函數(shù)的原型為:

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

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

lines為輸出的直線向量,每條線用4個(gè)元素表示,即直線的兩個(gè)端點(diǎn)的4個(gè)坐標(biāo)值

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

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

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

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

HoughLinesP函數(shù)是在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函數(shù)可以看出,該函數(shù)會(huì)調(diào)用cvHoughLines2函數(shù)。它通過參數(shù)CV_HOUGH_PROBABILISTIC,最終調(diào)用了icvHoughLinesProbabilistic函數(shù):

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

下面就給出應(yīng)用HoughLinesP函數(shù)檢測直線段的應(yīng)用程序:

#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;
}

下圖為輸出的圖像:

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C++ STL之list雙向鏈表容器方式

    C++ STL之list雙向鏈表容器方式

    這篇文章主要介紹了C++ STL之list雙向鏈表容器方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • C語言詳細(xì)分析講解關(guān)鍵字const與volatile的用法

    C語言詳細(xì)分析講解關(guān)鍵字const與volatile的用法

    在C語言中,我們經(jīng)常會(huì)見到const和volatile這兩個(gè)關(guān)鍵字,那么我們今天就來介紹下這兩個(gè)關(guān)鍵字,提起?const?關(guān)鍵字,我們可能首先想到的是經(jīng)過它修飾的變量便是常量了。其實(shí)我們這種想法是錯(cuò)誤的,其實(shí)?const?修飾的變量是只讀的,其本質(zhì)還是變量
    2022-04-04
  • 用C語言求解第N項(xiàng)斐波那契數(shù)列問題

    用C語言求解第N項(xiàng)斐波那契數(shù)列問題

    這篇文章主要介紹了用C語言求解第N項(xiàng)斐波那契數(shù)列問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • C++中靜態(tài)庫與動(dòng)態(tài)庫的使用示例

    C++中靜態(tài)庫與動(dòng)態(tài)庫的使用示例

    在C/C++中使用庫的技術(shù),庫主要分為兩種類型:靜態(tài)庫和動(dòng)態(tài)庫,本文主要介紹了C++中靜態(tài)庫與動(dòng)態(tài)庫的使用示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • 使用C語言繪制統(tǒng)計(jì)圖中的餅圖

    使用C語言繪制統(tǒng)計(jì)圖中的餅圖

    常用的統(tǒng)計(jì)圖有條形圖、柱形圖、折線圖、曲線圖、餅圖、環(huán)形圖、扇形圖,本文主要為大家詳細(xì)介紹了如何使用使用C語言繪制統(tǒng)計(jì)圖中的餅圖,希望對(duì)大家有所幫助
    2024-02-02
  • 詳解C語言之緩沖區(qū)溢出

    詳解C語言之緩沖區(qū)溢出

    緩沖區(qū)是一塊連續(xù)的計(jì)算機(jī)內(nèi)存區(qū)域,可保存相同數(shù)據(jù)類型的多個(gè)實(shí)例。緩沖區(qū)可以是堆棧、堆和靜態(tài)數(shù)據(jù)區(qū)。在C/C++語言中,通常使用字符數(shù)組和malloc/new實(shí)現(xiàn)緩沖區(qū)。溢出指數(shù)據(jù)被添加到分配給該緩沖區(qū)的內(nèi)存塊之外。緩沖區(qū)溢出是最常見的程序缺陷
    2021-06-06
  • C++連接并使用MySQL數(shù)據(jù)庫

    C++連接并使用MySQL數(shù)據(jù)庫

    這篇文章主要為大家詳細(xì)介紹了C++連接并使用MySQL數(shù)據(jù)庫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • C++?Cartographer源碼中關(guān)于MapBuilder的聲明與構(gòu)造

    C++?Cartographer源碼中關(guān)于MapBuilder的聲明與構(gòu)造

    這篇文章主要介紹了C++?Cartographer源碼中關(guān)于MapBuilder的聲明與構(gòu)造,前面已經(jīng)談到了Cartographer中添加軌跡的方法和傳感器的數(shù)據(jù)流動(dòng)走向。在添加軌跡的時(shí)候,除了添加位姿估計(jì)器還有采樣器,訂閱回調(diào)函數(shù)之外,最重要的是通過map_builder_bridge添加了一條軌跡
    2023-03-03
  • C++中的強(qiáng)制類型轉(zhuǎn)換操作詳解

    C++中的強(qiáng)制類型轉(zhuǎn)換操作詳解

    C++中提供了四種強(qiáng)制類型轉(zhuǎn)換技術(shù):static_cast、dynamic_cast、reinterpret_cast和const_cast。這些技術(shù)能夠在需要時(shí)將一種類型轉(zhuǎn)換為另一種類型,但需要注意它們的適用條件和安全性。程序員需要根據(jù)具體情況選擇合適的強(qiáng)制類型轉(zhuǎn)換方式,以確保程序的正確性和可靠性
    2023-04-04
  • C++11非受限聯(lián)合體的使用

    C++11非受限聯(lián)合體的使用

    本文主要介紹了C++11非受限聯(lián)合體的使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01

最新評(píng)論

盐山县| 遂川县| 洛隆县| 西乌珠穆沁旗| 江陵县| 尼勒克县| 寻乌县| 安阳县| 汾西县| 盐城市| 昌乐县| 惠安县| 靖西县| 常熟市| 康乐县| 武川县| 莒南县| 永胜县| 科技| 内丘县| 县级市| 合作市| 汪清县| 加查县| 乌什县| 波密县| 株洲市| 揭西县| 雅江县| 宜阳县| 德清县| 青海省| 沿河| 定兴县| 桐梓县| 保康县| 南丰县| 大城县| 祁阳县| 江津市| 阿拉善右旗|