特征匹配及C++实现

1.简介

特征匹配是计算机视觉和图像处理领域中一种关键技术,它用于在两个图像之间找到并识别相同的特征点。

2.原理

2.1 分类

  1. 基于局部特征的匹配:这种匹配依赖于图像中局部区域的描述,如SIFT(尺度不变特征变换)、SURF(加速稳健特征)、ORB(Oriented FAST and Rotated BRIEF)等。这些方法首先检测关键点,然后生成描述子,最后通过比较描述子来匹配特征。
  2. 基于全局特征的匹配:与局部特征匹配不同,全局特征匹配使用整个图像或大区域的特征。例如,直方图匹配或基于图像的全局统计特性进行匹配。

  3. 直接匹配:直接匹配方法直接对两组特征点进行匹配,不涉及复杂的描述子生成,例如通过简单的像素值比较或者使用最近邻搜索。

  4. 间接匹配:间接匹配首先建立特征描述子,然后基于描述子的相似性进行点之间的匹配,如使用暴力匹配(Brute Force Match)FLANN(快速近邻)算法等。

2.2 实现步骤

  1. 关键点检测:首先,算法需要在图像中检测出关键点。这些关键点通常是图像中的显著特征,比如角点、边缘或纹理变化点。常见的关键点检测算法包括Harris角点检测器、FAST(Features from Accelerated Segment Test)等。

  2. 特征描述:检测到关键点后,需要对每个关键点的周围区域进行描述,以生成一个特征向量。这个描述应该能够捕捉到该点的局部特征,并且对图像的某些变化(如旋转、缩放、亮度变化等)保持不变。常见的特征描述算法包括SIFT(尺度不变特征变换)、SURF(加速稳健特征)、ORB(Oriented FAST and Rotated BRIEF)等。

  3. 特征向量匹配:对于两幅图像,算法会分别生成特征向量。然后,通过比较这些特征向量来寻找匹配点。通常,会计算两幅图像中特征向量之间的距离(如欧氏距离、汉明距离等),并使用一定的标准(如距离阈值或比值测试)来确定是否为匹配点。

  4. 距离阈值和比值测试:为了提高匹配的准确性,通常会使用距离阈值或比值测试来过滤掉错误的匹配。比值测试是一种常用的方法,它比较了最佳匹配和次佳匹配之间的距离比值,如果比值低于某个阈值,则认为匹配有效。

  5. 几何校正和变换:一旦确定了匹配点,就可以利用这些点来估计图像之间的几何变换(如平移、旋转、仿射变换等)。这可以通过最小二乘法、RANSAC(随机抽样一致性)等方法来实现。

  6. 鲁棒性:为了应对实际应用中的挑战(如噪声、遮挡、光照变化等),特征匹配算法需要具有一定的鲁棒性。这通常通过设计鲁棒的关键点检测器、特征描述子以及匹配策略来实现。

  7. 尺度和旋转不变性:一些高级的特征匹配算法还具有尺度和旋转不变性,这意味着它们能够在不同尺度和旋转角度下检测和匹配相同的特征。

3.C++实现

以下代码在ubuntu 20.04上验证通过,使用的OpenCV版本是3.4.15。

创建如图所示的文件夹,具体可以参考我的另一篇博客ORB算法及C++实现-CSDN博客

在match.cpp源文件中写入如下代码

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

using namespace std;

const float kRatioThresh = 0.7f;

int main(int argc, char **argv)
{
    //查看是否正确输入图像
    if(argc < 3)
    {
        cout<<"输入图像格式错误!"<<endl;
        return -1;
    }

    //读取图像
    cv::Mat img1 = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
    cv::Mat img2 = cv::imread(argv[2], cv::IMREAD_GRAYSCALE);

    if(img1.empty() || img2.empty())
    {
        cout<<"图像读取失败!"<<endl;
        return -1;
    }

    /*
    //创建ORB特征检测器
    cv::Ptr<cv::ORB> orb = cv::ORB::create();

    //检测关键点和计算描述符
    vector<cv::KeyPoint> keypoints1, keypoints2;
    cv::Mat descriptors1, descriptors2;
    orb -> detectAndCompute(img1,cv::Mat(),keypoints1, descriptors1);
    orb -> detectAndCompute(img2,cv::Mat(),keypoints2, descriptors2);
    */

    // 创建 SIFT 检测器
    cv::Ptr<cv::SIFT> sift = cv::SIFT::create();

    vector<cv::KeyPoint> keypoints1, keypoints2;
    cv::Mat descriptors1, descriptors2;

    sift -> detectAndCompute(img1,cv::Mat(),keypoints1, descriptors1);
    sift -> detectAndCompute(img2, cv::Mat(),keypoints2,descriptors2);


    /*
    //创建暴力匹配器
    cv::Ptr<cv::BFMatcher> bfmatcher = cv::BFMatcher::create(cv::NORM_HAMMING, true);
    //匹配关键点
    vector < cv::DMatch > matches;
    bfmatcher -> match(descriptors1, descriptors2, matches);
    */

    //创建FLANN匹配器
    cv::Ptr<cv::FlannBasedMatcher> flannmatcher = cv::FlannBasedMatcher::create();
    //匹配关键点
    vector<vector<cv::DMatch>> matches;
    flannmatcher -> knnMatch(descriptors1, descriptors2, matches, 2);

    //根据最近邻次近邻距离比来筛选特征点
    vector<cv::DMatch> good_matches;
    for(size_t i=0;i<matches.size();i++)
    {
        if(matches[i][0].distance < kRatioThresh*matches[i][1].distance)
        {
            good_matches.push_back(matches[i][0]);
        }
    }

    cv::Mat img_matches;
    cv::drawMatches(img1,keypoints1, img2, keypoints2, good_matches, img_matches);

    //显示匹配结果
    cv::imshow("Matches",img_matches);
    cv::waitKey(0);

    return 0;
}

上述代码中提供了SIFT和ORB进行特征检测,然后进行暴力匹配或FLANN匹配,读者可以都尝试一下。

注意:OpenCV C++版本要在3.4.11及以后才能执行命令

cv::Ptr<cv::SIFT> sift = cv::SIFT::create()  //创建SIFT检测器

然后在CMakeLists.txt文件中写入以下代码

cmake_minimum_required(VERSION 3.2)

#指定项目名称
project(matches)

#指定C++运行版本为11
set(CMAKE_CXX_STANDARD 11)

#找到OpenCV库
find_package(OpenCV REQUIRED)

#包含OpenCV的头文件
include_directories(${OpeCV_INCLUDE_DIRS})

#添加可执行文件,文件名为matches
add_executable(matches src/match.cpp)

#链接到OpenCV库
target_link_libraries(matches ${OpenCV_LIBS})

接着在build文件夹下打开终端并运行以下代码

cmake ..
make

运行成功后界面如下

其中绿色的文件matches就是我们生成的可执行文件,和CMakeLists.txt最后两行一致。

最后在build文件夹下执行我们的可执行文件

./matches ./../images/left01.jpg ./../images/left02.jpg

结果如下

更多详细情况可参考OpenCV 之 特征匹配 - 知乎

  • 16
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值