Advertisement

OpenCV中提取SIFT特征点、图像匹配、图像配准

阅读量:

该代码实现了一幅图像的自动配准过程,主要包括以下步骤:
读取图像:读取待配准图和基准图。
特征检测:使用SIFT算法提取两幅图像的特征点。
特征描述符提取:为每个特征点生成描述符。
特征匹配:使用BFMatcher进行暴力匹配,获取特征点对。
误匹配剔除:通过RANSAC算法剔除误匹配点对。
单应矩阵计算:根据剔除后的匹配点对计算单应矩阵。
图像配准:利用单应矩阵对待配准图像进行重采样配准。
该方法通过多步处理实现了图像的自动配准,适用于不同姿态下的图像匹配。

在实际应用中,首先通过SIFT算法提取图像的特征点,然后对提取的特征点进行配对,形成特征点对。接着,计算得到的变换矩阵通常即为单应矩阵,最后利用该单应矩阵进行图像的几何配准。目前,基于OpenCV的实现已成为该领域较为成熟的技术方案,具体代码和主要的函数实现将在下文进行详细讲解。

一、代码如下:
复制代码
    #include<iostream>
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/features2d/features2d.hpp>
    #include <opencv2/nonfree/nonfree.hpp>
    #include "opencv2/calib3d/calib3d.hpp"
    #include <highgui.h>  
    #include <cv.h>  
    #include<vector>
    #include<cmath>
    
    #define PI 3.14159265358979323846264338327950288419716939937510582097
    using namespace std;
    using namespace cv;
    
    void main()
    {   
    //读取原始基准图和待匹配图
    Mat srcImg1 = imread("1.JPG");      //待配准图
    Mat srcImg2 = imread("2.JPG");      //基准图
    
    //显示基准和待配准图
    imshow("待配准图", srcImg1);
    imshow("基准图", srcImg2);
    
    //定义SIFT特征检测类对象
    SiftFeatureDetector siftDetector1;
    SiftFeatureDetector siftDetector2;
    
    //定义KeyPoint变量
    vector<KeyPoint>keyPoints1;
    vector<KeyPoint>keyPoints2;
    
    //特征点检测
    siftDetector1.detect(srcImg1, keyPoints1);
    siftDetector2.detect(srcImg2, keyPoints2);
    
    //绘制特征点(关键点)
    Mat feature_pic1, feature_pic2;
    drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar::all(-1));
    drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar::all(-1));
    
    drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar(0, 255, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar(0, 255, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    
    //显示原图
    
    //显示结果
    imshow("feature1", feature_pic1);
    imshow("feature2", feature_pic2);
    
    //计算特征点描述符 / 特征向量提取
    SiftDescriptorExtractor descriptor;
    Mat description1;
    descriptor.compute(srcImg1, keyPoints1, description1);
    Mat description2;
    descriptor.compute(srcImg2, keyPoints2, description2);
    
    cout << keyPoints1.size() << endl;
    cout << description1.cols << endl;      //列数
    cout << description1.rows << endl;      //行数
    
    
    //进行BFMatch暴力匹配
    //BruteForceMatcher<L2<float>>matcher;    //实例化暴力匹配器
    FlannBasedMatcher matcher;  //实例化FLANN匹配器
    vector<DMatch>matches;   //定义匹配结果变量
    matcher.match(description1, description2, matches);  //实现描述符之间的匹配
    
    //中间变量
    int i,j,k;double sum=0;double b;
    
    double max_dist = 0;  
    double min_dist = 100;  
    for(int i=0; i<matches.size(); i++)  
    {  
        double dist = matches[i].distance;  
        if(dist < min_dist) 
            min_dist = dist;  
        if(dist > max_dist) 
            max_dist = dist;  
    }  
    cout<<"最大距离:"<<max_dist<<endl;  
    cout<<"最小距离:"<<min_dist<<endl;  
    
    //筛选出较好的匹配点  
    vector<DMatch> good_matches;  
    double dThreshold = 0.5;    //匹配的阈值,越大匹配的点数越多
    for(int i=0; i<matches.size(); i++)  
    {  
        if(matches[i].distance < dThreshold * max_dist)  
        {  
            good_matches.push_back(matches[i]);  
        }  
    }  
    
    //RANSAC 消除误匹配特征点 主要分为三个部分:
    //1)根据matches将特征点对齐,将坐标转换为float类型
    //2)使用求基础矩阵方法findFundamentalMat,得到RansacStatus
    //3)根据RansacStatus来将误匹配的点也即RansacStatus[i]=0的点删除
    
       //根据matches将特征点对齐,将坐标转换为float类型
    vector<KeyPoint> R_keypoint01,R_keypoint02;
    for (i=0;i<good_matches.size();i++)   
    {
        R_keypoint01.push_back(keyPoints1[good_matches[i].queryIdx]);
        R_keypoint02.push_back(keyPoints2[good_matches[i].trainIdx]);
       // 这两句话的理解:R_keypoint1是要存储img01中能与img02匹配的特征点,
       // matches中存储了这些匹配点对的img01和img02的索引值
    }
    
    //坐标转换
    vector<Point2f>p01,p02;
    for (i=0;i<good_matches.size();i++)
    {
        p01.push_back(R_keypoint01[i].pt);
        p02.push_back(R_keypoint02[i].pt);
    }
    
    //计算基础矩阵并剔除误匹配点
    vector<uchar> RansacStatus;
    Mat Fundamental= findHomography(p01,p02,RansacStatus,CV_RANSAC);
    Mat dst;
    warpPerspective(srcImg1, dst, Fundamental,Size(srcImg1.cols,srcImg1.rows));
    
    imshow("配准后的图",dst );
    imwrite("dst.jpg", dst);
    
    //剔除误匹配的点对
    vector<KeyPoint> RR_keypoint01,RR_keypoint02;
    vector<DMatch> RR_matches;            //重新定义RR_keypoint 和RR_matches来存储新的关键点和匹配矩阵
    int index=0;
    for (i=0;i<good_matches.size();i++)
    {
        if (RansacStatus[i]!=0)
        {
            RR_keypoint01.push_back(R_keypoint01[i]);
            RR_keypoint02.push_back(R_keypoint02[i]);
            good_matches[i].queryIdx=index;
            good_matches[i].trainIdx=index;
            RR_matches.push_back(good_matches[i]);
            index++;
        }
    }
    cout<<"找到的特征点对:"<<RR_matches.size()<<endl;
    
    //画出消除误匹配后的图
    Mat img_RR_matches;
    drawMatches(srcImg1,RR_keypoint01,srcImg2,RR_keypoint02,RR_matches,img_RR_matches, Scalar(0, 255, 0), Scalar::all(-1));
    imshow("消除误匹配点后",img_RR_matches);
    imwrite("匹配图.jpg", img_RR_matches);
    
    waitKey(0);
    }
二、主要函数解释:

1、findHomography函数:根据匹配点对坐标对的值,和RANSAC随机一致性算法剔除误匹配点对,并计算两幅图的单应矩阵。
主要参数:参数1–待配准图中特征点坐标向量,
参数2–基准图像中特征点坐标向量;
返回值–计算出的3*3的单应矩阵的值,该矩阵即代表两幅图的变换关系
注意:这个函数计算处单应矩阵,最少需要4对成功的匹配点对。
2、warpPerspective函数:根据单应矩阵,对待配准的图像进行重采样,计算配准后的图。
主要参数:参数1–待配准图类对象,
参数2–待匹配图根据单应矩阵变换后的输出图像类对象;
参数3–单应矩阵;
参数4–输出图像类对象的大小,即输出图像的高度和宽度;
返回值–计算出的3*3的单应矩阵的值,该矩阵即代表两幅图的变换关系

三、算法运行效果:
这里写图片描述

实验结果表明,从待匹配的图像到匹配后的图像,图像发生了一定程度的旋转,并且逐渐趋向于基准图像。

全部评论 (0)

还没有任何评论哟~