opencv之SURF图像匹配

1.概述

前面介绍模板匹配的时候已经提到模板匹配时一种基于灰度的匹配方法,而基于特征的匹配方法有FAST、SIFT、SURF等。上面两篇文章已经介绍过使用Surf算法进行特征点检測以及使用暴力匹配(BruteForceMatcher)和近期邻匹配(FLANN)两种匹配方法。接下来将更深一步介绍利用Surf检測到的特征点以及匹配对进行图像匹配.

利用Surf算法进行图像匹配其一般流程为:检測物体特征点->计算特征点描写叙述子->使用BurteForceMatcher或FLANN进行特征点匹配->匹配到的特征点进行透视变换findHomography()->透视矩阵变换perspectiveTransform()->绘制匹配物体轮廓

2. OpenCV API

透视变换

findHomography()

这个函数的作用是在图像原平面和目标图像平面之间寻找并返回一个透视变换矩阵H,例如以下:

所以反向投影误差

最小化。假设參数metchod被设置为默认值0,改函数使用全部的点以简单的最小二乘法计算一个初始的单应预计。能够简单理解为透视变换矩阵就是把一幅图像从一个空间变换到还有一个空间所须要进行旋转平移而进行加权的矩阵。

可是并非全部的点匹配对(srcPoints_i, dstPoints_i)都适合使用刚性的透视变换(有些异常值),这样变换得到的透视矩阵误差较大。

在这样的情况下能够使用两种鲁棒的方法RANSAC和LMeDS,尝试使用非常多相应匹配点的随机子集。使用该子集和最简单的最小二乘法预计单应性矩阵。然后计算得到的透视变换矩阵的质量/好坏(quality/goodness),然后使用最佳子集来计算单应性矩阵的初始预计矩阵和内在值/异常值(inliers/outliers)的掩码。

不管方法是否具有鲁棒性,使用Levenberg-Marquardt方法进一步精确计算单应性矩阵(假设方法具有鲁棒性仅使用内在值(inline))来降低再投影误差。

RANSAC差点儿能够处理随意比例的异常值,可是它须要一个阈值来区分内在值还是异常值。LMeDS不须要不论什么阈值,可是仅仅有在内在值比例大于50%的情况下才干准确计算。假设没有太多异常值,噪声有比較小的情况下使用默认方法就可以

函数用来查找初始的内在和外在矩阵,透视变换矩阵确定了一个比例.不管何时假设不能预计H矩阵则函数将返回一个空矩阵

Mat cv::findHomography  (   InputArray  srcPoints,
                            InputArray  dstPoints,
                            int     method = 0,
                            double  ransacReprojThreshold = 3,
                            OutputArray     mask = noArray(),
                            const int   maxIters = 2000,
                            const double    confidence = 0.995
                        )

srcPoints:原平面相应点,能够是CV_32FC2或vector类型的矩阵。

dstPoints:目标平面相应点,能够是CV_32FC2或vector类型的矩阵

method:用于计算矩阵的方法,可选方法上面已经介绍过有默认值0,CV_RANSAC和CV_LMEDS

ransacReprojThreshold:有默认值3,区分内在值还是异常值的阈值点,仅仅在RANSAC方法实用。当||dstPoints-convertPointsHomogeneous(H*srcPoints)||>ransacReprojThreshold,这个点就会被觉得是异常值(outlier).假设srcPoints和dstPoints是以像素为单位。则參数的取值范围一般在1-10之间。

mask:可选參数,有默认值noArray()。通过鲁棒性方法(RANSAC或LMEDS)设置输出掩码。

函数还有一种定义形式例如以下:

Mat cv::findHomography  (   InputArray  srcPoints,
                            InputArray  dstPoints,
                            OutputArray     mask,
                            int     method = 0,
                            double  ransacReprojThreshold = 3
                        )

求得的透视矩阵是一个3x3的变换矩阵。

perspectiveTransform()

函数能够进行向量透视矩阵变换。

void cv::perspectiveTransform   (   InputArray  src,
                                    OutputArray     dst,
                                    InputArray  m
                                )

src:双通道或三通道浮点型原图像或数组。每一个元素都是二维或三维可被转换的向量。

dst:目标数组或图像,与原图像有同样的尺寸和类型

m:变换矩阵,为3x3或4x4的浮点型矩阵

3.演示样例代码

#include <iostream>
#include <stdio.h>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\nonfree\features2d.hpp>
#include <opencv2\calib3d\calib3d.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat image_object = imread("match_src.jpg", IMREAD_GRAYSCALE);
    Mat image_scene = imread("match_dst.jpg", IMREAD_GRAYSCALE);

    //推断图像是否载入成功
    if (image_object.empty() || image_scene.empty())
    {
        cout << "图像载入失败";
        return -1;
    }
    else
        cout << "图像载入成功..." << endl << endl;

    //检測特征点
    const int minHessian = 700;
    SurfFeatureDetector detector(minHessian);
    vector<KeyPoint>keypoints_object, keypoints_scene;
    detector.detect(image_object, keypoints_object);
    detector.detect(image_scene, keypoints_scene);

    //计算特征点描写叙述子
    SurfDescriptorExtractor extractor;
    Mat descriptors_object, descriptors_scene;
    extractor.compute(image_object, keypoints_object, descriptors_object);
    extractor.compute(image_scene, keypoints_scene, descriptors_scene);

    //使用FLANN进行特征点匹配
    FlannBasedMatcher matcher;
    vector<DMatch>matches;
    matcher.match(descriptors_object, descriptors_scene, matches);

    //计算匹配点之间最大和最小距离
    double max_dist = 0;
    double min_dist = 100;
    for (int i = 0; i < descriptors_object.rows; i++)
    {
        double dist = matches[i].distance;
        if (dist < min_dist)
        {
            min_dist = dist;
        }
        else if (dist > max_dist)
        {
            max_dist = dist;
        }
    }
    printf("Max dist: %f \n", max_dist);
    printf("Min dist: %f \n", min_dist);

    //绘制“好”的匹配点
    vector<DMatch>good_matches;
    for (int i = 0; i < descriptors_object.rows; i++)
    {
        if (matches[i].distance<2*min_dist)
        {
            good_matches.push_back(matches[i]);
        }
    }
    Mat image_matches;
    drawMatches(image_object, keypoints_object, image_scene, keypoints_scene, good_matches, image_matches,
        Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

    //定位“好”的匹配点
    vector<Point2f> obj;
    vector<Point2f> scene;
    for (int i = 0; i < good_matches.size(); i++)
    {
        //DMathch类型中queryIdx是指match中第一个数组的索引,keyPoint类型中pt指的是当前点坐标
        obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
        scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
    }

    Mat H = findHomography(obj, scene, CV_RANSAC);
    vector<Point2f> obj_corners(4), scene_corners(4);
    obj_corners[0] = cvPoint(0, 0);
    obj_corners[1] = cvPoint(image_object.cols, 0);
    obj_corners[2] = cvPoint(image_object.cols, image_object.rows);
    obj_corners[3] = cvPoint(0, image_object.rows);

    perspectiveTransform(obj_corners, scene_corners, H);

    //绘制角点之间的直线
    line(image_matches, scene_corners[0] + Point2f(image_object.cols, 0),
        scene_corners[1] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
    line(image_matches, scene_corners[1] + Point2f(image_object.cols, 0),
        scene_corners[2] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
    line(image_matches, scene_corners[2] + Point2f(image_object.cols, 0),
        scene_corners[3] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
    line(image_matches, scene_corners[3] + Point2f(image_object.cols, 0),
        scene_corners[0] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);

    //输出图像
    namedWindow("匹配图像", WINDOW_AUTOSIZE);
    imshow("匹配图像", image_matches);
    waitKey(0);

    return 0;
}

程序说明

在定位匹配点中用到了DMatch的queryIdx、trainIdx成员变量和keyPoint的成员变量pt,做个说明:

DMatch有三个构造函数。当中一组例如以下:

cv::DMatch::DMatch  (   int     _queryIdx,  //在对描写叙述子匹配时。第一组特征点的索引
                        int     _trainIdx,  //在对描写叙述子匹配时,第二组特征点的索引
                        int     _imgIdx,    //多个图像中图像的索引
                        float   _distance   //两个特征向量间的欧氏距离,越小表明匹配度越高
                    )

对于keyPoint类有两种构造形式例如以下:

cv::KeyPoint::KeyPoint  (   Point2f     _pt,
                            float   _size,
                            float   _angle = -1,
                            float   _response = 0,
                            int     _octave = 0,
                            int     _class_id = -1
                        )
cv::KeyPoint::KeyPoint  (   float   x,
                            float   y,
                            float   _size,
                            float   _angle = -1,
                            float   _response = 0,
                            int     _octave = 0,
                            int     _class_id = -1
                        )

两种形式在本质上是一样的,仅仅是第一种形式中的特征点坐标pt在另外一种形式中以x和y的形式给出。

pt关键点坐标

size是关键点邻域直径

angle特征点方向,范围为[0,360)。负值表示不使用

response关键点检測器对于关键点的响应程度

octave关键点位于图像金字塔的层

class_id用于聚类的id

执行结果

原文地址:https://www.cnblogs.com/llguanli/p/8371062.html

时间: 2024-10-14 00:26:04

opencv之SURF图像匹配的相关文章

【OpenCV入门教程之十八】OpenCV仿射变换 &amp; SURF特征点描述合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/33320997 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 本篇文章中,我们一起探讨了OpenCV中

opencv提取surf特征点出现的错误

opencv实现surf特征的提取.本来是一个非常easy的代码,结果我执行时却出现了各种错误,以下来谈谈我出现的错误及问题的解决过程. 首先,我把提取surf特征的过程整合成了一个函数,我单独建立一个project读取两张图片,然后调用这个surf提取的函数时时不管是debug还是release模式下都是没有问题的.当我把这个函数加入到我如今已有的project代码里面的时候出现了各种奇葩错误.以下是我surf特征提取的函数 void surfdetect(IplImage *image1,I

OpenCV仿射变换 &amp; SURF特征点描述合辑

OpenCV仿射变换 & SURF特征点描述合辑 转载地址:http://blog.csdn.net/poem_qianmo/article/details/33320997?utm_source=tuicool&utm_medium=referral

OpenCV图像Surf与flann特征点(转载)

Surf(Speed Up Robust Feature) Surf算法的原理                                                                            1.构建Hessian矩阵构造高斯金字塔尺度空间 其实surf构造的金字塔图像与sift有很大不同,就是因为这些不同才加快了其检测的速度.Sift采用的是DOG图像,而surf采用的是Hessian矩阵行列式近似值图像.Hessian矩阵是Surf算法的核心,为了方便运

【OpenCV新手教程之十八】OpenCV仿射变换 &amp;amp; SURF特征点描写叙述合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/33320997 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本号: 2.4.9 本篇文章中.我们一起探讨了OpenCV

Opencv学习笔记(六)SURF学习笔记

原创文章,转载请注明出处:http://blog.csdn.net/crzy_sparrow/article/details/7392345 本人挺菜的,肯定有非常多错误纰漏之处 ,希望大家不吝指正. 看了harris角点检測之后,開始研究SURF角点检測,发现挺复杂的,一时也仅仅了解了大概,把了解的东西总结下,以便下次深入学习. SURF角点检測算法是对SIFT的一种改进,主要体如今速度上,效率更高.它和SIFT的主要差别是图像多尺度空间的构建方法不同. 在计算视觉领域,尺度空间被象征性的表述

OpenCV特征点检測------Surf(特征点篇)

Surf(Speed Up Robust Feature) Surf算法的原理                                                                           1.构建Hessian矩阵构造高斯金字塔尺度空间 事实上surf构造的金字塔图像与sift有非常大不同,就是由于这些不同才加快了其检測的速度. Sift採用的是DOG图像.而surf採用的是Hessian矩阵行列式近似值图像.Hessian矩阵是Surf算法的核心,为了方

OpenCV特征点检测------Surf(特征点篇)

Surf(Speed Up Robust Feature) Surf算法的原理                                                                           1.构建Hessian矩阵构造高斯金字塔尺度空间 其实surf构造的金字塔图像与sift有很大不同,就是因为这些不同才加快了其检测的速度.Sift采用的是DOG图像,而surf采用的是Hessian矩阵行列式近似值图像.Hessian矩阵是Surf算法的核心,为了方便运算

目标检测之harr---角点检测harr 的opencv实现

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/29356187 作者:毛星云(浅墨) 微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 本篇文章中,我们一起探讨了OpenCV中Ha