环境参数:
Windows10
vs2010
opencv2.4.13

SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。

Lowe将SIFT算法分解为如下四步:

  1. 尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。
  2. 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。
  3. 方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。
  4. 关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。

下面就来在Qt中调用Opencv来简单实现一下吧。
mainwindow.cpp如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_clicked()
{   //打开一张图片
    QString fileName = QFileDialog::getOpenFileName(this,tr("Open Image"),".",tr("Image File(*.png *.jpg *.jpeg *.bmp)"));
    image1 = cv::imread(fileName.toLatin1().data());
    if(image1.empty())
    {
        cout<<"Please open an image!" <<endl;
    }
    else
    {
     //把Mat转换成QImage
    cv::cvtColor(image1,image1,CV_BGR2RGB);
    QImage img = QImage((const unsigned char*)(image1.data),image1.cols,image1.rows,QImage::Format_RGB888);

    //在QLabel中显示图片
    ui->label->setPixmap(QPixmap::fromImage(img));
    ui->label->resize(ui->label->pixmap()->size());
    }
}

void MainWindow::on_pushButton_2_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,tr("Open Image"),".",tr("Image File(*.png *.jpg *.jpeg *.bmp)"));
    image2 = cv::imread(fileName.toLatin1().data());
    if(image1.empty())
    {
        cout<<"Please open an image!" <<endl;
    }
    else
    {
    cv::cvtColor(image2,image2,CV_BGR2RGB);
    QImage img2 = QImage((const unsigned char*)(image2.data),image2.cols,image2.rows,QImage::Format_RGB888);

    ui->label_2->setPixmap(QPixmap::fromImage(img2));
    ui->label_2->resize(ui->label_2->pixmap()->size());
    }
}

void MainWindow::on_pushButton_3_clicked()
{
    if(image1.empty())
    {
        cout<<"Please open an image!" <<endl;
    }
    else
    {   //特征点检测
        SiftFeatureDetector featureDetector;
        featureDetector.detect(image1, image1_kp);
        //画出特征点
        drawKeypoints(image1,image1_kp,image1,Scalar(0,255,0),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

        QImage img1 = QImage((const unsigned char*)(image1.data),image1.cols,image1.rows,QImage::Format_RGB888);
        ui->label->setPixmap(QPixmap::fromImage(img1));
        ui->label->resize(QSize(img1.width(),img1.height()));
    }
    if(image2.empty())
    {
        cout<<"Please open an image!" <<endl;
    }
    else
    {

        SiftFeatureDetector featureDetector;
        featureDetector.detect(image2, image2_kp);

        drawKeypoints(image2,image2_kp,image2,Scalar(0,255,0),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

        QImage img2 = QImage((const unsigned char*)(image2.data),image2.cols,image2.rows,QImage::Format_RGB888);

        ui->label_2->setPixmap(QPixmap::fromImage(img2));
        ui->label_2->resize(QSize(img2.width(),img2.height()));
    }

}

void MainWindow::on_pushButton_4_clicked()
{
    Mat image1_desc,image2_desc;
    //特征点提取
    SiftDescriptorExtractor featureExtractor;
    featureExtractor.compute(image1, image1_kp, image1_desc);
    featureExtractor.compute(image2, image2_kp, image2_desc);

    //FLANN based descriptor matcher object
    FlannBasedMatcher matcher;
    vector<Mat> image1_desc_collection(1, image1_desc);
    matcher.add(image1_desc_collection);
    matcher.train();

    //match image1 and image2 descriptor,getting 2 nearest neighbors for all test descriptor
    vector<vector<DMatch> > matches;
    matcher.knnMatch(image2_desc, matches, 2);

    //filter for good matches according to Lowe's algorithm
    vector<DMatch> good_matches;
    for (unsigned int i = 0; i < matches.size(); i++)
    {
        if (matches[i][0].distance < 0.6*matches[i][1].distance)
            good_matches.push_back(matches[i][0]);
    }

    Mat img_show;
    drawMatches(image2, image2_kp, image1, image1_kp, good_matches, img_show);

    QImage img3 = QImage((const unsigned char*)(img_show.data),img_show.cols,img_show.rows,QImage::Format_RGB888);

    ui->label->setPixmap(QPixmap::fromImage(img3));
    ui->label->resize(QSize(img3.width(),img3.height()));
    ui->label_2->hide();
}

这里需要注意的一点是由于图片在打开的时候已经从BGR转换到RGB了,最后提取和匹配特征点之后就不需要再转换了,不然图片发蓝。

对应的C++代码

#include<opencv.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include<iostream>

using namespace std;
using namespace cv;

int main()
{
    Mat image1 = imread("2.jpg");
    if(image1.empty())  
    {  
        cout<<"Please open an image!" <<endl;  
        return -1;
    }  
    //cvtColor(image1,image1,CV_BGR2RGB);

    Mat image2 = cv::imread("2.jpg");  
    if(image2.empty())  
    {  
        cout<<"Please open an image!" <<endl;  
        return -1;
    }  
    //cvtColor(image2,image2,CV_BGR2RGB);

    //特征点检测 
    SiftFeatureDetector featureDetector;
    std::vector<KeyPoint> image1_kp, image2_kp;
    featureDetector.detect(image1, image1_kp);
    //画出特征点  
    drawKeypoints(image1,image1_kp,image1,Scalar(0,255,0),DrawMatchesFlags::DRAW_RICH_KEYPOINTS); 

    featureDetector.detect(image2, image2_kp);  
    drawKeypoints(image2,image2_kp,image2,Scalar(0,255,0),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);


    Mat image1_desc,image2_desc;  
    //特征点提取  
    SiftDescriptorExtractor featureExtractor;  
    featureExtractor.compute(image1, image1_kp, image1_desc);  
    featureExtractor.compute(image2, image2_kp, image2_desc);  


    FLANN based descriptor matcher object  
    //FlannBasedMatcher matcher;  
    //vector<Mat> image1_desc_collection(1, image1_desc);  
    //matcher.add(image1_desc_collection);  
    //matcher.train();  

    match image1 and image2 descriptor,getting 2 nearest neighbors for all test descriptor  
    //vector<vector<DMatch> > matches;  
    //matcher.knnMatch(image2_desc, matches, 2);  

    filter for good matches according to Lowe's algorithm  
    //vector<DMatch> good_matches;  
    //for (unsigned int i = 0; i < matches.size(); i++)  
    //{  
    //  if (matches[i][0].distance < 0.6*matches[i][1].distance)  
    //      good_matches.push_back(matches[i][0]);  
    //}  

    //Mat img_show;  
    drawMatches(image2, image2_kp, image1, image1_kp, good_matches, img_show); 
    //drawMatches(image1, image1_kp, image2, image2_kp, good_matches, img_show);

    //用burte force进行匹配特征向量  
    BFMatcher matcher;//定义一个burte force matcher对象  
    //FlannBasedMatcher matcher;
    vector<DMatch>matches;  
    matcher.match(image1_desc,image2_desc,matches);  

    //绘制匹配线段  
    Mat img_matches;  
    drawMatches(image1, image1_kp, image2, image2_kp, matches, img_matches);//将匹配出来的结果放入内存img_matches中  

    //显示匹配线段  
    cvNamedWindow("sift_Matches");  
    imshow("sift_Matches",img_matches);//显示的标题为Matches  
    if (waitKey(0)==27)
    {
        cv::destroyAllWindows();
        return 0;
    }
    return 0;
}

Opencv进行图像特征点检查与匹配

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

#include <opencv2/nonfree/features2d.hpp>
#include <vector>
#include <opencv2/legacy/legacy.hpp>


using namespace cv;
using namespace std;

//Sift特征检测与匹配
int Sift(const string &file1,const string &file2)
{
    Mat img1 = imread(file1,CV_LOAD_IMAGE_GRAYSCALE);

    Mat img2 = imread(file2,CV_LOAD_IMAGE_GRAYSCALE);

    if(!img1.data || !img2.data)
    {
        cout<<"opencv error"<<endl;
        return -1;
    }

    cout <<"Open pictures successfully"<<endl;

    SiftFeatureDetector detector;
    vector<KeyPoint> keyPoints_1,keyPoints_2;

    detector.detect(img1,keyPoints_1);
    detector.detect(img2,keyPoints_2);

    Mat img_keypoints_1,img_keypoints_2;

    drawKeypoints(img1,keyPoints_1,img_keypoints_1,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
    drawKeypoints(img2,keyPoints_2,img_keypoints_2,Scalar::all(-1),DrawMatchesFlags::DEFAULT);

    cvNamedWindow("sift_keypoints_1");
    cvNamedWindow("sift_keypoints_2");

    imshow("sift_keypoints_1",img_keypoints_1);//显示特征点
    imshow("sift_keypoints_2",img_keypoints_2);

    SiftDescriptorExtractor extractor;//定义描述子对象

    Mat descriptors_1,descriptors_2;//存放特征向量的矩阵

    extractor.compute(img1,keyPoints_1,descriptors_1);//计算特征向量
    extractor.compute(img2,keyPoints_2,descriptors_2);

    //用burte force进行匹配特征向量
    BruteForceMatcher<L2<float>> matcher;//定义一个burte force matcher对象
    vector<DMatch>matches;
    matcher.match(descriptors_1,descriptors_2,matches);

    //绘制匹配线段
    Mat img_matches;
    drawMatches(img1,keyPoints_1,img2,keyPoints_2,matches,img_matches);//将匹配出来的结果放入内存img_matches中

    //显示匹配线段
    cvNamedWindow("sift_Matches");
    imshow("sift_Matches",img_matches);//显示的标题为Matches
    waitKey(6000);
}

// Surf 特征检测与匹配:
int Surf(const string &file1,const string &file2)
{
    Mat img1 = imread(file1,CV_LOAD_IMAGE_GRAYSCALE);

    Mat img2 = imread(file2,CV_LOAD_IMAGE_GRAYSCALE);

    if(!img1.data || !img2.data)
    {
        cout<<"opencv error"<<endl;
        return -1;
    }

    cout <<"Open pictures successfully"<<endl;

    SurfFeatureDetector detector;
    vector<KeyPoint> keyPoints_1,keyPoints_2;

    detector.detect(img1,keyPoints_1);
    detector.detect(img2,keyPoints_2);

    Mat img_keypoints_1,img_keypoints_2;

    drawKeypoints(img1,keyPoints_1,img_keypoints_1,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
    drawKeypoints(img2,keyPoints_2,img_keypoints_2,Scalar::all(-1),DrawMatchesFlags::DEFAULT);

    cvNamedWindow("surf_keypoints_1");
    cvNamedWindow("surf_keypoints_2");

    imshow("surf_keypoints_1",img_keypoints_1);//显示特征点
    imshow("surf_keypoints_2",img_keypoints_2);

    SurfDescriptorExtractor extractor;//定义描述子对象

    Mat descriptors_1,descriptors_2;//存放特征向量的矩阵

    extractor.compute(img1,keyPoints_1,descriptors_1);//计算特征向量
    extractor.compute(img2,keyPoints_2,descriptors_2);

    //用burte force进行匹配特征向量
    //BruteForceMatcher<L2<float>> matcher;//定义一个burte force matcher对象
    FlannBasedMatcher  matcher;
    vector<DMatch>matches;
    matcher.match(descriptors_1,descriptors_2,matches);

    //绘制匹配线段
    Mat img_matches;
    drawMatches(img1,keyPoints_1,img2,keyPoints_2,matches,img_matches);//将匹配出来的结果放入内存img_matches中

    //显示匹配线段
    cvNamedWindow("surf_Matches");
    imshow("surf_Matches",img_matches);//显示的标题为Matches
    waitKey(6000);

}

int main()
{
    string file1 = "1.jpg";
    string file2 = "1.jpg";

    //Sift(file1,file2);
    Surf(file1,file2);
    return 0;
}

提示:
以下代码可以相互替换

// 可以相互更换
SiftFeatureDetector featureDetector;  
SurfFeatureDetector featureDetector; 
OrbFeatureDetector featureDetector;
// 可以相互更换
SurfDescriptorExtractor extractor;//定义描述子对象
SiftDescriptorExtractor extractor;
OrbDescriptorExtractor
// 可以相互更换
FlannBasedMatcher matcher; 
BFMatcher matcher;//Brute Force based descriptor matcher object  
BruteForceMatcher<L2<float>> matcher;//定义一个burte force matcher对象