【opencv】教程代码 —features2D(2)

使用SURF算法检测两幅图关键点后暴力匹配

SURF特征检测

使用SURF(Speeded Up Robust Features)算法来检测两张图像之间的关键点,并使用FLANN(Fast Library for Approximate Nearest Neighbors)基于特征描述符向量进行匹配

图像特征点检测(SURF)、(FLANN)匹配和目标定位

1.feature_description—SURF_matching_Demo.cpp(Debug模式)使用SURF算法检测两幅图关键点后暴力匹配

5d2215a509e099110a897b45e4bcd0a7.png

box.png

9f32be968bf98412e06bc71f3decfee6.png

box_in_scene.png

708d7dfeccbd55a5a67018744f85c2be.png

minHessian=400

1aa3e6c00029a382a651dfb8fde6ed23.png

minHessian=1000

6128a411d9dad7c93dc1218bdddb225f.png

minHessian=10000

911da9be70835cf10d8a2c2ca2e25c1a.png

#include <iostream> // 包含标准输入输出流库
#include "opencv2/core.hpp" // 包括OpenCV的核心功能头文件
#ifdef HAVE_OPENCV_XFEATURES2D // 如果定义了HAVE_OPENCV_XFEATURES2D
#include "opencv2/highgui.hpp" // 包括用户界面相关功能的头文件
#include "opencv2/features2d.hpp" // 包括特征检测相关功能的头文件
#include "opencv2/xfeatures2d.hpp" // 包括特殊特征检测的头文件(非free模块)


using namespace cv; // 使用cv命名空间
using namespace cv::xfeatures2d; // 使用cv的xfeatures2d命名空间
using std::cout; // 使用标准库中的cout
using std::endl; // 使用标准库中的endl


const char* keys = // 定义命令行参数
    "{ help h |                  | Print help message. }"
    "{ input1 | box.png          | Path to input image 1. }"
    "{ input2 | box_in_scene.png | Path to input image 2. }";


int main( int argc, char* argv[] ) // 主函数
{
    CommandLineParser parser( argc, argv, keys ); // 创建命令行解析器
    Mat img1 = imread( samples::findFile( parser.get<String>("input1") ), IMREAD_GRAYSCALE ); // 读取第一张图片为灰度图
    Mat img2 = imread( samples::findFile( parser.get<String>("input2") ), IMREAD_GRAYSCALE ); // 读取第二张图片为灰度图
    if ( img1.empty() || img2.empty() ) // 如果图片读取失败
    {
        cout << "Could not open or find the image!\n" << endl; // 打印错误信息
        parser.printMessage(); // 打印帮助信息
        return -1; // 返回错误代码
    }


    //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors
    int minHessian = 400; // 设置SURF算法的Hessian阈值
    Ptr<SURF> detector = SURF::create( minHessian ); // 创建SURF特征检测器
    std::vector<KeyPoint> keypoints1, keypoints2; // 存放两张图片的关键点
    Mat descriptors1, descriptors2; // 存放两张图片的描述符
    detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 ); // 检测并计算img1的关键点和描述符
    detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 ); // 检测并计算img2的关键点和描述符


    //-- Step 2: Matching descriptor vectors with a brute force matcher
    // Since SURF is a floating-point descriptor NORM_L2 is used
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE); // 创建暴力匹配器
    std::vector< DMatch > matches; // 存放匹配结果
    matcher->match( descriptors1, descriptors2, matches ); // 执行匹配
    //-- Draw matches
    Mat img_matches; // 存放匹配之后的图片
    drawMatches( img1, keypoints1, img2, keypoints2, matches, img_matches ); // 绘制匹配结果


    //-- Show detected matches
    imshow("Matches", img_matches ); // 在窗口中显示匹配结果


    waitKey(); // 等待用户按键
    return 0; // 程序成功结束
}
#else
int main() // 如果没定义HAVE_OPENCV_XFEATURES2D
{
    std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; // 提示需要xfeatures2d模块
    return 0; // 程序结束
}
#endif

这段代码是使用OpenCV库进行图像特征点检测和匹配的示例。它通过SURF算法来检测两幅图片中的特征点,并使用暴力匹配器来找出这些特征点之间的匹配关系。最终将匹配的结果用图形化的方式展示出来。这对于很多计算机视觉任务,比如物体识别、图像拼接等是非常实用的。如果环境中没有安装OpenCV的xfeatures2d模块,则给出提示信息。

e942b242e11c0c6c54a5fa63620bc523.png

2.feature_detection—SURF_detection_Demo.cpp  SURF特征检测

38f480fb07cd90b6c8af1c0da83425bd.png

675595e35ce838102ea85f88c84d6b7b.png

#include <iostream> // 包含输入输出流的头文件
#include "opencv2/core.hpp" // 包含OpenCV核心功能的头文件
#ifdef HAVE_OPENCV_XFEATURES2D
#include "opencv2/highgui.hpp" // 包含OpenCV GUI功能的头文件
#include "opencv2/features2d.hpp" // 包含OpenCV特征检测相关功能的头文件
#include "opencv2/xfeatures2d.hpp" // 包含OpenCV extra features2d模块的头文件,例如SURF


using namespace cv; // 使用cv命名空间
using namespace cv::xfeatures2d; // 使用cv::xfeatures2d命名空间,因为SURF是其中的一部分
using std::cout; // 使用标准命名空间下的cout
using std::endl; // 使用标准命名空间下的endl


int main( int argc, char* argv[] )
{
    CommandLineParser parser( argc, argv, "{@input | box.png | input image}" ); // 命令行参数解析器
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_GRAYSCALE ); // 读取图像文件转换为灰度图
    if ( src.empty() )
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "Usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }


    //-- Step 1: Detect the keypoints using SURF Detector
    // 步骤1:使用SURF检测器检测关键点
    int minHessian = 400; // Hessian算子的阈值
    Ptr<SURF> detector = SURF::create( minHessian ); // 创建SURF检测器
    std::vector<KeyPoint> keypoints; // 关键点的向量
    detector->detect( src, keypoints ); // 检测关键点


    //-- Draw keypoints
    // 绘制关键点
    Mat img_keypoints;
    drawKeypoints( src, keypoints, img_keypoints ); // 将检测到的关键点绘制到图上


    //-- Show detected (drawn) keypoints
    // 显示检测到(绘制的)关键点
    imshow("SURF Keypoints", img_keypoints ); // 创建一个窗口展示关键点


    waitKey(); // 等待任意键输入
    return 0;
}
#else
int main()
{
    std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl;
    return 0;
}
#endif

这段代码的功能是使用SURF算法来检测图像中的关键点。首先,它使用命令行参数解析器读取图像,并将其转换为灰度图。如果无法读取图像,它会打印错误消息并退出程序。如果图像读取成功,它创建一个SURF检测器,并根据指定的Hessian阈值来检测图像中的关键点。然后,它将这些关键点绘制在源图像上,并使用imshow函数显示结果。如果没有安装OpenCV的xfeatures2d类的附加模块,它会在命令行输出一条信息,说明需要xfeatures2d模块才能运行此代码。

3. SURF_FLANN_matching_Demo.cpp 使用SURF(Speeded Up Robust Features)算法来检测两张图像之间的关键点,并使用FLANN(Fast Library for Approximate Nearest Neighbors)基于特征描述符向量进行匹配

480c3931209ad1feb76836ec477641c5.png

0a0f8d0416a367929207b2d446b11162.png

#include <iostream>  // 包含标准输入输出流库
#include "opencv2/core.hpp" // 包含OpenCV核心功能库


#ifdef HAVE_OPENCV_XFEATURES2D // 如果定义了HAVE_OPENCV_XFEATURES2D(检查xfeatures2d模块是否可用)
#include "opencv2/highgui.hpp" // 包含OpenCV高级GUI库
#include "opencv2/features2d.hpp" // 包含OpenCV特征点检测库
#include "opencv2/xfeatures2d.hpp" // 包含OpenCV附加特征点检测库


using namespace cv;  // 使用cv命名空间
using namespace cv::xfeatures2d; // 使用cv::xfeatures2d命名空间
using std::cout;  // 使用cout(标准输出)
using std::endl;  // 使用endl(换行符)


const char* keys =  // 定义命令行参数
    "{ help h |                  | Print help message. }"  // 帮助信息
    "{ input1 | box.png          | Path to input image 1. }"  // 输入图像1的路径
    "{ input2 | box_in_scene.png | Path to input image 2. }";  // 输入图像2的路径


int main( int argc, char* argv[] )  // 主函数
{
    CommandLineParser parser( argc, argv, keys ); // 命令行解析器
    Mat img1 = imread( samples::findFile( parser.get<String>("input1") ), IMREAD_GRAYSCALE ); // 读取输入图像1为灰度图
    Mat img2 = imread( samples::findFile( parser.get<String>("input2") ), IMREAD_GRAYSCALE ); // 读取输入图像2为灰度图
    if ( img1.empty() || img2.empty() ) // 如果任一图像为空则打印错误信息
    {
        cout << "Could not open or find the image!\n" << endl;
        parser.printMessage(); // 打印帮助信息
        return -1; // 返回-1表示错误
    }


    //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors
    int minHessian = 400; // 定义Hessian阈值
    Ptr<SURF> detector = SURF::create( minHessian ); // 创建SURF检测器
    std::vector<KeyPoint> keypoints1, keypoints2;  // 定义关键点向量
    Mat descriptors1, descriptors2;  // 定义描述符矩阵
    detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 ); // 检测图像1的关键点和描述符
    detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 ); // 检测图像2的关键点和描述符


    //-- Step 2: Matching descriptor vectors with a FLANN based matcher
    // Since SURF is a floating-point descriptor NORM_L2 is used
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED); // 创建基于FLANN的描述符匹配器
    std::vector< std::vector<DMatch> > knn_matches;  // 定义K邻近匹配向量
    matcher->knnMatch( descriptors1, descriptors2, knn_matches, 2 ); // 执行K邻近匹配


    //-- Filter matches using the Lowe's ratio test
    const float ratio_thresh = 0.7f;  // 定义Lowe's比率测试阈值
    std::vector<DMatch> good_matches;  // 定义良好匹配向量
    for (size_t i = 0; i < knn_matches.size(); i++) // 遍历所有K邻近匹配
    {
        if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance) // 如果满足Lowe's比率测试则认为是良好匹配
        {
            good_matches.push_back(knn_matches[i][0]); // 添加到良好匹配向量中
        }
    }


    //-- Draw matches
    Mat img_matches;  // 定义匹配结果图像
    drawMatches( img1, keypoints1, img2, keypoints2, good_matches, img_matches, Scalar::all(-1),
                 Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); // 绘制良好匹配


    //-- Show detected matches
    imshow("Good Matches", img_matches ); // 显示匹配结果图像


    waitKey();  // 等待按键事件
    return 0;  // 返回0表示成功
}
#else // 如果没有定义HAVE_OPENCV_XFEATURES2D
int main()  // 主函数
{
    std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; // 打印错误信息
    return 0;  // 返回0表示成功
}
#endif

这段代码是一个使用OpenCV库进行图像处理的例子,特别是使用SURF(Speeded Up Robust Features)算法来检测两张图像之间的关键点,并使用FLANN(Fast Library for Approximate Nearest Neighbors)基于特征描述符向量进行匹配。首先,代码读取两张图像并将它们转换成灰度图。然后,SURF算法被用于检测关键点并计算描述符。接着,运用FLANN匹配器对这两组描述符进行匹配,并使用Lowe's比率测试过滤出良好的匹配点。最后,良好的匹配点被绘制并显示出来。如果没有安装OpenCV的xfeatures2d模块,代码将打印一条错误信息。

drawMatches( img1, keypoints1, img2, keypoints2, good_matches, img_matches, Scalar::all(-1),
                 Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );

b92e36a6a9d798c224c32e1b961df1b8.png

4.SURF_FLANN_matching_homography_Demo.cpp  图像特征点检测、匹配和目标定

c98a4988f0e8f4f163e525aeab49f4ff.png

8e6ebca54694c0c80c69eb93adc6ffb6.png

// 引用必要的库
#include <iostream>
#include "opencv2/core.hpp"
#ifdef HAVE_OPENCV_XFEATURES2D // 判断是否有xfeatures2d模块,如果有则进行下面的操作
#include "opencv2/calib3d.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/features2d.hpp"
#include "opencv2/xfeatures2d.hpp"


// 使用命名空间简化代码
using namespace cv;
using namespace cv::xfeatures2d;
using std::cout;
using std::endl;


// 定义程序参数
const char* keys =
        "{ help h |                  | 打印帮助信息}"
        "{ input1 | box.png          | 第一张输入图像路径}"
        "{ input2 | box_in_scene.png | 第二张输入图像路径}";


// 程序主函数
int main( int argc, char* argv[] )
{
    // 定义命令行解析器
    CommandLineParser parser( argc, argv, keys );
    // 读取图片
    Mat img_object = imread( samples::findFile( parser.get<String>("input1") ), IMREAD_GRAYSCALE );
    Mat img_scene = imread( samples::findFile( parser.get<String>("input2") ), IMREAD_GRAYSCALE );
    // 判断图片是否读取成功
    if ( img_object.empty() || img_scene.empty() )
    {
        cout << "无法打开或找到图片!\n" << endl;
        parser.printMessage(); //打印错误信息
        return -1;
    }


    //-- Step 1: 使用SURF检测器检测关键点并计算描述符
    int minHessian = 400; //设置SURF算法中的hessian阈值
    Ptr<SURF> detector = SURF::create( minHessian ); // 创建SURF检测器
    std::vector<KeyPoint> keypoints_object, keypoints_scene; // 定义储存关键点的向量
    Mat descriptors_object, descriptors_scene; //定义储存描述符的矩阵
    detector->detectAndCompute( img_object, noArray(), keypoints_object, descriptors_object ); // 在目标图片中检测关键点并计算描述符
    detector->detectAndCompute( img_scene, noArray(), keypoints_scene, descriptors_scene ); //在场景图片中检测关键点并计算描述符


    //-- Step 2: 通过FLANN匹配器对描述符向量进行匹配
    // 由于SURF是一个浮点数描述符,此处使用NORM_L2
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);
    std::vector< std::vector<DMatch> > knn_matches; //定义knn匹配的结果向量
    matcher->knnMatch( descriptors_object, descriptors_scene, knn_matches, 2 ); //knn匹配


    //-- Step 3: 利用Lowe's比率测试过滤匹配点
    const float ratio_thresh = 0.75f;
    std::vector<DMatch> good_matches; //定义储存优质匹配点的向量
    for (size_t i = 0; i < knn_matches.size(); i++)
    {
        if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance) //通过比率测试进行筛选
        {
            good_matches.push_back(knn_matches[i][0]);
        }
    }


    //-- Step 4: 绘制优质匹配点
    Mat img_matches;
    drawMatches( img_object, keypoints_object, img_scene, keypoints_scene, good_matches, img_matches, Scalar::all(-1),
                 Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );


    //-- Step 5: 找到场景图片中的目标位置
    std::vector<Point2f> obj;
    std::vector<Point2f> scene;


    for( size_t i = 0; i < good_matches.size(); i++ )
    {
        //-- 从优质匹配点中获取关键点
        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, RANSAC ); //计算单应性矩阵


    //-- Step 6: 获取目标图片的角点(the object to be "detected")
    std::vector<Point2f> obj_corners(4);
    obj_corners[0] = Point2f(0, 0);
    obj_corners[1] = Point2f( (float)img_object.cols, 0 );
    obj_corners[2] = Point2f( (float)img_object.cols, (float)img_object.rows );
    obj_corners[3] = Point2f( 0, (float)img_object.rows );
    std::vector<Point2f> scene_corners(4);


    perspectiveTransform( obj_corners, scene_corners, H); //对目标点进行透视投影变换


    //-- Draw lines between the corners (the mapped object in the scene - image_2 )
    line( img_matches, scene_corners[0] + Point2f((float)img_object.cols, 0),
          scene_corners[1] + Point2f((float)img_object.cols, 0), Scalar(0, 255, 0), 4 );
    line( img_matches, scene_corners[1] + Point2f((float)img_object.cols, 0),
          scene_corners[2] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 );
    line( img_matches, scene_corners[2] + Point2f((float)img_object.cols, 0),
          scene_corners[3] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 );
    line( img_matches, scene_corners[3] + Point2f((float)img_object.cols, 0),
          scene_corners[0] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 );


    //-- Step 7: 展示检测到的匹配
    imshow("Good Matches & Object detection", img_matches );


    waitKey(); //等待用户按键
    return 0;
}
#else
int main()
{
    std::cout << "这个教程代码需要xfeatures2d contrib模块才能运行." << std::endl;
    return 0;
}
#endif

以上代码实现了一个物体识别程序,它能从一张场景图像中找到另一张目标图像的位置。首先,通过SURF检测器找到两张图片的关键点和描述符。接着,通过FLANN匹配器找到匹配的点,并通过Lowe's比率测试进行筛选,得到优质的匹配点。最后,根据这些匹配点找到目标图片在场景图片中的位置,并在匹配图上绘制出目标的角点

411cb829841d7dbe767d4a38741aa9d9.png

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值