计算机视觉攻略 笔记9 (局部模板匹配)

局部模板匹配

  • 通过特征点匹配,可以将一幅图像的点集和另一幅图像(或一批图像)的点集关联起来。如果两个点集对应着现实世界中的同一个场景元素,它们就应该是匹配的。
  • 仅凭单个像素就判断两个关键点的相似度显然是不够的,因此要在匹配过程中考虑每个关键点周围的图像块。如果两幅图像块对应着同一个场景元素,那么它们的像素值应该会比较相似。
  • 最常见的图像块是边长为奇数的正方形,关键点的位置就是正方形的中心。可通过比较块内像素的强度值来衡量两个正方形图像块的相似度。常见的方案是采用简单的差的平方和(Sum of Squared Differences,SSD)算法。

示例程序

#include <iostream>
#include <algorithm>
#include <vector>
#include <opencv2/core/core.hpp>
#include </home/jlm/3rdparty/opencv/opencv_contrib/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;

int main(int argc, char** argv) {
    
    
    if(argc != 3)
    {
    
    
        cerr << "don't get the right numbers of image" << endl;
        return -1;
    }
    cv::Mat image1 = cv::imread(argv[1],cv::IMREAD_GRAYSCALE);
    cv::Mat image2 = cv::imread(argv[2],cv::IMREAD_GRAYSCALE);
    if(image1.empty() || image2.empty())
    {
    
    
        cout << "don't get the data of the argv[1]" << endl;
        return -1;
    }
    cv::imshow("Original1", image1);
    cv::imshow("Original2", image2);
    cv::waitKey(0);
    // 定义特征检测器
    cv::Ptr<cv::FeatureDetector> ptrDetector ; // 泛型检测器指针
    ptrDetector = // 这里选用FAST检测器
            cv::FastFeatureDetector::create(80);
//    这里采用了可以指向任何特征检测器的泛型指针类型 cv::Ptr<cv::FeatureDetector> 。
//    上述代码可用于各种兴趣点检测器,只需在调用函数时更换检测器即可。
    vector<cv::KeyPoint> vKeypoints1;
    vector<cv::KeyPoint> vkeypoints2;
    // 检测关键点
    ptrDetector-> detect(image1, vKeypoints1);
    ptrDetector-> detect(image2, vkeypoints2);

    // 定义正方形邻域11X11
    const int nsize(11);    //邻域尺寸
    cv::Rect neighborhood(0, 0, nsize, nsize); // 11X11
    cv::Mat patch1;
    cv::Mat patch2;

//    将一幅图像的关键点与另一幅图像的全部关键点进行比较。在第二幅图像中找出与第一幅图
//    像中的每个关键点最相似的图像块。
    // 在第二幅图像中找出与第一幅图像中的每个关键点最匹配的
    cv::Mat result;
    std::vector<cv::DMatch> matches;
    // 针对图像一的全部关键点
    for (int i = 0; i < vKeypoints1.size(); i++)
    {
    
    
        // 定义图像块
        neighborhood.x = vKeypoints1[i].pt.x - nsize/2;
        neighborhood.y = vKeypoints1[i].pt.y - nsize/2;
        // 如果超出图像范围,就继续处理下一个点
        // 以关键点为中心的正方形邻域
        if (neighborhood.x < 0 || neighborhood.y < 0  ||
            neighborhood.x + nsize >= image1.cols ||
            neighborhood.y + nsize >= image1.rows)
        {
    
    
            continue;
        }
        // 第一幅图像的块
        patch1 = image1(neighborhood);
        // 存放最匹配的值
        cv::DMatch bestMatch;
        // 针对第二幅图像的全部关键点
        for (int j = 0; j < vkeypoints2.size(); j++)
        {
    
    
            // 定义图像块
            neighborhood.x = vkeypoints2[j].pt.x - nsize/2;
            neighborhood.y = vkeypoints2[j].pt.y - nsize/2;
            // 如果邻域超出图像范围,就继续处理下一个点
            if (neighborhood.x < 0 || neighborhood.y < 0 ||
                neighborhood.x + nsize >= image2.cols ||
                neighborhood.y + nsize >= image2.rows)
            {
    
    
                continue;
            }
            // 第二幅图像的块
            patch2 = image2(neighborhood);

            // 匹配两个图像块
            //    注意,这里用 cv::matchTemplate 函数来计算图像块的相似度
            cv::matchTemplate(patch1, patch2, result, cv::TM_SQDIFF);

            // 检查是否位最佳匹配
            //    找到一个可能的匹配项后,用一个 cv::DMatch 对象来表示。这个工具类存储了两个被
            //    匹配关键点的序号和它们的相似度。
            if(result.at<float>(0,0) < bestMatch.distance)
            {
    
    
                bestMatch.distance = result.at<float>(0, 0);
                bestMatch.queryIdx = i;
                bestMatch.trainIdx = j;
            }
        }
        // 添加最佳匹配
        matches.push_back(bestMatch);
    }
//    两个图像块越相似,它们对应着同一个场景点的可能性就越大。因此需要根据相似度对匹配结果进行排序
    // 提取最佳的25个匹配项
    std::nth_element(matches.begin(), matches.begin() + 25, matches.end());
    matches.erase(matches.begin() + 25, matches.end());
    // 画出匹配结果
    cv::Mat matchImage;
    cv::drawMatches(image1, vKeypoints1,
                    image2, vkeypoints2,
                    matches,
                    matchImage,
                    cv::Scalar(255, 255, 255),
                    cv::Scalar(255, 255, 255));
    cv::imshow("Result Image",matchImage);
    cv::waitKey(0);
    return 0;
}

这里用一个简单的标准来比较图像块,即指定 cv::TM_SQDIFF 标志,逐个像素地计算差值的平方和。在比较图像 I 1 的像素(x, y)和图像 I 2 的像素(x’, y’ )时,用下面的公式衡量相似度:
在这里插入图片描述这些(i, j)点的累加值就是以每个点为中心的整个正方形模板的偏移值。如果两个图像块比较相似,它们的相邻像素之间的差距就比较小,因此累加值最小的块就是最匹配的图像块。

识别出的匹配项存储在 cv::DMatch 类型的向量中。 cv::DMatch 数据结构本质上包含两个索引,第一个索引指向第一个关键点向量中的元素,第二个索引指向第二个关键点向量中匹配上的特征点。它还包含一个数值,表示两个已匹配的描述子之间的差距。

猜你喜欢

转载自blog.csdn.net/jlm7689235/article/details/108051542