1. 程式人生 > >(轉載)利用SIFT和RANSAC算法(openCV框架)實現物體的檢測與定位,並求出變換矩陣(findFundamentalMat和findHomography的比較) 置頂

(轉載)利用SIFT和RANSAC算法(openCV框架)實現物體的檢測與定位,並求出變換矩陣(findFundamentalMat和findHomography的比較) 置頂

bsp 解釋 邊界 返回值 class 不同的 rip 很多 per

原文鏈接:https://blog.csdn.net/qq_25352981/article/details/46914837#commentsedit

本文目標是通過使用SIFT和RANSAC算法,完成特征點的正確匹配,並求出變換矩陣,通過變換矩陣計算出要識別物體的邊界(文章中有部分源碼,整個工程我也上傳了,請點擊這裏)。

SIFT算法是目前公認的效果最好的特征點檢測算法,關於該算法的就不多說了,網上的資料有很多,在此提供兩個鏈接,一個是SIFT原文的譯文,一個是關於SIFT算法的詳細解釋:

SIFT算法譯文

SIFT算法詳解

整個實現過程可以復述如下:提供兩張初始圖片,一幅為模板圖像,一幅為測試圖片,目的就是根據模板圖片中的物體,檢測出測試圖片中的物體,並表示出物體的具體位置和大小,測試圖片中的物體位置和大小,已經事先用白色方框標記。

首先,對兩幅圖片,都使用SIFT算法提取特征點,提取結果如下:(SIFT特征提取方法就用的是上文鏈接“SIFT算法詳解”中提供的代碼)

技術分享圖片技術分享圖片

然後對特征點進行匹配,按照SIFT算法原文作者的思路,每個特征點產生一個128維的向量,計算向量之間的歐式距離,采用最近比次近的方式完成匹配,如果最近距離比上次近距離小於0.8,則認為這是一個正確的匹配, 否則認為匹配不成功。結果這種匹配後的情況如下圖:

技術分享圖片

可以發現,仍然存在著很多錯誤的匹配點,所以再嘗試用RANSAC算法消除錯誤匹配,嘗試使用openCV中的findFundamentalMat函數消除錯誤匹配:

技術分享圖片

通過使用findFundamentalMat函數,函數返回一個3*3的矩陣,一開始我認為這個矩陣就是變換矩陣,只要將左圖中的點與這個變換矩陣相乘,就可以得到右圖中的對應點。但是這其實是不對的。

在這裏有一個誤解,就是findFundamentalMat函數確實可以使用RANSAC方法消除錯誤匹配,從名字上可以發現,這個函數的作用是返回基礎矩陣的,基礎矩陣和變換矩陣是兩個不同的概念。基礎矩陣描述是三維場景中的像點之間的對應關系(其實到現在為止這個函數求出的基礎矩陣有個毛用我也不知道)。所以說,如果使用這個函數,這個實驗也就能做到這一步了,沒法再往下做了。

所以,為了得到變換矩陣,後來我才發現openCV中還有函數findHomography,這個函數才是真正的計算變換矩陣的函數,它的函數返回值才是真正的變換矩陣。

其實這個問題困擾了我很久,關於消除錯誤匹配的方法,網上查出來的多數都是通過findFundamentalMat函數來進行,所以我就想當然的認為該函數的返回值是變換矩陣了。而網上關於findHomography的介紹比較少,所以才會讓人們誤解findFundamentalMat會計算出變換矩陣了。

嘗試用findHomography函數返回的矩陣,在模板圖像中,已經用綠色方框標示出物體輪廓,根據物體的四個邊界點,與變換矩陣相乘,即可得到變換後的物體的輪廓的四個邊界點,將此邊界點連接即為物體輪廓,如下圖所示(綠色方框為事先標註的模板物體中的輪廓,白色方框為事先標註的測試圖片中的輪廓,紅色方框為經過綠色方框經變換矩陣變換後計算出的輪廓):

技術分享圖片

從結果可以看出,這才是比較正確的結果。

實驗過程中的主要代碼如下(這是主要的代碼,SIFT算法和一些其他的功能函數我都寫在了其他的文件中):

#include<math.h>  
#include<time.h>  
  
#include <windows.h>  
#include <iostream>  
using namespace std;  
#include <cv.h>  
#include <highgui.h>  
#include <cxcore.h>  
using namespace cv;  
#include "sift.h"  
#include "my_function.h"  
  
int main()  
{  
    //加載兩幅圖片  
    Mat src1 = imread("F:\\ylab\\image database\\camera\\obj01_001.jpg");  
    Mat src2 = imread("F:\\ylab\\image database\\imagesTest2\\test01_.jpg");  
  
    //這四個坐標是模板圖像中綠色方框的四個頂點  
    Point2f m1(173.0,0.0),m2(168.0,464.0),m3(507.0,464.0),m4(499.0,0.0);  
    std::vector<Point2f> obj_corners(4);  
    obj_corners[0] = cvPoint(173.0,0.0);   
    obj_corners[1] = cvPoint(168.0,464.0);  
    obj_corners[2] = cvPoint(507.0,464.0);   
    obj_corners[3] = cvPoint(499.0,0.0);  
  
    //原始圖片比較大,我這裏將圖片同一處理成了640*480的大小  
    Size certainsize=Size(640,480);  
    Mat src_1;  
    Mat src_2;  
    resize(src1,src_1,certainsize);  
    resize(src2,src_2,certainsize);  
  
    //兩個圖像的特征點序列  
    Vector<Keypoint> feature_1,feature_2;  
  
    //采用sift算法,計算特征點序列,這個SIFT函數是在另外的文件中寫好的  
    Sift(src_1, feature_1, 1.6);  
    Sift(src_2, feature_2, 1.6);  
  
    //feature_dis為帶有距離的特征點結構體序列  
    Vector<Key_point> feature_dis_1;  
    Vector<Key_point> feature_dis_2;  
    Vector<Key_point> result;  
      
    //對特征點進行匹配,這個Match_feature是我自己寫的,就是采用最近比次近小於0.8即為合適的匹配,這種匹配方式  
    //openCV中並沒有,所以我就自己寫了  
    Match_feature(feature_1,feature_2,feature_dis_1,feature_dis_2);   
      
    printf("The number of features is %d\n",feature_1.size());  
    printf("The number of the match features is %d\n",feature_dis_1.size());  
  
    //從這裏開始使用RANSAC方法進行運算  
    //下面的程序都好無奈,所有的結構都只能轉化成openCV的類型才能用openCV的函數。。  
    Ptr<DescriptorMatcher> descriptor_matcher = DescriptorMatcher::create( "BruteForce" );//創建特征匹配器    
    int count=feature_dis_1.size();  
  
    //把特征點序列轉化成openCV能夠使用的類型  
    vector<KeyPoint>keypoints1,keypoints2;  
    KeyPoint keyp;  
    for(int i=0;i<count;i++)  
    {  
        keyp.pt.x=feature_dis_1[i].dx;  
        keyp.pt.y=feature_dis_1[i].dy;  
        keypoints1.push_back(keyp);  
        keyp.pt.x=feature_dis_2[i].dx;  
        keyp.pt.y=feature_dis_2[i].dy;  
        keypoints2.push_back(keyp);  
    }  
  
    Mat descriptors1(count,FEATURE_ELEMENT_LENGTH, CV_32F);  
    Mat descriptors2(count,FEATURE_ELEMENT_LENGTH, CV_32F);  
      
     for (int i=0; i<count; i++)  
    {  
        for(int j=0;j<FEATURE_ELEMENT_LENGTH;j++)  
        {  
            descriptors1.at<float>(i,j)=feature_dis_1[i].descriptor[j];  
            descriptors2.at<float>(i,j)=feature_dis_2[i].descriptor[j];  
        }  
            
    }  
  
    Mat img_match;  
    vector<DMatch> matches;   
    descriptor_matcher->match( descriptors1, descriptors2, matches );   
    Mat img_matches;  
    drawMatches(src_1,keypoints1,src_2,keypoints2,matches,img_matches);  
    //其實我前面已經完成匹配了,到這裏,用openCV自帶的方式重新匹配了一遍,並且顯示了一下  
    imshow("SIFT",img_matches);  
    //imwrite("F:\\ylab\\CSDN_image\\3.jpg",img_matches);  
      
    Mat p1(feature_dis_1.size(),2,CV_32F);  
    Mat p2(feature_dis_1.size(),2,CV_32F);  
    for(int i=0;i<feature_dis_1.size();i++)  
    {  
        p1.at<float>(i,0)=feature_dis_1[i].dx;  
        p1.at<float>(i,1)=feature_dis_1[i].dy;  
        p2.at<float>(i,0)=feature_dis_2[i].dx;  
        p2.at<float>(i,1)=feature_dis_2[i].dy;  
    }  
    // 用RANSAC方法計算F  
    Mat m_Fundamental;  
    // 上面這個變量是基本矩陣  
    vector<uchar> m_RANSACStatus;  
    // 上面這個變量已經定義過,用於存儲RANSAC後每個點的狀態  
  
    //一開始使用findFundamentalMat函數,發現可以消除錯誤匹配,實現很好的效果,但是  
    //就是函數返回值不是變換矩陣,而是沒有什麽用的基礎矩陣  
    m_Fundamental = findFundamentalMat(p1,p2,m_RANSACStatus,CV_FM_RANSAC);  
      
    //這裏使用findHomography函數,這個函數的返回值才是真正的變換矩陣  
    Mat m_homography;  
    vector<uchar> m;  
    m_homography=findHomography(p1,p2,CV_RANSAC,3,m);                                
  
    //由變換矩陣,求得變換後的物體邊界四個點  
    std::vector<Point2f> scene_corners(4);  
    perspectiveTransform( obj_corners, scene_corners, m_homography);  
    line( src_2, scene_corners[0] , scene_corners[1] , Scalar(0, 0, 255), 2 );  
    line( src_2, scene_corners[1] , scene_corners[2] , Scalar(0, 0, 255), 2 );  
    line( src_2, scene_corners[2] , scene_corners[3] , Scalar(0, 0, 255), 2 );  
    line( src_2, scene_corners[3] , scene_corners[0] , Scalar(0, 0, 255), 2 );  
     
      
  
    int nr=m_Fundamental.rows; // number of rows    
    int nc=m_Fundamental.cols *m_Fundamental.channels(); // total number of elements per line   
  
    // 計算野點個數  
    int OutlinerCount = 0;  
    for (int i=0; i<Count; i++)  
    {  
         if (m_RANSACStatus[i] == 0) // 狀態為0表示野點  
         {  
              OutlinerCount++;  
         }  
    }  
   
    // 計算內點  
     vector<Point2f> m_LeftInlier;  
     vector<Point2f> m_RightInlier;  
     vector<DMatch> m_InlierMatches;  
    // 上面三個變量用於保存內點和匹配關系  
    int ptCount = (int)matches.size();  
    int InlinerCount = ptCount - OutlinerCount;  
    m_InlierMatches.resize(InlinerCount);  
    m_LeftInlier.resize(InlinerCount);  
    m_RightInlier.resize(InlinerCount);  
    InlinerCount = 0;  
    for (int i=0; i<ptCount; i++)  
    {  
         if (m_RANSACStatus[i] != 0)  
         {  
               m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);  
               m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);  
               m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);  
              m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);  
              m_InlierMatches[InlinerCount].queryIdx = InlinerCount;  
              m_InlierMatches[InlinerCount].trainIdx = InlinerCount;  
              InlinerCount++;  
           }  
    }  
 //   //printf("最終的匹配點個數為:%d\n",InlinerCount);  
    //// 把內點轉換為drawMatches可以使用的格式  
    vector<KeyPoint> key1(InlinerCount);  
    vector<KeyPoint> key2(InlinerCount);  
    KeyPoint::convert(m_LeftInlier, key1);  
    KeyPoint::convert(m_RightInlier, key2);  
   
    // 顯示計算F過後的內點匹配  
    //Mat m_matLeftImage;  
    //Mat m_matRightImage;  
    // 以上兩個變量保存的是左右兩幅圖像  
      
    line(src_1,m1,m2,Scalar(0,255,0),2);  
    line(src_1,m2,m3,Scalar(0,255,0),2);  
    line(src_1,m3,m4,Scalar(0,255,0),2);  
    line(src_1,m4,m1,Scalar(0,255,0),2);  
  
    Mat OutImage;  
    drawMatches(src_1, key1, src_2, key2, m_InlierMatches, OutImage);     
    imshow("SIFT_RANSAC",OutImage);   
    //imwrite("F:\\ylab\\CSDN_image\\5.jpg",OutImage);  
    cvWaitKey( 0 );  
    return 0;  
}  

  

(轉載)利用SIFT和RANSAC算法(openCV框架)實現物體的檢測與定位,並求出變換矩陣(findFundamentalMat和findHomography的比較) 置頂