重映射與SURF特徵點檢測與匹配

一、重映射
1.1、簡介
  重映射在圖像處理中主要的功能爲:將一個圖像中一個位置的像素放置到另一個圖像指定位置的過程,可以根據自己設定的函數將圖像進行變換,較常見的功能有關於x軸翻轉,關於y軸翻轉,關於x、y軸翻轉。爲了完成映射過程,有必要獲得一些插值爲非整數像素座標,因爲源圖像與目標圖像的像素座標不是一一對應的。在實際應用中,我們通過重映射來表達每個像素的位置 (x,y),轉換關係如下圖所示 :

這裏寫圖片描述

  其中 g( ) 是目標圖像, f( ) 是源圖像, h(x,y) 是作用於 (x,y) 的映射方法函數。在openCV中是通過 remap( )函數來實現重映射功能。
1.2、remap( )函數解析
   remap( )函數的原型和參數如下:

  C++: void remap(InputArray src, 
                  OutputArraydst, 
                  InputArray map1, 
                  InputArray map2, 
                  int interpolation,   
                  int borderMode=BORDER_CONSTANT, 
                  const Scalar& borderValue=Scalar( ) )  

參數解析:
  第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可,且需爲單通道8位或者浮點型圖像。
  第二個參數,OutputArray類型的dst,函數調用後的運算結果存在這裏,即這個參數用於存放函數調用後的輸出結果,和源圖片有一樣的尺寸和類型。
  第三個參數,InputArray類型的map1,它有兩種可能的表示對象。
    (1)表示點(x,y)的第一個映射。
    (2)表示CV_16SC2 , CV_32FC1 或CV_32FC2類型的X值。
  第四個參數,InputArray類型的map2,同樣,它也有兩種可能的表示對象,而且它是根據map1來確定表示那種對象。
    (1)若map1表示點(x,y)時。這個參數不代表任何值。
    (2)表示CV_16UC1 , CV_32FC1類型的Y值(第二個值)。
  第五個參數,int類型的interpolation,插值方式,之前的resize( )函數中有講到,需要注意,resize( )函數中提到的INTER_AREA插值方式在這裏是不支持的,所以可選的插值方式如下:
    (1)INTER_NEAREST - 最近鄰插值;

    (2)INTER_LINEAR – 雙線性插值(默認值);
    (3)INTER_CUBIC – 雙三次樣條插值(逾4×4像素鄰域內的雙三次插值);
    (4)INTER_LANCZOS4 -Lanczos插值(逾8×8像素鄰域的Lanczos插值)。
  第六個參數,int類型的borderMode,邊界模式,有默認值BORDER_CONSTANT,表示目標圖像中“離羣點(outliers)”的像素值不會被此函數修改。
  第七個參數,const Scalar&類型的borderValue,當有常數邊界時使用的值,其有默認值Scalar( ),即默認值爲0。
1.3、重映射實例
  1、代碼

#include "opencv2/highgui/highgui.hpp"  
#include "opencv2/imgproc/imgproc.hpp"  
#include <iostream>  

using namespace cv;

int main()
{
    //【0】變量定義  
    Mat srcImage, dstImage;
    Mat map_x, map_y;

    //【1】載入原始圖  
    srcImage = imread("1.jpg", 1);
    if (!srcImage.data) { printf("讀取圖片錯誤,請確定目錄下是否有imread函數指定的圖片存在~! \n"); return false; }
    imshow("原始圖", srcImage);

    //【2】創建和原始圖一樣的效果圖,x重映射圖,y重映射圖  
    dstImage.create(srcImage.size(), srcImage.type());
    map_x.create(srcImage.size(), CV_32FC1);
    map_y.create(srcImage.size(), CV_32FC1);

    //【3】雙層循環,遍歷每一個像素點,改變map_x & map_y的值  
    for (int j = 0; j < srcImage.rows; j++)
    {
        for (int i = 0; i < srcImage.cols; i++)
        {
            //改變map_x & map_y的值.   
            map_x.at<float>(j, i) = static_cast<float>(srcImage.cols-i);
            map_y.at<float>(j, i) = static_cast<float>(j);
        }
    }

    //【4】進行重映射操作  
    remap(srcImage, dstImage, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));

    //【5】顯示效果圖  
    imshow("【程序窗口】", dstImage);
    waitKey();

    return 0;
}

  2、運行結果
  (1)原圖

這裏寫圖片描述

  (2)重映射效果圖

這裏寫圖片描述

二、利用SURF算法進行特徵點檢測
2.1、SURF算法簡介
  Speeded Up Robust Features(SURF,加速穩健特徵)是一種穩健的局部特徵點檢測和描述算法。最初由Herbert Bay發表在2006年的歐洲計算機視覺國際會議(Europen Conference on Computer Vision,ECCV)上,並在2008年正式發表在Computer Vision and Image Understanding期刊上。
  Surf是對David Lowe在1999年提出的Sift算法的改進,提升了算法的執行效率,爲算法在實時計算機視覺系統中應用提供了可能。與Sift算法一樣,Surf算法的基本路程可以分爲三大部分:局部特徵點的提取、特徵點的描述、特徵點的匹配。但Surf在執行效率上有兩大制勝法寶——一個是積分圖在Hessian(黑塞矩陣)上的使用,一個是降維的特徵描述子的使用。一般來說,標準的SURF算子比SIFT算子快好幾倍,並且在多幅圖片下具有更好的穩定性。
  Surf改進了Sift算法的特徵提取和描述方式,用一種更爲高效的方式完成特徵的提取和描述,具體實現流程如下:
    (1) 構建Hessian(黑塞矩陣),生成所有的興趣點,將其用於特徵的提取;
    (2)構建尺度空間;
    (3) 特徵點定位;
    (4)特徵點主方向分配;
    (5) 生成特徵點描述子;
    (6)特徵點匹配。
2.2、drawKeypoints( )函數解析

void drawKeypoints(const Mat&image, 
            const vector<KeyPoint>& keypoints, 
            constScalar& color=Scalar::all(-1), 
            int flags=DrawMatchesFlags::DEFAULT )  

  第一個參數,const Mat&類型的src,輸入圖像。
  第二個參數,const vector&類型的keypoints,根據源圖像得到的特徵點,它是一個輸出參數。
  第三個參數,Mat&類型的outImage,輸出圖像,其內容取決於第五個參數標識符falgs。
  第四個參數,const Scalar&類型的color,關鍵點的顏色,有默認值Scalar::all(-1)。
  第五個參數,int類型的flags,繪製關鍵點的特徵標識符,有默認值DrawMatchesFlags::DEFAULT。可以選取別的值。
2.3、SURF特徵點檢測實例
1、下面實現特徵點的功能需要用到下面幾點:
  (1)使用 FeatureDetector 接口來發現感興趣點;
  (2)使用 SurfFeatureDetector 以及其函數 detect 來實現檢測過程;
  (3)使用函數 drawKeypoints 繪製檢測到的關鍵點。
2、代碼

#include "opencv2/core/core.hpp"  
#include "opencv2/features2d/features2d.hpp"  
#include "opencv2/highgui/highgui.hpp"  
#include "opencv2/nonfree/nonfree.hpp"  
#include <iostream>  

using namespace cv;

int main(int argc, char** argv)
{
    //【0】改變console字體顏色      
    system("color 2F");

    //【1】載入源圖片並顯示  
    Mat srcImage1 = imread("1.jpg", 1);
    Mat srcImage2 = imread("2.jpg", 1);
    if (!srcImage1.data || !srcImage2.data)//檢測是否讀取成功  
    {
        printf("讀取圖片錯誤,請確定目錄下是否有imread函數指定名稱的圖片存在~! \n"); return false;
    }
    imshow("原始圖1", srcImage1);
    imshow("原始圖2", srcImage2);

    //【2】定義需要用到的變量和類  
    int minHessian = 400;//定義SURF中的hessian閾值特徵點檢測算子  
    SurfFeatureDetector detector(minHessian);//定義一個SurfFeatureDetector(SURF) 特徵檢測類對象  
    std::vector<KeyPoint> keypoints_1, keypoints_2;//vector模板類是能夠存放任意類型的動態數組,能夠增加和壓縮數據  

    //【3】調用detect函數檢測出SURF特徵關鍵點,保存在vector容器中  
    detector.detect(srcImage1, keypoints_1);
    detector.detect(srcImage2, keypoints_2);

    //【4】繪製特徵關鍵點  
    Mat img_keypoints_1; 
    Mat img_keypoints_2;
    drawKeypoints(srcImage1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
    drawKeypoints(srcImage2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT);

    //【5】顯示效果圖  
    imshow("特徵點檢測效果圖1", img_keypoints_1);
    imshow("特徵點檢測效果圖2", img_keypoints_2);

    waitKey(0);
    return 0;
}

3、運行結果
  (1)原圖

這裏寫圖片描述

這裏寫圖片描述

  (2)特徵點檢測效果

這裏寫圖片描述

這裏寫圖片描述

三、SURF特徵點匹配
3.1、FLANN匹配算法簡介
  FLANN (Fast_Library_for_Approximate_Nearest_Neighbors)意爲快速最近鄰搜索包。它是一個對大數據集和高維特徵進行最近鄰搜索的算法的集合,而且這些算法都已經被優化過了。在面對大數據集時它的效果要好於 BFMatcher。 經驗證,FLANN比其他的最近鄰搜索軟件快10倍。
3.2、代碼

///SURF特徵點匹配  
#include "opencv2/opencv.hpp"  
using namespace cv;
#include "opencv2/nonfree/nonfree.hpp"//SURF相關  
#include "opencv2/legacy/legacy.hpp"//匹配器相關  
#include <iostream>  
using namespace std;
int main()
{
    //1.SURF特徵點提取——detect()方法    
    Mat srcImg1 = imread("1.jpg", CV_LOAD_IMAGE_COLOR);
    Mat srcImg2 = imread("2.jpg", CV_LOAD_IMAGE_COLOR);
    Mat dstImg1, dstImg2;
    //定義SURF特徵檢測類對象    
    SurfFeatureDetector surfDetector;//SurfFeatureDetector是SURF類的別名    
    //定義KeyPoint變量    
    vector<KeyPoint> keyPoints1;
    vector<KeyPoint> keyPoints2;
    //特徵點檢測    
    surfDetector.detect(srcImg1, keyPoints1);
    surfDetector.detect(srcImg2, keyPoints2);
    //繪製特徵點(關鍵點)    
    drawKeypoints(srcImg1, keyPoints1, dstImg1);
    drawKeypoints(srcImg2, keyPoints2, dstImg2);
    //顯示結果    
    imshow("dstImg1", dstImg1);
    imshow("dstImg2", dstImg2);
    //2.特徵點描述符(特徵向量)提取——compute()方法    
    SurfDescriptorExtractor descriptor;//SurfDescriptorExtractor是SURF類的別名     
    Mat description1;
    Mat description2;
    descriptor.compute(srcImg1, keyPoints1, description1);
    descriptor.compute(srcImg2, keyPoints2, description2);
    //3.使用Flann匹配器進行匹配——FlannBasedMatcher類的match()方法    
    FlannBasedMatcher matcher;//實例化Flann匹配器  
    vector<DMatch> matches;
    matcher.match(description1, description2, matches);
    //4.對匹配結果進行篩選(依據DMatch結構體中的float類型變量distance進行篩選)    
    float minDistance = 100;
    float maxDistance = 0;
    for (int i = 0; i < matches.size(); i++)
    {
        if (matches[i].distance < minDistance)
            minDistance = matches[i].distance;
        if (matches[i].distance > maxDistance)
            maxDistance = matches[i].distance;
    }
    cout << "minDistance: " << minDistance << endl;
    cout << "maxDistance: " << maxDistance << endl;
    vector<DMatch> goodMatches;
    for (int i = 0; i < matches.size(); i++)
    {
        if (matches[i].distance < 2 * minDistance)
        {
            goodMatches.push_back(matches[i]);
        }
    }
    //5.繪製匹配結果——drawMatches()    
    Mat dstImg3;
    drawMatches(srcImg1, keyPoints1, srcImg2, keyPoints2, goodMatches, dstImg3);
    imshow("dstImg3", dstImg3);
    waitKey(0);
    return 0;
}

3.3、運行結果
  (1)原圖

這裏寫圖片描述

這裏寫圖片描述

  (2)特徵匹配結果

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章