一财骨、通俗易懂理解圖像金字塔特征點數(shù)目模她、灰度質(zhì)心圓索引
1.參考資料:
[1] ORBSLAM2 source code
2.主要函數(shù):
//特征點提取器的構(gòu)造函數(shù)
ORBextractor::ORBextractor(int _nfeatures, //指定要提取的特征點數(shù)目
float _scaleFactor, //指定圖像金字塔的縮放系數(shù)
int _nlevels, //指定圖像金字塔的層數(shù)
int _iniThFAST, //指定初始的FAST特征點提取參數(shù)议双,可以提取出最明顯的角點
int _minThFAST): //如果因為圖像紋理不豐富提取出的特征點不多絮姆,為了達(dá)到想要的特征點數(shù)目羊壹,
//就使用這個參數(shù)提取出不是那么明顯的角點
nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),
iniThFAST(_iniThFAST), minThFAST(_minThFAST)//設(shè)置這些參數(shù)
3.遇到的問題:
1)圖像金字塔特征點數(shù)目
等比數(shù)列的思路計算總面積侥祭,然后計算單位面積的分配的特征點數(shù)量叁执,得到金字塔每一層需要提取的特征點數(shù)量。注意其中scale^2用scale替換矮冬。
2)灰度質(zhì)心圓索引
3)FAST特征點判定
4)BRIEF描述子
一個特征點的谈宛,描述子是如何用pattern得到的或者描述子是如何得到的?
BRIEF算法的核心思想是在關(guān)鍵點P的周圍以一定模式選取N個點對,把這N個點對的比較結(jié)果組合起來作為描述子胎署。
5)orientated FAST(ORB orientated rotated brief)
orientated FAST是改變描述子還是增加特征點的方向呢吆录?
不管叫什么名字,這個算法要解決的問題是:
回顧一下BRIEF描述子的計算過程:在當(dāng)前關(guān)鍵點P周圍以一定模式選取N個點對琼牧,組合這N個點對的T操作的結(jié)果就為最終的描述子恢筝。當(dāng)我們選取點對的時候,是以當(dāng)前關(guān)鍵點為原點障陶,以水平方向為X軸滋恬,以垂直方向為Y軸建立坐標(biāo)系。當(dāng)圖片發(fā)生旋轉(zhuǎn)時抱究,坐標(biāo)系不變恢氯,同樣的取點模式取出來的點卻不一樣,計算得到的描述子也不一樣,這是不符合我們要求的勋拟。因此我們需要重新建立坐標(biāo)系勋磕,使新的坐標(biāo)系可以跟隨圖片的旋轉(zhuǎn)而旋轉(zhuǎn)。這樣我們以相同的取點模式取出來的點將具有一致性敢靡。ORB特征原理(淺顯易懂)
[?]6)ORB特征點方向計算實現(xiàn)旋轉(zhuǎn)不變性
這個問題問的起始是怎么具體解決旋轉(zhuǎn)不變性的問題
為什么是圓呢挂滓?這個圖應(yīng)該怎么看呢?
一個是旋轉(zhuǎn)前,一個是旋轉(zhuǎn)后啸胧,我們后續(xù)是在旋轉(zhuǎn)后的基礎(chǔ)上做的赶站。
當(dāng)我們選取點對的時候,是以當(dāng)前關(guān)鍵點為原點纺念,以水平方向為X軸贝椿,以垂直方向為Y軸建立坐標(biāo)系。當(dāng)圖片發(fā)生旋轉(zhuǎn)時陷谱,坐標(biāo)系就要旋轉(zhuǎn)一下烙博。
在圖1中,P為關(guān)鍵點烟逊。圓內(nèi)為取點區(qū)域渣窜,每個小格子代表一個像素。現(xiàn)在我們把這塊圓心區(qū)域看做一塊木板宪躯,木板上每個點的質(zhì)量等于其對應(yīng)的像素值乔宿。根據(jù)積分學(xué)的知識我們可以求出這個密度不均勻木板的質(zhì)心Q。
我們知道圓心是固定的而且隨著物體的旋轉(zhuǎn)而旋轉(zhuǎn)眷唉。當(dāng)我們以PQ作為坐標(biāo)軸時(圖2)予颤,在不同的旋轉(zhuǎn)角度下囤官,我們以同一取點模式取出來的點是一致的冬阳。這就解決了旋轉(zhuǎn)一致性的問題。
以計算出來的特征點方向為x軸党饮,再建立y軸肝陪。在這個基礎(chǔ)上計算特征點的描述子,這樣就特征點就具備了旋轉(zhuǎn)不變性刑顺。
那計算灰度質(zhì)心用的是圓呢氯窍?為什么方形的不行呢。
我目前認(rèn)為是可以的蹲堂。如圖狼讨。
=========================================================================
題目:圖像金字塔特征點數(shù)目的計算方式。描述子加入計算特征點的方向的目的柒竞。
參考:
思路:
1.思路:
圖像金字塔特征點數(shù)目的計算方式:等比數(shù)列政供。
描述子加入計算特征點的方向的目的:為了使得特征點的描述子具有旋轉(zhuǎn)不變性。
2.圖解(請用紙):
3.公式推導(dǎo)(請用紙):
要點程序:
// 最底層分配的特征點個數(shù)
float nDesiredFeaturesPerScale =
nfeatures * (1 - factor) /
(1 - (float)pow((double)factor, (double)nlevels));
其他:
二、通俗易懂理解特征提取仿函數(shù)布隔、圖像擴(kuò)充金字塔
1.參考資料:
[1] ORBSLAM2 source code
2.主要函數(shù):
void Frame::ExtractORB(int flag, const cv::Mat &im)
void ORBextractor::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,
OutputArray _descriptors)
void ORBextractor::ComputePyramid(cv::Mat image)
3.遇到的問題:
1)仿函數(shù)
void ORBextractor::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,
OutputArray _descriptors)
為什么要重載小括號運算符operator()?
可以用于仿函數(shù)(一個可以實現(xiàn)函數(shù)功能的對象)
仿函數(shù)(functor)又稱為函數(shù)對象(functor object)是一個能行使函數(shù)功能的類离陶。仿函數(shù)的語法幾乎和我們使用的函數(shù)調(diào)用一樣,不過作為仿函數(shù)的類衅檀,都必須重載operator()運算符
1.仿函數(shù)可以擁有自己的數(shù)據(jù)成員和成員變量招刨,這意味著仿函數(shù)擁有狀態(tài)。這在一般的函數(shù)中是不可能的哀军。
2.仿函數(shù)通常比一般函數(shù)有更好的速度沉眶。
2)圖像金字塔
cv::Rect矩形類參數(shù)設(shè)置
typedef struct CvRect
{
int x; /* 方形的左上角的x-坐標(biāo) */
int y; /* 方形的左上角的y-坐標(biāo)*/
int width; /* 寬 */
int height; /* 高 */
}
通俗解釋:最底層圖像,分辨率最高杉适,可以看到最遠(yuǎn)的點沦寂。
copyMakeBorder
把源圖像拷貝到目的圖像的中央,四面填充指定的像素淘衙。圖片如果已經(jīng)拷貝到中間传藏,只填充邊界
PS:BORDER_ISOLATED由于我們是整張圖像而不是ROI,所以可加可不加彤守。
=========================================================================
題目:什么叫仿函數(shù)毯侦,怎么寫,怎么用具垫,有什么好處侈离?
參考:
思路:
1.思路:
仿函數(shù)的語法幾乎和我們使用的函數(shù)調(diào)用一樣,不過作為仿函數(shù)的類筝蚕,都必須重載operator()運算符
1.仿函數(shù)可以擁有自己的數(shù)據(jù)成員和成員變量卦碾,這意味著仿函數(shù)擁有狀態(tài)。這在一般的函數(shù)中是不可能的起宽。
2.仿函數(shù)通常比一般函數(shù)有更好的速度洲胖。
2.圖解(請用紙):
3.公式推導(dǎo)(請用紙):
要點程序:
// 這是一個變量
ORBextractor* mpORBextractorLeft, *mpORBextractorRight;
// 這是一個變量,這是一個看著像函數(shù)的用法
(*mpORBextractorLeft)(im, //待提取特征點的圖像
cv::Mat(), //掩摸圖像, 實際沒有用到
mvKeys, //輸出變量坯沪,用于保存提取后的特征點
mDescriptors); //輸出變量绿映,用于保存特征點的描述子
// 然后為什么能這么干呢?在類里面實現(xiàn)了這個operator()函數(shù)
void operator()(cv::InputArray image, cv::InputArray mask,
std::vector<cv::KeyPoint> &keypoints,
cv::OutputArray descriptors)
其他:
三腐晾、通俗易懂理解特征點四叉樹均勻化分配策略
1.參考資料:
[1] ORBSLAM2 source code
[2] ORB-SLAM2代碼筆記(十):ORBextractor
2.主要函數(shù):
void ORBextractor::ComputeKeyPointsOctTree(
vector<vector<KeyPoint> >& allKeypoints)
void ExtractorNode::DivideNode(ExtractorNode &n1,
ExtractorNode &n2,
ExtractorNode &n3,
ExtractorNode &n4)
非常重要的函數(shù):
vector<cv::KeyPoint> ORBextractor::DistributeOctTree(const vector<cv::KeyPoint>& vToDistributeKeys, const int &minX,
const int &maxX, const int &minY, const int &maxY, const int &N, const int &level)
void ExtractorNode::DivideNode(ExtractorNode &n1,
ExtractorNode &n2,
ExtractorNode &n3,
ExtractorNode &n4)
3.主要過程如下:
1)劃分網(wǎng)格(網(wǎng)格大小是30像素)叉弦,對每個網(wǎng)格進(jìn)行FAST特征提取,沒有提取的到的降低檢測閾值
2)對當(dāng)前層圖像生成一個提取器節(jié)點藻糖,把特征點分配到這個提取器節(jié)點中
3)1分為4一次淹冰,看看節(jié)點數(shù)量是否大于我要提取的節(jié)點個數(shù),如果小于的話巨柒,還需要接著分裂樱拴,其中節(jié)點為空凝颇,刪除節(jié)點,節(jié)點中特征點數(shù)量為1疹鳄,不再分裂拧略。大于的話,結(jié)束分裂瘪弓,從每個節(jié)點中選擇一個響應(yīng)值最好的垫蛆,然后獲取最終的特征點集合。
4.PS:
1)特征點提取的傳統(tǒng)方法(網(wǎng)格篩點法):
void ORBextractor::ComputeKeyPointsOld(
std::vector<std::vector<KeyPoint> > &allKeypoints)
2)主要過程如下:
第一步計算將圖像分成多少個cell腺怯,對每個cell分別進(jìn)行提取特征點袱饭,cell的計算方法是根據(jù)需要提取的特征點數(shù)目,假設(shè)每個cell中需要提取5個特征點呛占,以此進(jìn)行計算需要的cell數(shù)目虑乖。
接著對計算好的cell進(jìn)行特征點提取。首先使用閾值較大的參數(shù)作為FAST特征點檢測閾值晾虑,如果提取到的特征點數(shù)目足夠多疹味,那么直接計算下一個元包即可,否則使用較小的參數(shù)重新提取特征點帜篇。
然后就涉及到特征點數(shù)目的分配問題糙捺。由于圖像中不可避免出現(xiàn)紋理豐富和紋理較淺的區(qū)域,在紋理豐富的區(qū)域笙隙,角點的數(shù)目可能提取很多洪灯,而在紋理不豐富的區(qū)域,角點提取很少竟痰。對于將特征點數(shù)目不足的cell中剩余不足的數(shù)目分配到其他cell中签钩,提高其他cell中期望提取的特征點數(shù)量閾值,直到最后提取足夠數(shù)量的特征點坏快。當(dāng)然如果提取的數(shù)量確實不足預(yù)期铅檩,也就停止了。
5.問題
=========================================================================
題目:特征點均勻化策略一分為4都是一分為4到底嗎假消,比如說1張圖像柠并,已經(jīng)一分為4岭接。在這個基礎(chǔ)上富拗,下一輪是否對這4個中的每一個都進(jìn)行一分為四了?還是說到數(shù)目了就停止了鸣戴。
參考:
思路:
1.思路:
不斷地進(jìn)行一分為4啃沪。=>快要達(dá)到目標(biāo)數(shù)量的時候,那就sort一下=>從數(shù)量多的進(jìn)行開始進(jìn)行一分為4窄锅,同時每次判斷是否滿足目標(biāo)數(shù)量创千。這樣很符合一個人的思維缰雇。
2.圖解(請用紙):
3.公式推導(dǎo)(請用紙):
要點程序:
其他:
四、通俗易懂理解ORB特征點方向計算實現(xiàn)旋轉(zhuǎn)不變性
1.參考資料:
[1] ORBSLAM2 source code
2.主要函數(shù):
static void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints, const vector<int>& umax)
static float IC_Angle(const Mat& image, Point2f pt, const vector<int> & u_max)
這個圖有疑問啊!!!暫時不jiujie
五追驴、通俗易懂理解ORB描述子steer brief計算方法
1.參考資料:
[1] ORBSLAM2 source code
2.主要函數(shù):
static void computeOrbDescriptor(const KeyPoint& kpt,
const Mat& img, const Point* pattern,
uchar* desc)
3.如何將描述子和特征點方向結(jié)合起來
我們期望準(zhǔn)確地描述特征點械哟,所以當(dāng)我們把圖像順時針轉(zhuǎn)了一個角度的話,我們期望獲取某一點旋轉(zhuǎn)后的坐標(biāo)殿雪,以便正常地描述該特征點暇咆。因為旋轉(zhuǎn)后坐標(biāo)對應(yīng)的灰度值和之前的灰度值是一樣的。
描述特征點的坐標(biāo)集合中的坐標(biāo)值是固定的丙曙。
高斯模糊把雜點去掉
4.問題
1)描述子最終的形式是怎樣的爸业?
cv::Mat格式,描述整幅圖像的特征點亏镰,每一行是一個描述子.
對于一個描述子來說扯旷,一共比較32次,每次用16個點索抓,比較8次钧忽,產(chǎn)生1個字節(jié),所以每個描述子用32*8=256bit.
=========================================================================
題目:如何利用特征點方向?qū)γ枋鲎舆M(jìn)行旋轉(zhuǎn)呢逼肯?請寫出公式惰瓜。
參考:
思路:
1.思路:
2.圖解(請用紙):
3.公式推導(dǎo)(請用紙):
要點程序:
// x'= xcos(θ) - ysin(θ), y'= xsin(θ) + ycos(θ)
#define GET_VALUE(idx) \
center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \ // y'* step
cvRound(pattern[idx].x*a - pattern[idx].y*b)] // x'
其他:
=========================================================================
題目:高斯模糊的目的是什么?
參考:
思路:
1.思路:
高斯blur汉矿,去除噪點崎坊。
2.圖解(請用紙):
3.公式推導(dǎo)(請用紙):
要點程序:
// 注意:提取特征點的時候,使用的是清晰的原圖像洲拇;這里計算描述子的時候奈揍,為了避免圖像噪聲的影響,使用了高斯模糊
GaussianBlur(workingMat, //源圖像
workingMat, //輸出圖像
Size(7, 7), //高斯濾波器kernel大小赋续,必須為正的奇數(shù)
2, //高斯濾波在x方向的標(biāo)準(zhǔn)差
2, //高斯濾波在y方向的標(biāo)準(zhǔn)差
BORDER_REFLECT_101); //邊緣拓展點插值類型
其他:
六男翰、通俗易懂理解去畸變、算圖像邊界纽乱、劃分網(wǎng)格
1.參考資料:
[1] ORBSLAM2 source code
2.主要函數(shù):
Frame::Frame(const cv::Mat &imGray, const double &timeStamp, ORBextractor* extractor,ORBVocabulary* voc, cv::Mat &K, cv::Mat &distCoef, const float &bf, const float &thDepth)
:mpORBvocabulary(voc),mpORBextractorLeft(extractor),mpORBextractorRight(static_cast<ORBextractor*>(NULL)),
mTimeStamp(timeStamp), mK(K.clone()), mDistCoef(distCoef.clone()), mbf(bf), mThDepth(thDepth)
void Frame::UndistortKeyPoints()
void Frame::ComputeImageBounds(const cv::Mat &imLeft)
注意一下去畸變函數(shù)需要輸入雙通道的mat:
//為了能夠直接調(diào)用opencv的函數(shù)來去畸變蛾绎,需要先將矩陣調(diào)整為2通道(對應(yīng)坐標(biāo)x,y)
mat=mat.reshape(2);
cv::undistortPoints(mat,mat,mK,mDistCoef,cv::Mat(),mK);
//調(diào)整回只有一個通道,回歸我們正常的處理方式
mat=mat.reshape(1);
分配特征點到網(wǎng)格鸦列,網(wǎng)格在匹配的時候用到
void Frame::AssignFeaturesToGrid()
bool Frame::PosInGrid(const cv::KeyPoint &kp, int &posX, int &posY)