ORB-SLAM2中生成金字塔提取FAST角點和計算BRIEF描述子

//這個是類ORBextractor的帶參構造函數,并且使用初始化列表對該類中的這5個變量賦值
ORBextractor::ORBextractor(int _nfeatures, float _scaleFactor, int _nlevels,int _iniThFAST, int _minThFAST):nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),iniThFAST(_iniThFAST), minThFAST(_minThFAST)
{//mvScaleFactor是用來存儲金字塔中每層圖像對應的尺度因子的vector變量//所以他的大小就是金字塔的層數mvScaleFactor.resize(nlevels);//mvLevelSigma2是該層尺度因子的平方,有點表示面積的意思,具體含義等遇到了再解釋mvLevelSigma2.resize(nlevels);//將vector中的第一個元素值初始化為1.mvScaleFactor[0]=1.0f;mvLevelSigma2[0]=1.0f;//計算金字塔中每一層圖像對應的尺度因子和尺度因子的平方。//可以發現金字塔中第0層的尺度因子是1,然后每向上高一層,圖像的尺度因子是在上一層圖像的尺度因子 //上乘以scaleFactor,在本工程下該值為1.2//1   1*1.2   1*1.2*1.2    1*1.2*1.2*1.2   ...for(int i=1; i<nlevels; i++){mvScaleFactor[i]=mvScaleFactor[i-1]*scaleFactor;mvLevelSigma2[i]=mvScaleFactor[i]*mvScaleFactor[i];}//如果說上面是正向的尺度,那么下面的就是逆向尺度了,是正向尺度的倒數mvInvScaleFactor.resize(nlevels);mvInvLevelSigma2.resize(nlevels);for(int i=0; i<nlevels; i++){mvInvScaleFactor[i]=1.0f/mvScaleFactor[i];mvInvLevelSigma2[i]=1.0f/mvLevelSigma2[i];}//mvImagePyramid是一個存儲金字塔圖像的vector,vector中的每一個元素是用Mat數據類型表示的圖        //像, std::vector<cv::Mat> mvImagePyramid;mvImagePyramid.resize(nlevels);//mnFeaturesPerLevel是一個存儲金字塔中每層圖像應該提取的特征點的個數//std::vector<int> mnFeaturesPerLevel;mnFeaturesPerLevel.resize(nlevels);//那在這里factor = 1/1.2;float factor = 1.0f / scaleFactor;//nDesiredFeaturesPerScale是根據總的要在整幅圖像中提取的特征點數nFeatures(在這里是1000)//還有金字塔的層數來計算每層圖像上應該提取的特征點的個數。//根據下面的程序可以得知nDesiredFeaturesPerScale是第0層圖像上應該提取的特征點的個數float nDesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));int sumFeatures = 0;for( int level = 0; level < nlevels-1; level++ ){//那么mnFeaturesPerLevel[0]就是上面計算出來的這個值嘍mnFeaturesPerLevel[level] = cvRound(nDesiredFeaturesPerScale);//由于四舍五入的原因將實際計算出的數值加到一起與預設的1000比較sumFeatures += mnFeaturesPerLevel[level];//層數越高則需要提取的特征的個數就越少,并且相鄰層圖像上提取的特征點的個數存在1.2倍//關系//這里是我計算的結果//217+180+150+125+104+87+72+60 = 995nDesiredFeaturesPerScale *= factor;}//看到這里就會知道上面為啥要把計算出來的特征點個數求和的原因了,就是來決定最后//一層圖像應該提取的特征的個數了,根據計算最后一層要提取60個點,那現在就得提取65個//才能達到總共提取1000個點的要求。mnFeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);//下面這一塊是為了提取特征點做準備了,等后面再講解。const int npoints = 512;const Point* pattern0 = (const Point*)bit_pattern_31_;std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));//This is for orientation// pre-compute the end of a row in a circular patchumax.resize(HALF_PATCH_SIZE + 1);int v, v0, vmax = cvFloor(HALF_PATCH_SIZE * sqrt(2.f) / 2 + 1);int vmin = cvCeil(HALF_PATCH_SIZE * sqrt(2.f) / 2);const double hp2 = HALF_PATCH_SIZE*HALF_PATCH_SIZE;for (v = 0; v <= vmax; ++v)umax[v] = cvRound(sqrt(hp2 - v * v));// Make sure we are symmetricfor (v = HALF_PATCH_SIZE, v0 = 0; v >= vmin; --v){while (umax[v0] == umax[v0 + 1])++v0;umax[v] = v0;++v0;}
}//下面這個函數實現對輸入的圖像計算圖像金字塔的任務
void ORBextractor::ComputePyramid(cv::Mat image)
{//nlevels = 8for (int level = 0; level < nlevels; ++level){//在構造函數中已經提前定義好了每一層圖像對應的反向尺度因子float scale = mvInvScaleFactor[level];//不同的level,scale的值不同所以就算出了每一層上的圖像的大小Size sz(cvRound((float)image.cols*scale), cvRound((float)image.rows*scale));//為新生成的圖像加上邊界Size wholeSize(sz.width + EDGE_THRESHOLD*2, sz.height + EDGE_THRESHOLD*2);//根據上面的計算的尺度來創建一幅新的圖像, Mat類型//type()是Mat類中的一個成員函數,返回數據類型Mat temp(wholeSize, image.type()), masktemp;//mvImagePyramid是用來存儲每一層圖像的vector變量,為他的每一個元素設置特定大小的圖像//mvImagePyramid[0]中存儲的是原圖像//通過Rect定義temp圖像的左上側起點和右下側終點mvImagePyramid[level] = temp(Rect(EDGE_THRESHOLD, EDGE_THRESHOLD, sz.width, sz.height));// Compute the resized imageif( level != 0 ){//mvImagePyramid[1]開始,都是由上一層的圖像的尺寸得到// dsize = Size(round(fx*src.cols), round(fy*src.rows))//dsize是輸出圖像的大小,按照上面的計算公式計算得到了已經//resize(InputArry src, Output dst, Size dsize, double fx = 0, double fy = 0, //int interpolation = INTER_LINEAR)resize(mvImagePyramid[level-1], mvImagePyramid[level], sz, 0, 0, INTER_LINEAR);//將設置出的圖像分別拷貝到相應的層上去copyMakeBorder(mvImagePyramid[level], temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD,BORDER_REFLECT_101+BORDER_ISOLATED);            }else{copyMakeBorder(image, temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD,BORDER_REFLECT_101);            }}}//函數的三要素是:函數名稱,函數參數, 函數返回值
//ComputeKeyPointsOctTree是類ORBextractor的成員函數
//參數是vector類型的引用變量allKeypoints.
//返回值是void類型
//在參數文件TUM1.yaml下預定義了一些變量的值
//ORBextractor. nFeatures: 1000
//ORBextractor. scaleFactor: 1.2
//ORBextractor. nlevels: 8
//ORBextractor. iniThFAST: 20
//ORBextractor. minThFAST: 7
//在ORBextractor.h中用帶參構造函數來初始化類ORBextractor中的相應的變量
//在類ORBextractor中還有一串變量
//std::vector<float> mvScaleFactor;
//std::vector<float> mvInvScaleFactor;
//std::vector<float> mvlevelSifma2;
//std::vector<float> mvInvLevelSigma2;
//如果說第一組變量是金字塔中某一層圖像的屬性,那么第二組是成員變量是一幅圖像的屬性。
void ORBextractor::ComputeKeyPointsOctTree(vector<vector<KeyPoint> >& allKeypoints)
{//通過vector中的resize函數來重新將vector變量allKeypoints的大小設置為nlevels.//nlevels也就是圖像金字塔中圖像的層數allKeypoints.resize(nlevels);const float W = 30;//對金字塔中的每一層圖像進行一系列的操作for (int level = 0; level < nlevels; ++level){//EDGE_THRESHOLD=19//minBorderX, minBorderY, maxBorderX, maxBorderY四個變量一起設定了用于提取特征的區域const int minBorderX = EDGE_THRESHOLD-3;const int minBorderY = minBorderX;const int maxBorderX = mvImagePyramid[level].cols-EDGE_THRESHOLD+3;const int maxBorderY = mvImagePyramid[level].rows-EDGE_THRESHOLD+3;//定義變量vToDistributeKeys存儲從每一層圖像中提取出來的特征。//vector中存儲的數據類型是在opencv中定義的KeyPoint類vector<cv::KeyPoint> vToDistributeKeys;//reserve:分配空間,更改capacity但是不改變size 預留空間//resize:分配空間,同時改變capacity和sizevToDistributeKeys.reserve(nfeatures*10);//丈量了可以用來進行操作的“場地”大小const float width = (maxBorderX-minBorderX);const float height = (maxBorderY-minBorderY);//預將圖像劃分為30*30的網狀//計算每個小格子的長和寬各占多少個像素const int nCols = width/W;const int nRows = height/W;///計算最終長和寬被分成了多少個小格子cellconst int wCell = ceil(width/nCols);const int hCell = ceil(height/nRows);//通過控制i來縱向遍歷每一個cellfor(int i=0; i<nRows; i++){const float iniY =minBorderY+i*hCell;//這里maxY相當于給當前遍歷的這個cell加上一個外框float maxY = iniY+hCell+6;if(iniY>=maxBorderY-3)continue;if(maxY>maxBorderY)maxY = maxBorderY;//通過控制j來橫向遍歷每一個cell.for(int j=0; j<nCols; j++){const float iniX =minBorderX+j*wCell;float maxX = iniX+wCell+6;if(iniX>=maxBorderX-6)continue;if(maxX>maxBorderX)maxX = maxBorderX;//通過上面的兩個for循環就可以完成遍歷該層圖像中所有的cell.//vKeysCell用來盛放該cell中提取的特征點vector<cv::KeyPoint> vKeysCell;//變量i和j的組合控制,當遍歷到(i, j)個cell時,就提取這個cell下的FAST角點//如下是opencv中FAST函數的原型//輸入圖像,輸出提取的特征點, 選取特征點的閾值///FAST( InputArray image, CV_OUT vector<KeyPoint>& keypoints,/// int threshold, bool nonmaxSuppression=true );FAST(mvImagePyramid[level].rowRange(iniY,maxY).colRange(iniX,maxX),vKeysCell,iniThFAST,true);if(vKeysCell.empty()){///如果按照上面的方法在這個cell中提取不到特征點,那么就放低要求,//用minThFAST閾值來提取FAST角點FAST(mvImagePyramid[level].rowRange(iniY,maxY).colRange(iniX,maxX),vKeysCell,minThFAST,true);}if(!vKeysCell.empty()){//如果已經提取到關鍵點,那么就遍歷這些所有提取的關鍵點for(vector<cv::KeyPoint>::iterator vit=vKeysCell.begin(); vit!=vKeysCell.end();vit++){
///迭代器vit就相當于一個指向vector中存儲的KeyPoint的指針,通過*vit就可以獲取指針所指向的的
//特定的點//pt表示KeyPoint的位置屬性//KeyPoint是opencv中的一個類,pt是該類中的一個屬性,獲取獲取關鍵點的坐標
//因為單純的(*vit).pt.x和(*vit).pt.y表示在當前cell下的坐標,還要轉化為在可提取特征范圍內的坐標(*vit).pt.x+=j*wCell;(*vit).pt.y+=i*hCell;//把從每個cell中提取的特征點都存儲到vector變量vToDistributeKeys中去vToDistributeKeys.push_back(*vit);}}}}
//截止到這里已經將該層圖像中的所有cell遍歷結束并且,將提取的所有的特征點都已經存儲到vector
//變量vToDistributeKeys中去了//vector<vector<KeyPoint> >& allKeypoints//allKeypoints是一個用來存儲vector的vector//allKeypoints的大小是金字塔的層數nlevels//allKeypoints[level]是一個對應于每層圖像上提取的特征點的vector//allKeypoints[level].size也就是在該層上要提取的特征點的個數vector<KeyPoint> & keypoints = allKeypoints[level];//keypoints的預留內存是每幅圖像上所有要提取的特征數。keypoints.reserve(nfeatures);所有提取的關鍵點,提取的范圍,是從哪一層上提取的特征///下面這部分是將提取的關鍵點進行八叉樹優化keypoints = DistributeOctTree(vToDistributeKeys, minBorderX, maxBorderX,minBorderY, maxBorderY,mnFeaturesPerLevel[level], level);///PATCH_SIZE指代什么呢level=0表示原圖像,隨著層數的增加圖像越來越小,那么在每幅圖像上提取的特征個數
//也會相應的減少// PATCH_SIZE = 31.//vector變量mvScaleFactor中存儲了一幅圖像對應的一個金字塔中所有層圖像的尺度因子//不同層圖像的尺度因子不同,那么在該層中提取的特征點所對應的有效區域就不同。const int scaledPatchSize = PATCH_SIZE*mvScaleFactor[level];//nkps表示keypoints中的特征點的個數const int nkps = keypoints.size();for(int i=0; i<nkps ; i++){///遍歷在該層圖像上提取的所有的特征點,在這些特征點坐標上都加上整幅圖像的邊界信息就可以
//得到關鍵點在整幅圖像中的坐標keypoints[i].pt.x+=minBorderX;keypoints[i].pt.y+=minBorderY;//類KeyPoint一個成員變量octave用來存儲該特征點是在哪一層圖像上提取得到的。//我們之后在使用某一個特征點的時候就可以直接通過特的這個屬性來知道他是從
//哪一層圖像上提取出來的。keypoints[i].octave=level;///這每一個關鍵點的size該如何理解呢,大概是根據近大遠小的原理來計算的。在不同層圖像上提取的點所表征的面積范圍不同。keypoints[i].size = scaledPatchSize;}}// 遍歷每一層圖像以及在該層上提取的特征點,計算每個特征點的方向。for (int level = 0; level < nlevels; ++level)computeOrientation(mvImagePyramid[level], allKeypoints[level], umax);
}

總結:

如何更以更清晰的思路來分析和學習ORB-SLAM呢,我認為一種好的方式就是編譯器如何利用C++這種工具來創建和維護該工程下一些變量的。如用來檢測閉環和重定位,local BA,full BA,用來描述系統中關鍵幀之間的共視關系的covisibility graph, 以及由covisibility graph得到的 Enssential graph進行位姿圖優化。 以上主要是利用C++下的強大的STL中的容器的概念來創建和存儲這些關系如 std::map<KeyFrame* int> mConnectedKeyFrameWeights; 用map來存儲了與當前幀有共視關系的關鍵幀以及共視的地圖點的個數。

要知道幀與幀的聯系是通過地圖點建立起來的,并且在創建特征點的時候為這個地圖點添加observation屬性,用map來存儲這個地圖點可以被那個關鍵幀觀測到,以及對應于該圖像中的特征點的index.

而對于新進來的一幅圖像我們要對她進行預處理,創建金字塔,提取FAST角點和計算BRIEF描述子,

用變量std::vector<cv::Mat> mvImagePyramid 來存儲計算出來的金字塔中的每一層圖像

用變量std::vector<std::vector>allKeypoints來存儲金字塔中每一層圖像中提取的特征點。

用變量std::vector<float> mvScaleFactor來存儲金字塔中每一層圖像對應的尺度因子

用變量std::vector<int> mnFeaturesPerLevel來存儲金字塔中每一幅圖像應該提取的特征點的個數

最后這兩個變量都是在類ORBextractor中的構造函數中計算初始化的。

輸入一幅圖像首先要計算8層金字塔,然后是針對于金字塔中的每一層圖像劃分為grid來提取特征點, 然后將這些特征點用八叉樹進行優化,然后在計算描述子,和觀測方向。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/252515.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/252515.shtml
英文地址,請注明出處:http://en.pswp.cn/news/252515.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

我們怎樣確保從大數據計算中獲得價值

我們怎樣確保從大數據計算中獲得價值 支持大數據方案并不是在硬件以及軟件層次終止&#xff0c;企業要想真正地從大數據中受益&#xff0c;領導者必須改變思考與對待信息的方式。 我們怎樣確保從大數據計算中獲得價值&#xff1f; 當所有可用數據都可用時&#xff0c;大數據…

jsoncpp-src-0.5.0.tar.gz 源碼錯誤!!!!

近期在做畢設&#xff0c;使用到了JsonCpp0.5.0版本號的源碼&#xff01;依照網上的安裝配置教程&#xff0c;搭建好環境后就能夠使用了&#xff01; 在這里就不浪費空間去將怎樣搭建開發環境了&#xff01;請大家去google一下就好了&#xff01;在解析一個Json文件時。程序總是…

青海省多地日降水量突破歷史極值

受高原槽和西北冷空氣的共同影響&#xff0c;青海省海西州茫崖等多地日降水量突破歷史極值。 李萬花 攝 受高原槽和西北冷空氣的共同影響&#xff0c;青海省海西州茫崖等多地日降水量突破歷史極值。 李萬花 攝 中新網西寧1月18日電 (孫睿 趙海梅)記者18日從青海省氣象局獲悉&am…

ORB-SLAM2中四叉樹管理特征點

當從圖像金字塔中的每一層圖像上提取特征點之后&#xff0c;都要先用四叉樹技術對這些特征點進行管理 //該類中定義了四叉樹創建的函數以及樹中結點的屬性 //bool bNoMore&#xff1a; 根據該結點中被分配的特征點的數目來決定是否繼續對其進行分割 //DivisionNode()&#xff…

Python多線程3:queue

queue模塊實現了多生產者。多消費者隊列。在多線程環境下&#xff0c;該隊列能實現多個線程間安全的信息交換。 queue模塊介紹 模塊實現了3種類型的隊列&#xff0c;差別在于隊列中條目檢索的順序不同。在FIFO隊列中。依照先進先出的順序檢索條目。在LIFO隊列中&#xff0c;最后…

微信小程序教程02:App(Object)和Page(Object) 構造器介紹

在/app.js中&#xff0c;有方法App&#xff0c;它的作用是注冊整個小程序的應用&#xff0c;其中可以傳入一些配置&#xff0c;或者存儲全局狀態。 App(Object) 構造器生命周期 屬性類型描述onLaunchFunction在小程序初始化時觸發&#xff0c;全局僅觸發一次onShowFunction小程…

阿里云.log

申請證書審核失敗的原因及處理方法;( 新添加站點 免費版 SSL 網頁內不能有 HTTPS的連接&#xff1b;更多點擊連接) 轉載于:https://www.cnblogs.com/q1104460935/p/8287377.html

SharePoint Search之(七)Search result- 結果源

在使用搜索引擎的時候。非常多情況下&#xff0c;用戶希望限定一下搜索范圍&#xff0c;以便更加easy找到想要的結果。在SharePoint 2013的search里&#xff0c;也支持類似的功能&#xff0c;SharePoint 默認提供了幾種范圍&#xff1a; 在SharePoint&#xff0c;這個叫Search …

曠視砸20億進軍AIoT,發布國內首個機器人協作大腦河圖

1 月 16 日&#xff0c;人工智能獨角獸曠視科技發布了機器人戰略&#xff0c;以及自 2018 年 4 月收購艾瑞思機器人&#xff0c;進軍機器人領域的最新進展——智能協同大腦河圖。在會上&#xff0c;曠視還大筆一揮&#xff0c;決定投入 20 億元&#xff0c;用于打造物流倉儲上下…

ORB-SLAM2-金字塔求解-特征點的提取-描述子的計算

//這個成員函數重載了函數括號運算符&#xff0c;讓他具有函數的特點 //但是還不知道在其他程序塊是如何應用這塊代碼的。 //InputArray和OutputArray是opencv中的兩個函數接口 void ORBextractor::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>&a…

am335x uboot, kernel 編譯

一、設置環境變量// 寫在家目錄下面的 .bashrc 里面export KERNEL_PATH~/aplex/kernel3.2.0 // kernel 路徑export UBOOT_PATH~/aplex/uboot2011.09 // u-boot 路勁export ROOTFS_PATH~/aplex/filesystemexport TOOLFS_PATH~/aplex/toolsexport ARCHarm …

php+ajax簡單實現跨域(http+https)請求調用

當一個網站 a站 需要調用另一個網站 b站 列表文章時 比如&#xff1a;www.a123.com 調用 www.b456.com 文章 在 a站 建立php文件獲取 b站 資源文章到本地后&#xff0c;再傳遞a站前端 在網站 b456 下的文件為 <ul class"ls_wz"> <li><a href"#&q…

ORB-SLAM2中MapPoints的描述子的計算

//我們在從金字塔的圖像中獲取特征點時為每一個特征點計算了描述子 //現在看看如何計算一個空間的地圖點的描述子 void MapPoint::ComputeDistinctiveDescriptors() {// Retrieve all observed descriptorsvector<cv::Mat> vDescriptors;//獲取到某一個地圖點可以被哪些關…

HDU:4185-Oil Skimming

Oil Skimming Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description Thanks to a certain “green” resources company, there is a new profitable industry of oil skimming. There are large slicks of crude oil floa…

域控制器情況分析

域控制器情況分析 1、Windows Server 的 Foundation、Standard、Enterprise 以及 Datacenter 版本號既可作為源server&#xff0c;也可作為目標server。僅支持將 Foundation Server 版本號作為受限方案中的目標server。在使用 Foundation Server 作為目標server之前&#xff0c…

Linux基礎命令---su

su臨時切換身份到另外一個用戶&#xff0c;使用su切換用戶之后&#xff0c;不會改變當前的工作目錄&#xff0c;但是會改變一些環境變量。此命令的適用范圍&#xff1a;RedHat、RHEL、Ubuntu、CentOS、SUSE、openSUSE、Fedora。1、語法su [選項] [參數]2、選項列表--help顯示…

在Ubuntu 16.04 上安裝和卸載matlab 2018b(Install and uninstall matlab 2018b on ubuntu)

1.安裝2018b可以參考下面兩篇文章 https://www.ph0en1x.space/2018/04/23/ubuntu_matlab/ https://blog.csdn.net/qq_32892383/article/details/79670871 2.卸載2018b 我的默認安裝在 /usr/local/MATLAB $ sudo rm -r /usr/local/MATLAB $ cd ~ $ ll (這個時候可以看到隱…

04.openssl編程——哈希表

4.1 哈希表在一般的數據結構如線性表和樹中&#xff0c;記錄在結構中的相對位置與記錄的關鍵字之間不存在確定的關系&#xff0c;在結構中查找記錄時需要進行一系列的關鍵字比較。這一類查找方法建立在比較的基礎上&#xff0c;查找的效率與比較次數密切相關。理想的情況是能…

shell數組中“和@的妙用

#!/bin/bashlist(4k"8k a bit""16k abc""32k gold"64k)for i in "${list[]}"do echo $idone 分別對比一下不帶” 和換成*&#xff0c;之間的區別。轉載于:https://www.cnblogs.com/zjd2626/p/7041341.html