c++学习【4】提取特征

读取图像 

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

using namespace std;
using namespace cv;

int main()
{

    //-- 读取图像
    Mat img_1 = imread("D:/opencv_c++/source/repos/Project1/3.jpg", IMREAD_COLOR);//"CV_LOAD_IMAGE_COLOR" 为旧版本中的宏定义名
    Mat img_2 = imread("D:/opencv_c++/source/repos/Project1/4.jpg", IMREAD_COLOR);

    resize(img_1, img_1, Size(), 0.4, 0.4);
    resize(img_2, img_2, Size(), 0.4, 0.4);

    imshow("img_1", img_1);
    imshow("img_2", img_2);
    waitKey(0);

    return 0;
}

KeyPoint 是 OpenCV 中用于表示图像中关键点的结构体,包含关键点的坐标、响应值、方向等属性。

std::vector<cv::KeyPoint> keypoints_1, keypoints_2;
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
    // 初始化 KeyPoint 变量
    cv::KeyPoint kp(10, 20, 1.5, 45, 100);

    // 输出 KeyPoint 的属性
    cout << "x: " << kp.pt.x << endl;
    cout << "y: " << kp.pt.y << endl;
    cout << "size: " << kp.size << endl;
    cout << "angle: " << kp.angle << endl;
    cout << "response: " << kp.response << endl;

    return 0;
}

x: 10
y: 20
size: 1.5
angle: 45
response: 100

Ptr 是一个智能指针类,可以指向任何 OpenCV 中的对象,并自动管理其内存。使用 Ptr 类型的智能指针,可以避免手动释放内存的繁琐操作,提高代码的可读性和可维护性。

在C++中,Ptr是OpenCV库中的一个智能指针类(smart pointer),用于管理动态分配的对象。Ptr<>模板类的尖括号中可以传入任何类型的指针,如int、float、char、double等基本数据类型的指针,也可以是OpenCV库中的对象的指针,如Mat、CascadeClassifier等。

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

using namespace std;
using namespace cv;

int main()
{
    Ptr<FeatureDetector> detector = ORB::create();
    Ptr<DescriptorExtractor> descriptor = ORB::create();
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
    // 获取指针所指向的对象的内存大小
    size_t size1 = sizeof(*detector);
    std::cout << "Memory size of FeatureDetector object: " << size1 << " bytes" << std::endl;
    size_t size2 = sizeof(*descriptor);
    std::cout << "Memory size of DescriptorExtractor object: " << size2 << " bytes" << std::endl;
    size_t size3 = sizeof(*matcher);
    std::cout << "Memory size of DescriptorMatcher object: " << size3 << " bytes" << std::endl;

    return 0;
}


Memory size of FeatureDetector object: 32 bytes
Memory size of DescriptorExtractor object: 32 bytes
Memory size of DescriptorMatcher object: 72 bytes

     //-- 第一步:检测 Oriented FAST 角点位置


    /*
    * 在C++中,箭头符号(->)用于访问类的成员函数和成员变量。
    * 使用detector对象调用detect()方法。
    * 使用OpenCV库中的特征检测器(detector)来检测图像(img_1)中的特征点,
    * 并将检测到的特征点存储在一个向量(keypoints_1)中
    */

    detector->detect(img_1, keypoints_1);
    detector->detect(img_2, keypoints_2);

这是OpenCV库中DescriptorExtractor类中的一个成员函数compute()的声明。这个函数是用来计算图像中的特征描述符的,具体来说,它会计算image输入图像中由keypoints向量指定的关键点的特征描述符,并将结果存储在descriptors输出数组中。

其中,函数的第一个参数image是输入图像,类型为InputArray,可以是Mat、UMat或其他支持的图像类型。第二个参数keypoints是输入和输出参数,类型为std::vector<KeyPoint>&,表示存储了关键点信息的向量。在函数调用前,向量中存储了待计算特征描述符的关键点信息;在函数调用后,向量中的每个元素都被更新为对应关键点的特征描述符信息。

第三个参数descriptors是输出参数,类型为OutputArray,表示用于存储特征描述符的数组。它可以是Mat、UMat或其他支持的数组类型。在函数调用后,descriptors数组中存储了与keypoints向量中每个关键点对应的特征描述符信息。

需要注意的是,compute()函数是一个虚函数,并且带有CV_WRAP修饰符,表示这个函数可以在Python等其他语言中使用。

    //-- 第二步:根据角点位置计算 BRIEF 描述子


    /*
    * 
    */

    descriptor->compute(img_1, keypoints_1, descriptors_1);
    descriptor->compute(img_2, keypoints_2, descriptors_2);

    Mat outimg1;
    drawKeypoints(img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
    imshow("ORB特征点", outimg1);

    //-- 第三步:对两幅图像中的BRIEF描述子进行匹配,

使用 Hamming 距离

matchcv::DescriptorMatcher 中的一个成员函数,而matcher 是实例化的 cv::DescriptorMatcher 对象

调用 cv::DescriptorMatchermatch 成员函数,传入 descriptors_1descriptors_2 两组特征描述子,将匹配结果存储在 matches 中。

    vector<DMatch> matches;
    BFMatcher matcher ( NORM_HAMMING );
    matcher->match(descriptors_1, descriptors_2, matches);

BFMatcher 是 OpenCV 中的一个类,用于计算两个特征描述子之间的距离,并进行暴力匹配。

Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming")BFMatcher matcher(NORM_HAMMING) 都表示创建一个使用汉明距离作为距离度量方式的暴力匹配器对象。它们的功能相同,只是创建方式不同。 

matches 是一个向量(std::vector)类型的对象,用于存储两组特征描述子进行匹配的结果。其中,matches 中的每一个元素都是一个 cv::DMatch 类型的对象,表示一对匹配的特征点。每个 cv::DMatch 对象包含以下信息:

  • queryIdx:表示第一组特征描述子中参与匹配的特征点的索引。
  • trainIdx:表示第二组特征描述子中参与匹配的特征点的索引。
  • distance:表示这一对特征点的匹配距离。

    //找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离 

    double min_dist = 10000, max_dist = 0;
    for (int i = 0; i < descriptors_1.rows; i++)
    {
        double dist = matches[i].distance;
        if (dist < min_dist) min_dist = dist;
        if (dist > max_dist) max_dist = dist;
    }

    //当描述子不符合条件时删除

之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.

push_back()函数,将一个新的元素加到一个vector类型数据的最后面

在CV中,cv::DMatch 是一个描述匹配特征点之间距离和匹配度的结构体。

    std::vector< DMatch > good_matches;
    for (int i = 0; i < descriptors_1.rows; i++)
    {
        if (matches[i].distance <= max(2 * min_dist, 30.0))
        {
            good_matches.push_back(matches[i]);
        }
    }

    //-- 第五步:绘制匹配结果

    Mat img_match;
    Mat img_goodmatch;
    drawMatches(img_1, keypoints_1, img_2, keypoints_2, matches, img_match);
    drawMatches(img_1, keypoints_1, img_2, keypoints_2, good_matches, img_goodmatch);
    imshow("所有匹配点对", img_match);
    imshow("优化后匹配点对", img_goodmatch);

完整代码

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

using namespace std;
using namespace cv;

int main()
{

    //-- 读取图像
    cv::Mat img_1 = imread("D:/opencv_c++/source/repos/Project1/3.jpg", cv::IMREAD_COLOR);//"CV_LOAD_IMAGE_COLOR" 为旧版本中的宏定义名
    cv::Mat img_2 = imread("D:/opencv_c++/source/repos/Project1/4.jpg", cv::IMREAD_COLOR);

    resize(img_1, img_1, cv::Size(), 0.4, 0.4);
    resize(img_2, img_2, cv::Size(), 0.4, 0.4);

    //-- 初始化
    /*
    * 这是一个 C++ 中使用 OpenCV 库时定义两个名为 keypoints_1 和 keypoints_2 的向量(vector),
    * 其存储的元素类型为 KeyPoint。
    * KeyPoint 是 OpenCV 中用于表示图像中关键点的结构体,包含关键点的坐标、响应值、方向等属性。
    * 这段代码的作用是定义了两个存储关键点的向量,可以用来存储从图像中提取出的关键点信息。
    */
    std::vector<cv::KeyPoint> keypoints_1, keypoints_2;
    /*
    * 定义两个 Mat 对象 descriptors_1 和 descriptors_2,用于存储特征点的描述符
    * 但是并没有指定它们的大小。
    * 如果有 N 个特征点,每个特征点的描述符是一个 D 维向量,那么可以定义一个大小为 N×D 的 Mat 对象来存储
    */
    cv::Mat descriptors_1, descriptors_2;
    /*
    * Ptr<FeatureDetector> 是一个模板类,用于创建一个指向 FeatureDetector 类型对象的智能指针。
    * 这个智能指针可以自动管理其所指向对象的内存,避免了手动释放内存的繁琐操作。
    * 在 C++ 中,<> 表示模板参数列表,
    * Ptr 是一个智能指针类,可以指向任何 OpenCV 中的对象,并自动管理其内存。
    * 使用 Ptr 类型的智能指针,可以避免手动释放内存的繁琐操作,提高代码的可读性和可维护性。
    * 在 OpenCV 中,很多对象都是通过 Ptr 类型的智能指针来管理的,
    * 例如 Mat、FeatureDetector、DescriptorExtractor 等等。
    */
    Ptr<FeatureDetector> detector = ORB::create();
    Ptr<DescriptorExtractor> descriptor = ORB::create();
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");

    //-- 第一步:检测 Oriented FAST 角点位置
    /*
    * 在C++中,箭头符号(->)用于访问类的成员函数和成员变量。
    * 使用detector对象调用detect()方法。
    * 使用OpenCV库中的特征检测器(detector)来检测图像(img_1)中的特征点,
    * 并将检测到的特征点存储在一个向量(keypoints_1)中
    */
    detector->detect(img_1, keypoints_1);
    detector->detect(img_2, keypoints_2);
    //-- 第二步:根据角点位置计算 BRIEF 描述子
    /*
    * 
    */
    descriptor->compute(img_1, keypoints_1, descriptors_1);
    descriptor->compute(img_2, keypoints_2, descriptors_2);

    //Mat outimg1;
    //drawKeypoints(img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
    //imshow("ORB特征点", outimg1);

    //-- 第三步:对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
    vector<DMatch> matches;
    //BFMatcher matcher ( NORM_HAMMING );
    matcher->match(descriptors_1, descriptors_2, matches);

    double min_dist = 10000, max_dist = 0;
    //找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离
    for (int i = 0; i < descriptors_1.rows; i++)
    {
        double dist = matches[i].distance;
        if (dist < min_dist) min_dist = dist;
        if (dist > max_dist) max_dist = dist;
    }
     仅供娱乐的写法
    //min_dist = min_element(matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance < m2.distance; })->distance;
    //max_dist = max_element(matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance < m2.distance; })->distance;

    printf("-- Max dist : %f \n", max_dist);
    printf("-- Min dist : %f \n", min_dist);

    //当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
    std::vector< DMatch > good_matches;
    for (int i = 0; i < descriptors_1.rows; i++)
    {
        if (matches[i].distance <= max(2 * min_dist, 30.0))
        {
            good_matches.push_back(matches[i]);
        }
    }

    //-- 第五步:绘制匹配结果
    Mat img_match;
    Mat img_goodmatch;
    drawMatches(img_1, keypoints_1, img_2, keypoints_2, matches, img_match);
    drawMatches(img_1, keypoints_1, img_2, keypoints_2, good_matches, img_goodmatch);
    imshow("所有匹配点对", img_match);
    imshow("优化后匹配点对", img_goodmatch);
    waitKey(0);

    //imshow("img_1", img_1);
    //imshow("img_2", img_2);
    waitKey(0);



    return 0;
}

封装

void find_feature_matches(const Mat &img_1, const Mat &img_2,
                          std::vector<KeyPoint> &keypoints_1,
                          std::vector<KeyPoint> &keypoints_2,
                          std::vector<DMatch> &matches) {
  //-- 初始化
  Mat descriptors_1, descriptors_2;
  // used in OpenCV3
  Ptr<FeatureDetector> detector = ORB::create();
  Ptr<DescriptorExtractor> descriptor = ORB::create();
  // use this if you are in OpenCV2
  // Ptr<FeatureDetector> detector = FeatureDetector::create ( "ORB" );
  // Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create ( "ORB" );
  Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
  //-- 第一步:检测 Oriented FAST 角点位置
  detector->detect(img_1, keypoints_1);
  detector->detect(img_2, keypoints_2);

  //-- 第二步:根据角点位置计算 BRIEF 描述子
  descriptor->compute(img_1, keypoints_1, descriptors_1);
  descriptor->compute(img_2, keypoints_2, descriptors_2);

  //-- 第三步:对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
  vector<DMatch> match;
  // BFMatcher matcher ( NORM_HAMMING );
  matcher->match(descriptors_1, descriptors_2, match);

  //-- 第四步:匹配点对筛选
  double min_dist = 10000, max_dist = 0;

  //找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离
  for (int i = 0; i < descriptors_1.rows; i++) {
    double dist = match[i].distance;
    if (dist < min_dist) min_dist = dist;
    if (dist > max_dist) max_dist = dist;
  }

  printf("-- Max dist : %f \n", max_dist);
  printf("-- Min dist : %f \n", min_dist);

  //当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
  for (int i = 0; i < descriptors_1.rows; i++) {
    if (match[i].distance <= max(2 * min_dist, 30.0)) {
      matches.push_back(match[i]);
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值