本博客內(nèi)容來源于網(wǎng)絡(luò)以及其他書籍,結(jié)合自己學(xué)習(xí)的心得進行重編輯,因為看了很多文章不便一一標注引用,如圖片文字等侵權(quán),請告知刪除。
傳統(tǒng)2D計算機視覺學(xué)習(xí)筆記目錄------->傳送門
傳統(tǒng)3D計算機視覺學(xué)習(xí)筆記目錄------->傳送門
前言
前幾天看的東西的數(shù)學(xué)計算較多,這篇文章講一下RANSAC,細心地話能在我們前幾篇將特征點時,里面有特征點的匹配效果,可以看出來經(jīng)過RANSAC之后匹配的準確率有很大的提升。RANSAC主要還是一種匹配算法的思想,很簡單,而且也很有效,在很多方面都有使用,下面我們看一下。
RANSAC簡介
RANSAC(RANdom SAmple Consensus)隨機抽樣一致算法,是一種在包含離群點在內(nèi)的數(shù)據(jù)集里,通過迭代的方式估計模型的參數(shù)。舉個例子,我們計算單應(yīng)性矩陣時,初始匹配有很多的誤匹配即是一個有離群點的數(shù)據(jù)集,然后我們估計出單應(yīng)性矩陣。
RANSAC是一種算法的思路,在計算機視覺中應(yīng)用較多。它是一種不確定的算法,即有一定的概率得出一個合理的結(jié)果,當(dāng)然也會出現(xiàn)錯誤的結(jié)果。如果要提高概率,一個要提高迭代的次數(shù),在一個就是減少數(shù)據(jù)集離群點的比例。
RANSAC 在視覺中有很多的應(yīng)用,比如2D特征點匹配,3D點云匹配,在圖片或者點云中識別直線,識別可以參數(shù)化的形狀。RANSAC還可以用來擬合函數(shù)等。
下面我們來看一下RANSAC的具體的思路

RANSAC 思路
RANSAC核心思想就是隨機性和假設(shè)性,隨機性用于減少計算,循環(huán)次數(shù)是利用正確數(shù)據(jù)出現(xiàn)的概率。而假設(shè)性,就是說隨機抽出來的數(shù)據(jù)都認為是正確的,并以此去計算其他點,獲得其他滿足變換關(guān)系的點,然后利用投票機制,選出獲票最多的那一個變換。
可能沒看懂,我們來看一下具體的流程:
1、在可以有(也可以沒有,主要看應(yīng)用場景)條件限制(比如選的子集里的點不能過遠等)的情況下,隨機選取子集,并假設(shè)為局內(nèi)點。子集的大小,主要取決于要擬合模型的復(fù)雜度。
2、用局內(nèi)點擬合一個模型,此模型適應(yīng)于假設(shè)的局內(nèi)點,所有的未知參數(shù)都能從假設(shè)的局內(nèi)點計算得出。
3、 用2中得到的模型去測試整個數(shù)據(jù)中其他數(shù)據(jù),如果某個點適用于估計的模型,認為它也是局內(nèi)點,將局內(nèi)點擴充。
4、如果有足夠多的點被歸類為假設(shè)的局內(nèi)點,那么估計的模型就足夠合理。
5、用所有擴充后的局內(nèi)點去重新估計模型。
6、通過估計局內(nèi)點與模型的錯誤率來評估模型。
7、如果當(dāng)前模型效果比最好模型更好而被選用為最好模型,否則拋棄當(dāng)前模型。至此完成一個迭代,然后從第1步開始一個新的迭代。
其實我們看完這個流程,其實RANSCA的思路通俗的說,就是在一定的條件下(不是沒有限制的隨機),去試,試的多了找到正確或者好的可能行就大了。整個思路很樸素,其實通過概率分析,其實他的得到好的結(jié)果的計算量,沒有你想想的那么大,當(dāng)然其還是有一定的概率會計算錯。

OpenCV RANSAC效果展示[代碼]
偷懶直接用了ORB的代碼和效果
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/features2d/features2d.hpp>
void extracte_orb(cv::Mat input,std::vector<cv::KeyPoint> &keypoint,cv::Mat &descriptor){
cv::Ptr<cv::ORB> f2d = cv::ORB::create(500);
f2d->detect(input,keypoint);
cv::Mat image_with_kp;
f2d->compute(input,keypoint,descriptor);
cv::drawKeypoints(input, keypoint, image_with_kp, cv::Scalar::all(-1),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
cv::imwrite("orb"+std::to_string(random())+".png",image_with_kp);
}
void match_two_image(cv::Mat image1,cv::Mat image2, std::vector<cv::KeyPoint> keypoint1,std::vector<cv::KeyPoint> keypoint2,cv::Mat descriptor1,cv::Mat descriptor2){
cv::BFMatcher matcher(cv::NORM_HAMMING);
std::vector<cv::DMatch> matches;
matcher.match(descriptor1,descriptor2, matches);
cv::Mat good_matches_image;
cv::drawMatches(image1, keypoint1, image2, keypoint2,
matches, good_matches_image, cv::Scalar::all(-1), cv::Scalar::all(-1),
std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imwrite("good_matches_image.png",good_matches_image);
{
std::vector <cv::KeyPoint> RAN_KP1, RAN_KP2;
std::vector<cv::Point2f> keypoints1, keypoints2;
for (int i = 0; i < matches.size(); i++) {
keypoints1.push_back(keypoint1[matches[i].queryIdx].pt);
keypoints2.push_back(keypoint2[matches[i].trainIdx].pt);
RAN_KP1.push_back(keypoint1[matches[i].queryIdx]);
RAN_KP2.push_back(keypoint2[matches[i].trainIdx]);
}
std::vector<uchar> RansacStatus;
cv::findFundamentalMat(keypoints1, keypoints2, RansacStatus, cv::FM_RANSAC);
std::vector <cv::KeyPoint> ransac_keypoints1, ransac_keypoints2;
std::vector <cv::DMatch> ransac_matches;
int index = 0;
for (size_t i = 0; i < matches.size(); i++)
{
if (RansacStatus[i] != 0)
{
ransac_keypoints1.push_back(RAN_KP1[i]);
ransac_keypoints2.push_back(RAN_KP2[i]);
matches[i].queryIdx = index;
matches[i].trainIdx = index;
ransac_matches.push_back(matches[i]);
index++;
}
}
cv::Mat after_ransac_sift_match;
cv::drawMatches(image1, ransac_keypoints1, image2, ransac_keypoints2,
ransac_matches, after_ransac_sift_match, cv::Scalar::all(-1), cv::Scalar::all(-1),
std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imwrite("after_ransac_orb_match.png",after_ransac_sift_match);
}
}
int main(int argc, char *argv[])
{
cv::Mat image1 = cv::imread(argv[1]);
cv::Mat image2 = cv::imread(argv[2]);
std::vector<cv::KeyPoint> keypoint1,keypoint2;
cv::Mat descriptor1, descriptor2;
extracte_orb(image1,keypoint1,descriptor1);
extracte_orb(image2,keypoint2,descriptor2);
match_two_image(image1,image2,keypoint1,keypoint2,descriptor1,descriptor2);
return 0;
}
| 初步匹配效果 | ransac后匹配效果 |
|---|---|
![]() |
![]() |
總結(jié)
從上面的結(jié)果來看,經(jīng)過ransac后的效果還會很好的,剔除了很多錯誤的匹配結(jié)果。
RANSAC的優(yōu)點是它能魯棒的估計模型參數(shù)。例如,它能從包含大量局外點的數(shù)據(jù)集中估計出較高精度的參數(shù),較少了離群點對模型結(jié)果的影響。
RANSAC的缺點是它計算參數(shù)的迭代次數(shù)沒有上限;如果設(shè)置迭代次數(shù)的上限,得到的結(jié)果可能不是最優(yōu)的結(jié)果,甚至可能得到錯誤的結(jié)果。RANSAC只有一定的概率得到可信的模型,概率與迭代次數(shù)成正比。RANSAC的另一個缺點是它要求設(shè)置跟問題相關(guān)的閥值。RANSAC只能從特定的數(shù)據(jù)集中估計出一個模型,如果存在兩個(或多個)模型,RANSAC不能找到別的模型。
雖然缺點不少,但是我還是不能失去他。
重要的事情說三遍:
如果我的文章對您有所幫助,那就點贊加個關(guān)注唄 ( * ^ __ ^ * )
如果我的文章對您有所幫助,那就點贊加個關(guān)注唄 ( * ^ __ ^ * )
如果我的文章對您有所幫助,那就點贊加個關(guān)注唄 ( * ^ __ ^ * )
傳統(tǒng)2D計算機視覺學(xué)習(xí)筆記目錄------->傳送門
傳統(tǒng)3D計算機視覺學(xué)習(xí)筆記目錄------->傳送門
任何人或團體、機構(gòu)全部轉(zhuǎn)載或者部分轉(zhuǎn)載、摘錄,請保留本博客鏈接或標注來源。博客地址:開飛機的喬巴
作者簡介:開飛機的喬巴(WeChat:zhangzheng-thu),現(xiàn)主要從事機器人抓取視覺系統(tǒng)以及三維重建等3D視覺相關(guān)方面,另外對slam以及深度學(xué)習(xí)技術(shù)也頗感興趣,歡迎加我微信或留言交流相關(guān)工作。

