日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2023/12/10 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ORB-SLAM2中生成金字塔提取FAST角点和计算BRIEF描述子 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
//這個(gè)是類ORBextractor的帶參構(gòu)造函數(shù),并且使用初始化列表對該類中的這5個(gè)變量賦值 ORBextractor::ORBextractor(int _nfeatures, float _scaleFactor, int _nlevels,int _iniThFAST, int _minThFAST):nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),iniThFAST(_iniThFAST), minThFAST(_minThFAST) {//mvScaleFactor是用來存儲金字塔中每層圖像對應(yīng)的尺度因子的vector變量//所以他的大小就是金字塔的層數(shù)mvScaleFactor.resize(nlevels);//mvLevelSigma2是該層尺度因子的平方,有點(diǎn)表示面積的意思,具體含義等遇到了再解釋mvLevelSigma2.resize(nlevels);//將vector中的第一個(gè)元素值初始化為1.mvScaleFactor[0]=1.0f;mvLevelSigma2[0]=1.0f;//計(jì)算金字塔中每一層圖像對應(yīng)的尺度因子和尺度因子的平方。//可以發(fā)現(xiàn)金字塔中第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];}//如果說上面是正向的尺度,那么下面的就是逆向尺度了,是正向尺度的倒數(shù)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是一個(gè)存儲金字塔圖像的vector,vector中的每一個(gè)元素是用Mat數(shù)據(jù)類型表示的圖 //像, std::vector<cv::Mat> mvImagePyramid;mvImagePyramid.resize(nlevels);//mnFeaturesPerLevel是一個(gè)存儲金字塔中每層圖像應(yīng)該提取的特征點(diǎn)的個(gè)數(shù)//std::vector<int> mnFeaturesPerLevel;mnFeaturesPerLevel.resize(nlevels);//那在這里factor = 1/1.2;float factor = 1.0f / scaleFactor;//nDesiredFeaturesPerScale是根據(jù)總的要在整幅圖像中提取的特征點(diǎn)數(shù)nFeatures(在這里是1000)//還有金字塔的層數(shù)來計(jì)算每層圖像上應(yīng)該提取的特征點(diǎn)的個(gè)數(shù)。//根據(jù)下面的程序可以得知nDesiredFeaturesPerScale是第0層圖像上應(yīng)該提取的特征點(diǎn)的個(gè)數(shù)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]就是上面計(jì)算出來的這個(gè)值嘍mnFeaturesPerLevel[level] = cvRound(nDesiredFeaturesPerScale);//由于四舍五入的原因?qū)?shí)際計(jì)算出的數(shù)值加到一起與預(yù)設(shè)的1000比較sumFeatures += mnFeaturesPerLevel[level];//層數(shù)越高則需要提取的特征的個(gè)數(shù)就越少,并且相鄰層圖像上提取的特征點(diǎn)的個(gè)數(shù)存在1.2倍//關(guān)系//這里是我計(jì)算的結(jié)果//217+180+150+125+104+87+72+60 = 995nDesiredFeaturesPerScale *= factor;}//看到這里就會知道上面為啥要把計(jì)算出來的特征點(diǎn)個(gè)數(shù)求和的原因了,就是來決定最后//一層圖像應(yīng)該提取的特征的個(gè)數(shù)了,根據(jù)計(jì)算最后一層要提取60個(gè)點(diǎn),那現(xiàn)在就得提取65個(gè)//才能達(dá)到總共提取1000個(gè)點(diǎn)的要求。mnFeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);//下面這一塊是為了提取特征點(diǎn)做準(zhǔn)備了,等后面再講解。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;} }//下面這個(gè)函數(shù)實(shí)現(xiàn)對輸入的圖像計(jì)算圖像金字塔的任務(wù) void ORBextractor::ComputePyramid(cv::Mat image) {//nlevels = 8for (int level = 0; level < nlevels; ++level){//在構(gòu)造函數(shù)中已經(jīng)提前定義好了每一層圖像對應(yīng)的反向尺度因子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);//根據(jù)上面的計(jì)算的尺度來創(chuàng)建一幅新的圖像, Mat類型//type()是Mat類中的一個(gè)成員函數(shù),返回?cái)?shù)據(jù)類型Mat temp(wholeSize, image.type()), masktemp;//mvImagePyramid是用來存儲每一層圖像的vector變量,為他的每一個(gè)元素設(shè)置特定大小的圖像//mvImagePyramid[0]中存儲的是原圖像//通過Rect定義temp圖像的左上側(cè)起點(diǎn)和右下側(cè)終點(diǎn)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是輸出圖像的大小,按照上面的計(jì)算公式計(jì)算得到了已經(jīng)//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);//將設(shè)置出的圖像分別拷貝到相應(yīng)的層上去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); }}}//函數(shù)的三要素是:函數(shù)名稱,函數(shù)參數(shù), 函數(shù)返回值 //ComputeKeyPointsOctTree是類ORBextractor的成員函數(shù) //參數(shù)是vector類型的引用變量allKeypoints. //返回值是void類型 //在參數(shù)文件TUM1.yaml下預(yù)定義了一些變量的值 //ORBextractor. nFeatures: 1000 //ORBextractor. scaleFactor: 1.2 //ORBextractor. nlevels: 8 //ORBextractor. iniThFAST: 20 //ORBextractor. minThFAST: 7 //在ORBextractor.h中用帶參構(gòu)造函數(shù)來初始化類ORBextractor中的相應(yīng)的變量 //在類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函數(shù)來重新將vector變量allKeypoints的大小設(shè)置為nlevels.//nlevels也就是圖像金字塔中圖像的層數(shù)allKeypoints.resize(nlevels);const float W = 30;//對金字塔中的每一層圖像進(jìn)行一系列的操作for (int level = 0; level < nlevels; ++level){//EDGE_THRESHOLD=19//minBorderX, minBorderY, maxBorderX, maxBorderY四個(gè)變量一起設(shè)定了用于提取特征的區(qū)域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中存儲的數(shù)據(jù)類型是在opencv中定義的KeyPoint類vector<cv::KeyPoint> vToDistributeKeys;//reserve:分配空間,更改capacity但是不改變size 預(yù)留空間//resize:分配空間,同時(shí)改變capacity和sizevToDistributeKeys.reserve(nfeatures*10);//丈量了可以用來進(jìn)行操作的“場地”大小const float width = (maxBorderX-minBorderX);const float height = (maxBorderY-minBorderY);//預(yù)將圖像劃分為30*30的網(wǎng)狀//計(jì)算每個(gè)小格子的長和寬各占多少個(gè)像素const int nCols = width/W;const int nRows = height/W;///計(jì)算最終長和寬被分成了多少個(gè)小格子cellconst int wCell = ceil(width/nCols);const int hCell = ceil(height/nRows);//通過控制i來縱向遍歷每一個(gè)cellfor(int i=0; i<nRows; i++){const float iniY =minBorderY+i*hCell;//這里maxY相當(dāng)于給當(dāng)前遍歷的這個(gè)cell加上一個(gè)外框float maxY = iniY+hCell+6;if(iniY>=maxBorderY-3)continue;if(maxY>maxBorderY)maxY = maxBorderY;//通過控制j來橫向遍歷每一個(gè)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;//通過上面的兩個(gè)for循環(huán)就可以完成遍歷該層圖像中所有的cell.//vKeysCell用來盛放該cell中提取的特征點(diǎn)vector<cv::KeyPoint> vKeysCell;//變量i和j的組合控制,當(dāng)遍歷到(i, j)個(gè)cell時(shí),就提取這個(gè)cell下的FAST角點(diǎn)//如下是opencv中FAST函數(shù)的原型//輸入圖像,輸出提取的特征點(diǎn), 選取特征點(diǎn)的閾值///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()){///如果按照上面的方法在這個(gè)cell中提取不到特征點(diǎn),那么就放低要求,//用minThFAST閾值來提取FAST角點(diǎn)FAST(mvImagePyramid[level].rowRange(iniY,maxY).colRange(iniX,maxX),vKeysCell,minThFAST,true);}if(!vKeysCell.empty()){//如果已經(jīng)提取到關(guān)鍵點(diǎn),那么就遍歷這些所有提取的關(guān)鍵點(diǎn)for(vector<cv::KeyPoint>::iterator vit=vKeysCell.begin(); vit!=vKeysCell.end();vit++){ ///迭代器vit就相當(dāng)于一個(gè)指向vector中存儲的KeyPoint的指針,通過*vit就可以獲取指針?biāo)赶虻牡?//特定的點(diǎn)//pt表示KeyPoint的位置屬性//KeyPoint是opencv中的一個(gè)類,pt是該類中的一個(gè)屬性,獲取獲取關(guān)鍵點(diǎn)的坐標(biāo) //因?yàn)閱渭兊?*vit).pt.x和(*vit).pt.y表示在當(dāng)前cell下的坐標(biāo),還要轉(zhuǎn)化為在可提取特征范圍內(nèi)的坐標(biāo)(*vit).pt.x+=j*wCell;(*vit).pt.y+=i*hCell;//把從每個(gè)cell中提取的特征點(diǎn)都存儲到vector變量vToDistributeKeys中去vToDistributeKeys.push_back(*vit);}}}} //截止到這里已經(jīng)將該層圖像中的所有cell遍歷結(jié)束并且,將提取的所有的特征點(diǎn)都已經(jīng)存儲到vector //變量vToDistributeKeys中去了//vector<vector<KeyPoint> >& allKeypoints//allKeypoints是一個(gè)用來存儲vector的vector//allKeypoints的大小是金字塔的層數(shù)nlevels//allKeypoints[level]是一個(gè)對應(yīng)于每層圖像上提取的特征點(diǎn)的vector//allKeypoints[level].size也就是在該層上要提取的特征點(diǎn)的個(gè)數(shù)vector<KeyPoint> & keypoints = allKeypoints[level];//keypoints的預(yù)留內(nèi)存是每幅圖像上所有要提取的特征數(shù)。keypoints.reserve(nfeatures);所有提取的關(guān)鍵點(diǎn),提取的范圍,是從哪一層上提取的特征///下面這部分是將提取的關(guān)鍵點(diǎn)進(jìn)行八叉樹優(yōu)化keypoints = DistributeOctTree(vToDistributeKeys, minBorderX, maxBorderX,minBorderY, maxBorderY,mnFeaturesPerLevel[level], level);///PATCH_SIZE指代什么呢level=0表示原圖像,隨著層數(shù)的增加圖像越來越小,那么在每幅圖像上提取的特征個(gè)數(shù) //也會相應(yīng)的減少// PATCH_SIZE = 31.//vector變量mvScaleFactor中存儲了一幅圖像對應(yīng)的一個(gè)金字塔中所有層圖像的尺度因子//不同層圖像的尺度因子不同,那么在該層中提取的特征點(diǎn)所對應(yīng)的有效區(qū)域就不同。const int scaledPatchSize = PATCH_SIZE*mvScaleFactor[level];//nkps表示keypoints中的特征點(diǎn)的個(gè)數(shù)const int nkps = keypoints.size();for(int i=0; i<nkps ; i++){///遍歷在該層圖像上提取的所有的特征點(diǎn),在這些特征點(diǎn)坐標(biāo)上都加上整幅圖像的邊界信息就可以 //得到關(guān)鍵點(diǎn)在整幅圖像中的坐標(biāo)keypoints[i].pt.x+=minBorderX;keypoints[i].pt.y+=minBorderY;//類KeyPoint一個(gè)成員變量octave用來存儲該特征點(diǎn)是在哪一層圖像上提取得到的。//我們之后在使用某一個(gè)特征點(diǎn)的時(shí)候就可以直接通過特的這個(gè)屬性來知道他是從 //哪一層圖像上提取出來的。keypoints[i].octave=level;///這每一個(gè)關(guān)鍵點(diǎn)的size該如何理解呢,大概是根據(jù)近大遠(yuǎn)小的原理來計(jì)算的。在不同層圖像上提取的點(diǎn)所表征的面積范圍不同。keypoints[i].size = scaledPatchSize;}}// 遍歷每一層圖像以及在該層上提取的特征點(diǎn),計(jì)算每個(gè)特征點(diǎn)的方向。for (int level = 0; level < nlevels; ++level)computeOrientation(mvImagePyramid[level], allKeypoints[level], umax); }

總結(jié):

如何更以更清晰的思路來分析和學(xué)習(xí)ORB-SLAM呢,我認(rèn)為一種好的方式就是編譯器如何利用C++這種工具來創(chuàng)建和維護(hù)該工程下一些變量的。如用來檢測閉環(huán)和重定位,local BA,full BA,用來描述系統(tǒng)中關(guān)鍵幀之間的共視關(guān)系的covisibility graph, 以及由covisibility graph得到的 Enssential graph進(jìn)行位姿圖優(yōu)化。 以上主要是利用C++下的強(qiáng)大的STL中的容器的概念來創(chuàng)建和存儲這些關(guān)系如 std::map<KeyFrame* int> mConnectedKeyFrameWeights; 用map來存儲了與當(dāng)前幀有共視關(guān)系的關(guān)鍵幀以及共視的地圖點(diǎn)的個(gè)數(shù)。

要知道幀與幀的聯(lián)系是通過地圖點(diǎn)建立起來的,并且在創(chuàng)建特征點(diǎn)的時(shí)候?yàn)檫@個(gè)地圖點(diǎn)添加observation屬性,用map來存儲這個(gè)地圖點(diǎn)可以被那個(gè)關(guān)鍵幀觀測到,以及對應(yīng)于該圖像中的特征點(diǎn)的index.

而對于新進(jìn)來的一幅圖像我們要對她進(jìn)行預(yù)處理,創(chuàng)建金字塔,提取FAST角點(diǎn)和計(jì)算BRIEF描述子,

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

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

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

用變量std::vector<int> mnFeaturesPerLevel來存儲金字塔中每一幅圖像應(yīng)該提取的特征點(diǎn)的個(gè)數(shù)

最后這兩個(gè)變量都是在類ORBextractor中的構(gòu)造函數(shù)中計(jì)算初始化的。

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

總結(jié)

以上是生活随笔為你收集整理的ORB-SLAM2中生成金字塔提取FAST角点和计算BRIEF描述子的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。