RANSAC算法學(xué)習(xí)筆記

本博客內(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)工作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容