若該文為原創文章,未經允許不得轉載
原博主部落格地址:https://blog.csdn.net/qq21497936
原博主部落格導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章部落格地址:https://blog.csdn.net/qq21497936/article/details/106926496
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼自己研究
紅胖子(紅模仿)的博文大全:開發技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、微控制器、軟硬結合等等)持續更新中…(點選傳送門)
上一篇:《OpenCV開發筆記(六十四):紅胖子8分鐘帶你深入瞭解SURF特徵點(圖文並茂+淺顯易懂+程式原始碼)》
下一篇:持續補充中…
紅胖子,來也!
識別除了傳統的模板匹配之外就是體徵點了,前面介紹了Suft特徵點,還有一個傳統的就會ORB特徵點了。
其實識別的特徵點多種多樣,既可以自己寫也可以使用opencv為我們提供的,一般來說根據特徵點的特性和效率,選擇適合我們場景的特徵就可以了。
本篇,介紹ORB特徵提取。
ORB是ORiented Brief的簡稱,是briedf演算法的改進版,於2011年在《ORB:an fficient alternative to SIFT or SURF》中提出。
ORB演算法分為兩部分,分別是特徵點提取和特徵點描述:
- 特徵提取:由FAST(Features from Accelerated Segment Test)演算法發展來的;
- 特徵點描述:根據BRIEF(Binary Robust IndependentElementary Features)特徵描述演算法改進的。
ORB特徵是將FAST特徵點的檢測方法與BRIEF特徵描述子結合起來,並在它們原來的基礎上做了改進與優化。據說,ORB演算法的速度是sift的100倍,是surf的10倍。
該特徵描述子是在特徵點附近隨機選取若干點對,將這些點對的灰度值的大小,組合成一個二進位制串,組合成一個二進位制傳,並將這個二進位制串作為該特徵點的特徵描述子。
Brief的速度快,但是使用灰度值作為描述字計算的源頭,毫無疑問會有一些顯而易見的問題:
- 旋轉後灰度變了導致無法識別,因其不具備旋轉不變形;
- 由於是計算灰度,噪聲灰度化則無法去噪,所以對噪聲敏感;
- 尺度不同影響灰度計算,所以也不具備尺度不變形;
ORB是試圖使其具備旋轉不變性和降低噪聲敏感度而提出的。
該步能夠提取大量的特徵點,但是有很大一部分的特徵點的質量不高。從影像中選取一點P,以P為圓心畫一個半徑為N畫素半徑的圓。圓周上如果有連續n個畫素點的灰度值比P點的灰度值大或者小,則認為P為特徵點。
通俗來說就是使用ID3演算法訓練一個決策樹,將特徵點圓周上的16個畫素輸入決策樹中,以此來篩選出最優的FAST特徵點。
使用非極大值抑制演算法去除臨近位置多個特徵點的問題。為每一個特徵點計算出其響應大小。計算方式是特徵點P和其周圍16個特徵點偏差的絕對值和。在比較臨近的特徵點中,保留響應值較大的特徵點,刪除其餘的特徵點。
ORB演算法提出使用矩(moment)法來確定FAST特徵點的方向。也就是說通過矩來計算特徵點以r為半徑範圍內的質心,特徵點座標到質心形成一個向量作為該特徵點的方向。
cv::Ptr<cv::ORB> _pOrb = cv::ORB::create();
std::vector<cv::KeyPoint> keyPoints1;
//特徵點檢測
_pOrb->detect(srcMat, keyPoints1);
static Ptr<ORB> create(int nfeatures=500,
float scaleFactor=1.2f,
int nlevels=8,
int edgeThreshold=31,
int firstLevel=0,
int WTA_K=2,
int scoreType=ORB::HARRIS_SCORE,
int patchSize=31,
int fastThreshold=20);
- 引數一:int型別的nfeatures,用於ORB的,保留最大的關鍵點數,預設值500;
- 引數二:float型別的scaleFactor,比例因子,大於1時為金字塔抽取比。的等於2表示經典的金字塔,每一個下一層的畫素比上一層少4倍,但是比例係數太大了將顯著降低特徵匹配分數。另一方面,太接近1個比例因子這意味著要覆蓋一定的範圍,你需要更多的金字塔級別,所以速度會受影響的,預設值1.2f;
- 引數三:int型別的nlevels,nlevels金字塔級別的數目。最小級別的線性大小等於輸入影像線性大小/功率(縮放因子,nlevels-第一級),預設值為8;
- 引數四:int型別的edgeThreshold,edgeThreshold這是未檢測到功能的邊框大小。它應該大致匹配patchSize引數。;
- 引數五:int型別的firstLevel,要將源影像放置到的金字塔級別。以前的圖層已填充使用放大的源影像;
- 引數六:int型別的WTA_K,生成定向簡短描述符的每個元素的點數。這個預設值2是指取一個隨機點對並比較它們的亮度,所以我們得到0/1的響應。其他可能的值是3和4。例如,3表示我們取3隨機點(當然,這些點座標是隨機的,但是它們是由預定義的種子,因此簡短描述符的每個元素都是從畫素確定地計算出來的矩形),找到最大亮度點和獲勝者的輸出索引(0、1或2)。如此輸出將佔用2位,因此需要一個特殊的漢明距離變數,表示為NORM_HAMMING2(每箱2位)。當WTA_K=4時,我們取4個隨機點計算每個點bin(也將佔用可能值為0、1、2或3的2位)。;
- 引數七:int型別的scoreType,HARRIS_SCORES表示使用HARRIS演算法對特徵進行排序(分數寫入KeyPoint::score,用於保留最佳nfeatures功能);FAST_SCORE是產生稍微不穩定關鍵點的引數的替代值,但計算起來要快一點;
- 引數八:int型別的patchSize,定向簡短描述符使用的修補程式的大小。當然,在較小的金字塔層特徵覆蓋的感知影像區域將更大;
- 引數九:int型別的fastThreshold,快速閾值;
void xfeatures2d::SURT::detect( InputArray image,
std::vector<KeyPoint>& keypoints,
InputArray mask=noArray() );
- 引數一:InputArray型別的image,輸入cv::Mat;
- 引數二:std::Vector型別的keypoints,檢測到的關鍵點;
- 引數三:InputArray型別的mask,預設為空,指定在何處查詢關鍵點的掩碼(可選)。它必須是8位整數感興趣區域中具有非零值的矩陣。;
void xfeatures2d::SURT::compute( InputArray image,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors );
- 引數一:InputArray型別的image,輸入cv::Mat;
- 引數二:std::Vector型別的keypoints,描述符不能為其已刪除計算的。有時可以新增新的關鍵點,例如:SIFT duplicates keypoint有幾個主要的方向(每個方向);
- 引數三:OutputArray型別的descriptors,計算描述符;
// 該函式結合了detect和compute,參照detect和compute函式引數
void xfeatures2d::SURT::detectAndCompute( InputArray image,
InputArray mask,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints=false );
void drawKeypoints( InputArray image,
const std::vector<KeyPoint>& keypoints,
InputOutputArray outImage,
const Scalar& color=Scalar::all(-1),
int flags=DrawMatchesFlags::DEFAULT );
- 引數一:InputArray型別的image,;
- 引數二:std::Vector型別的keypoints,原圖的關鍵點;
- 引數三:InputOutputArray型別的outImage,其內容取決於定義在輸出影像。請參閱引數五的標誌flag);
- 引數四:cv::Scalar型別的color,繪製關鍵點的顏色,預設為Scalar::all(-1)隨機顏色,每個點都是這個顏色,那麼隨機時,每個點都是隨機的;
- 引數五:int型別的flags,預設為DEFAULT,具體參照DrawMatchesFlags列舉如下:
本原始碼中包含了“透視變換”,請參照博文《OpenCV開發筆記(五十一):紅胖子8分鐘帶你深入瞭解透視變換(圖文並茂+淺顯易懂+程式原始碼)》
根據前面連續三篇的特徵點,我們其實可以猜到了所有的匹配都是這樣提取特徵點,然後使用一些演算法來匹配,至於使用什麼特徵點提取就是需要開發者根據實際的經驗去選取,單一的特徵點/多種特徵點提取混合/自己寫特徵點等等多種方式去提取特徵點,為後一步的特徵點匹配做準備,特徵點通用的就到此篇,後續會根據實際開發專案中使用的到隨時以新的篇章博文去補充。
《OpenCV開發筆記(六十三):紅胖子8分鐘帶你深入瞭解SIFT特徵點(圖文並茂+淺顯易懂+程式原始碼)》
《OpenCV開發筆記(六十四):紅胖子8分鐘帶你深入瞭解SURF特徵點(圖文並茂+淺顯易懂+程式原始碼》
《OpenCV開發筆記(六十五):紅胖子8分鐘帶你深入瞭解ORB特徵點(圖文並茂+淺顯易懂+程式原始碼)》
void OpenCVManager::testOrbFeatureDetector()
{
QString fileName1 = "13.jpg";
int width = 400;
int height = 300;
cv::Mat srcMat = cv::imread(fileName1.toStdString());
cv::resize(srcMat, srcMat, cv::Size(width, height));
cv::String windowName = _windowTitle.toStdString();
cvui::init(windowName);
cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2, srcMat.rows * 3),
srcMat.type());
cv::Ptr<cv::ORB> _pObr = cv::ORB::create();
int k1x = 0;
int k1y = 0;
int k2x = 100;
int k2y = 0;
int k3x = 100;
int k3y = 100;
int k4x = 0;
int k4y = 100;
while(true)
{
windowMat = cv::Scalar(0, 0, 0);
cv::Mat mat;
// 原圖先copy到左邊
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);
{
std::vector<cv::KeyPoint> keyPoints1;
std::vector<cv::KeyPoint> keyPoints2;
cvui::printf(windowMat, 0 + width * 1, 10 + height * 0, "k1x");
cvui::trackbar(windowMat, 0 + width * 1, 20 + height * 0, 165, &k1x, 0, 100);
cvui::printf(windowMat, 0 + width * 1, 70 + height * 0, "k1y");
cvui::trackbar(windowMat, 0 + width * 1, 80 + height * 0, 165, &k1y, 0, 100);
cvui::printf(windowMat, width / 2 + width * 1, 10 + height * 0, "k2x");
cvui::trackbar(windowMat, width / 2 + width * 1, 20 + height * 0, 165, &k2x, 0, 100);
cvui::printf(windowMat, width / 2 + width * 1, 70 + height * 0, "k2y");
cvui::trackbar(windowMat, width / 2 + width * 1, 80 + height * 0, 165, &k2y, 0, 100);
cvui::printf(windowMat, 0 + width * 1, 10 + height * 0 + height / 2, "k3x");
cvui::trackbar(windowMat, 0 + width * 1, 20 + height * 0 + height / 2, 165, &k3x, 0, 100);
cvui::printf(windowMat, 0 + width * 1, 70 + height * 0 + height / 2, "k3y");
cvui::trackbar(windowMat, 0 + width * 1, 80 + height * 0 + height / 2, 165, &k3y, 0, 100);
cvui::printf(windowMat, width / 2 + width * 1, 10 + height * 0 + height / 2, "k4x");
cvui::trackbar(windowMat, width / 2 + width * 1, 20 + height * 0 + height / 2, 165, &k4x, 0, 100);
cvui::printf(windowMat, width / 2 + width * 1, 70 + height * 0 + height / 2, "k4y");
cvui::trackbar(windowMat, width / 2 + width * 1, 80 + height * 0 + height / 2, 165, &k4y, 0, 100);
std::vector<cv::Point2f> srcPoints;
std::vector<cv::Point2f> dstPoints;
srcPoints.push_back(cv::Point2f(0.0f, 0.0f));
srcPoints.push_back(cv::Point2f(srcMat.cols - 1, 0.0f));
srcPoints.push_back(cv::Point2f(srcMat.cols - 1, srcMat.rows - 1));
srcPoints.push_back(cv::Point2f(0.0f, srcMat.rows - 1));
dstPoints.push_back(cv::Point2f(srcMat.cols * k1x / 100.0f, srcMat.rows * k1y / 100.0f));
dstPoints.push_back(cv::Point2f(srcMat.cols * k2x / 100.0f, srcMat.rows * k2y / 100.0f));
dstPoints.push_back(cv::Point2f(srcMat.cols * k3x / 100.0f, srcMat.rows * k3y / 100.0f));
dstPoints.push_back(cv::Point2f(srcMat.cols * k4x / 100.0f, srcMat.rows * k4y / 100.0f));
cv::Mat M = cv::getPerspectiveTransform(srcPoints, dstPoints);
cv::Mat srcMat2;
cv::warpPerspective(srcMat,
srcMat2,
M,
cv::Size(srcMat.cols, srcMat.rows),
cv::INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar::all(0));
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, srcMat2, 1.0f, 0.0f, mat);
//特徵點檢測
_pObr->detect(srcMat, keyPoints1);
//繪製特徵點(關鍵點)
cv::Mat resultShowMat;
cv::drawKeypoints(srcMat,
keyPoints1,
resultShowMat,
cv::Scalar(0, 0, 255),
cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, resultShowMat, 1.0f, 0.0f, mat);
//特徵點檢測
_pObr->detect(srcMat2, keyPoints2);
//繪製特徵點(關鍵點)
cv::Mat resultShowMat2;
cv::drawKeypoints(srcMat2,
keyPoints2,
resultShowMat2,
cv::Scalar(0, 0, 255),
cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, resultShowMat2, 1.0f, 0.0f, mat);
cv::imshow(windowName, windowMat);
}
// 更新
cvui::update();
// 顯示
// esc鍵退出
if(cv::waitKey(25) == 27)
{
break;
}
}
}
對應版本號v1.59.0
上一篇:《OpenCV開發筆記(六十四):紅胖子8分鐘帶你深入瞭解SURF特徵點(圖文並茂+淺顯易懂+程式原始碼)》
下一篇:持續補充中…