【opencv】示例-asift.cpp 对两张图片之间进行仿射特征比对

ffde010e5d8d369c5d53747b8c02cf7e.png

7306a586d8985056a8b34a7970e25b58.png

#include <opencv2/core.hpp> // 包含OpenCV核心功能的头文件
#include <opencv2/imgproc.hpp> // 包含OpenCV图像处理功能的头文件
#include <opencv2/features2d.hpp> // 包含OpenCV特征检测相关功能的头文件
#include <opencv2/highgui.hpp> // 包含OpenCV的GUI功能,如窗口显示的头文件
#include <opencv2/calib3d.hpp> // 包含OpenCV进行相机标定和三维重建功能的头文件
#include <iostream> // 包含标准输入输出流库的头文件
#include <iomanip> // 包含输入输出流格式设置的头文件


using namespace std; // 使用标准命名空间
using namespace cv; // 使用OpenCV命名空间


// 声明帮助函数,该函数会输出使用本程序的方式
static void help(char** argv)
{
    cout
    << "This is a sample usage of AffineFeature detector/extractor.\n"
    << "And this is a C++ version of samples/python/asift.py\n"
    << "Usage: " << argv[0] << "\n"
    // 以下是该程序的参数说明
    << "     [ --feature=<sift|orb|brisk> ]         # Feature to use.\n"
    << "     [ --flann ]                            # use Flann-based matcher instead of bruteforce.\n"
    << "     [ --maxlines=<number(50 as default)> ] # The maximum number of lines in visualizing the matching result.\n"
    << "     [ --image1=<image1(aero1.jpg as default)> ]\n"
    << "     [ --image2=<image2(aero3.jpg as default)> ] # Path to images to compare."
    << endl;
}


// 声明计时器函数,用于计算操作的耗时
static double timer()
{
    return getTickCount() / getTickFrequency();
}


// 程序的主函数,argc是参数数量,argv是参数列表
int main(int argc, char** argv)
{
    vector<String> fileName; // 存储文件名的字符串向量
    // 使用OpenCV的命令行解析器解析输入的命令行参数
    cv::CommandLineParser parser(argc, argv,
        "{help h ||}"
        "{feature|brisk|}"
        "{flann||}"
        "{maxlines|50|}"
        "{image1|aero1.jpg|}{image2|aero3.jpg|}");
    // 如果用户请求帮助,调用help函数并退出程序
    if (parser.has("help"))
    {
        help(argv);
        return 0;
    }
    // 从解析器中获取输入的参数
    string feature = parser.get<string>("feature");
    bool useFlann = parser.has("flann");
    int maxlines = parser.get<int>("maxlines");
    // 查找并存储输入的图像文件路径
    fileName.push_back(samples::findFile(parser.get<string>("image1")));
    fileName.push_back(samples::findFile(parser.get<string>("image2")));
    // 检查参数是否有误
    if (!parser.check())
    {
        parser.printErrors();
        cout << "See --help (or missing '=' between argument name and value?)" << endl;
        return 1;
    }


    // 读取图像,并将其转换为灰度图
    Mat img1 = imread(fileName[0], IMREAD_GRAYSCALE);
    Mat img2 = imread(fileName[1], IMREAD_GRAYSCALE);
    // 确保图像成功加载
    if (img1.empty())
    {
        cerr << "Image " << fileName[0] << " is empty or cannot be found" << endl;
        return 1;
    }
    if (img2.empty())
    {
        cerr << "Image " << fileName[1] << " is empty or cannot be found" << endl;
        return 1;
    }


    // 声明特征检测器和描述符匹配器的指针
    Ptr<Feature2D> backend;
    Ptr<DescriptorMatcher> matcher;


    // 根据用户选择初始化特征检测器和匹配器
    if (feature == "sift")
    {
        backend = SIFT::create();
        if (useFlann)
            matcher = DescriptorMatcher::create("FlannBased");
        else
            matcher = DescriptorMatcher::create("BruteForce");
    }
    else if (feature == "orb")
    {
        backend = ORB::create();
        if (useFlann)
            matcher = makePtr<FlannBasedMatcher>(makePtr<flann::LshIndexParams>(6, 12, 1));
        else
            matcher = DescriptorMatcher::create("BruteForce-Hamming");
    }
    else if (feature == "brisk")
    {
        backend = BRISK::create();
        if (useFlann)
            matcher = makePtr<FlannBasedMatcher>(makePtr<flann::LshIndexParams>(6, 12, 1));
        else
            matcher = DescriptorMatcher::create("BruteForce-Hamming");
    }
    else
    {
        cerr << feature << " is not supported. See --help" << endl;
        return 1;
    }


    // 提取特征点和描述符,并进行匹配
    cout << "extracting with " << feature << "..." << endl;
    Ptr<AffineFeature> ext = AffineFeature::create(backend);
    vector<KeyPoint> kp1, kp2;
    Mat desc1, desc2;


    ext->detectAndCompute(img1, Mat(), kp1, desc1);
    ext->detectAndCompute(img2, Mat(), kp2, desc2);
    cout << "img1 - " << kp1.size() << " features, "
         << "img2 - " << kp2.size() << " features"
         << endl;


    cout << "matching with " << (useFlann ? "flann" : "bruteforce") << "..." << endl;
    double start = timer(); // 开始计时
    // 匹配特征点,并筛选出好的匹配
    vector< vector<DMatch> > rawMatches;
    vector<Point2f> p1, p2;
    vector<float> distances;
    matcher->knnMatch(desc1, desc2, rawMatches, 2);
    // 筛选出好的匹配点
    for (size_t i = 0; i < rawMatches.size(); i++)
    {
        const vector<DMatch>& m = rawMatches[i];
        if (m.size() == 2 && m[0].distance < m[1].distance * 0.75)
        {
            p1.push_back(kp1[m[0].queryIdx].pt);
            p2.push_back(kp2[m[0].trainIdx].pt);
            distances.push_back(m[0].distance);
        }
    }
    // 利用单应性计算匹配点对的状态
    vector<uchar> status; // 创建一个uchar类型的向量status,用来存储每对匹配点是否是内点的状态
    vector< pair<Point2f, Point2f> > pointPairs; // 创建一个存储匹配点对(两个图像中匹配的点)的vector
    Mat H = findHomography(p1, p2, status, RANSAC); // 利用RANSAC算法计算从图像1到图像2的单应性矩阵H
    int inliers = 0; // 初始化内点数量计数器
    // 遍历status向量,统计内点数量并存储这些点对
    for (size_t i = 0; i < status.size(); i++)
    {
        // 如果status向量中的元素为true,则表示该匹配点对是内点
        if (status[i])
        {
            pointPairs.push_back(make_pair(p1[i], p2[i])); // 将内点对添加到pointPairs向量中
            distances[inliers] = distances[i]; // 将对应内点的距离存储到distances向量中
            // CV_Assert(inliers <= (int)i); // 断言inliers的值应小于等于当前索引,通常用于调试
            inliers++; // 内点数量加一
        }
    }
    distances.resize(inliers); // 重新调整distances向量的大小以匹配内点的数量
    
    // 输出执行时间
    cout << "execution time: " << fixed << setprecision(2) << (timer()-start)*1000 << " ms" << endl;
    // 输出内点与匹配点对的比例
    cout << inliers << " / " << status.size() << " inliers/matched" << endl;
    
    // 可视化匹配结果前的准备工作
    cout << "visualizing..." << endl;
    vector<int> indices(inliers); // 创建一个大小等于内点数量的整数型向量indices,用于存储排序后的索引
    // 将distances向量中元素的索引按照距离从小到大排序并存入indices向量
    cv::sortIdx(distances, indices, SORT_EVERY_ROW+SORT_ASCENDING);
    // 创建可视化图像并绘制匹配的特征点
    int h1 = img1.size().height;
    int w1 = img1.size().width;
    int h2 = img2.size().height;
    int w2 = img2.size().width;
    Mat vis = Mat::zeros(max(h1, h2), w1+w2, CV_8U);
    img1.copyTo(Mat(vis, Rect(0, 0, w1, h1)));
    img2.copyTo(Mat(vis, Rect(w1, 0, w2, h2)));
    cvtColor(vis, vis, COLOR_GRAY2BGR);


    vector<Point2f> corners(4);
    corners[0] = Point2f(0, 0);
    corners[1] = Point2f((float)w1, 0);
    corners[2] = Point2f((float)w1, (float)h1);
    corners[3] = Point2f(0, (float)h1);
    vector<Point2i> icorners;
    perspectiveTransform(corners, corners, H); // 对图像1的四个角进行单应性变换
    transform(corners, corners, Matx23f(1,0,(float)w1,0,1,0)); // 将变换后的角点移到图像2的右侧
    Mat(corners).convertTo(icorners, CV_32S); // 将角落点的类型转化为整数
    polylines(vis, icorners, true, Scalar(255,255,255)); // 在可视化图像中绘制边界线


    // 绘制前maxlines个的匹配对
    for (int i = 0; i < min(inliers, maxlines); i++)
    {
        int idx = indices[i];
        const Point2f& pi1 = pointPairs[idx].first;
        const Point2f& pi2 = pointPairs[idx].second;
        circle(vis, pi1, 2, Scalar(0,255,0), -1); // 绘制圆点
        circle(vis, pi2 + Point2f((float)w1,0), 2, Scalar(0,255,0), -1); // 在图像2相应的位置绘制圆点
        line(vis, pi1, pi2 + Point2f((float)w1,0), Scalar(0,255,0)); // 绘制连线
    }
    if (inliers > maxlines)
        cout << "only " << maxlines << " inliers are visualized" << endl;
    imshow("affine find_obj", vis); // 显示最终的可视化结果窗口


    // 当存在更多的匹配时,输出提示信息
    // Mat vis2 = Mat::zeros(max(h1, h2), w1+w2, CV_8U); // 创建另一个可视化用的空白图像
    // Mat warp1; // 存储变换后图像的矩阵
    // warpPerspective(img1, warp1, H, Size(w1, h1)); // 对图像1应用单应性变换
    // warp1.copyTo(Mat(vis2, Rect(0, 0, w1, h1))); // 将变换后的图像1复制到可视化图像的左半边
    // img2.copyTo(Mat(vis2, Rect(w1, 0, w2, h2))); // 将图像2复制到可视化图像的右半边
    // imshow("warped", vis2); // 显示变换后图像与图像2的对比窗口


    waitKey(); // 等待任意键按下
    cout << "done" << endl; // 输出完成提示
    return 0; // 程序结束
}

此段C++代码的主要功能是载入两张图像,通过OpenCV库进行特征点检测和匹配,然后通过单应性变换计算两图像之间的匹配关系,并将匹配结果可视化显示出来。用户可以指定不同的特征检测算法(如SIFT、ORB、BRISK等)以及是否使用FLANN库进行近似最邻近搜索而不是暴力匹配。代码结束时还会显示执行时间和匹配对的数量。

终端输出:

img1 - 39607 features, img2 - 24674 features
matching with bruteforce...
execution time: 35513.65 ms
41 / 105 inliers/matched
visualizing...

Ptr<AffineFeature> ext = AffineFeature::create(backend);

4ca2e08198e7ea9eee447b87043f3a19.png

Mat H = findHomography(p1, p2, status, RANSAC);

685a67c444636176a512ab30f8cc05bb.png

2b0f108afb23babef5cea0de1a4e7823.png

仿射特征匹配有哪些应用场景

17122b21c5fca6eac63ed99b0d01660c.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/522429.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在 Amazon Timestream 上通过时序数据机器学习进行预测分析

由于不断变化的需求和现代化基础设施的动态性质&#xff0c;为大型应用程序规划容量可能会非常困难。例如&#xff0c;传统的反应式方法依赖于某些 DevOps 指标&#xff08;如 CPU 和内存&#xff09;的静态阈值&#xff0c;而这些指标在这样的环境中并不足以解决问题。在这篇文…

5G随身wifi真实测评!飞猫5g随身wifi怎么样?飞猫5GVS格行5G随身wifi哪款网速快?5G随身wifi推荐品牌第一名!

飞猫5G随身wifi&#xff1a; 产品外观&#xff1a;黑色大气外观&#xff0c;净重175g&#xff0c;屏幕有信号和指示灯。 产品性能&#xff1a;采用展锐芯片。6根LDS天线&#xff0c;网速100-200mbps&#xff0c;网络延迟10-20ms&#xff0c;2.4G/5G双频可选&#xff0c;超稳定…

重生奇迹mu剑士连招技巧

剑士是攻守兼备的近战之王&#xff0c;拥有着娴熟的剑术与强大的力量。 剑士的攻击能力和防御能力都比较强&#xff0c;自身的血量也比较高。他不仅可以双持单手武器&#xff0c;达到双剑流或者剑盾流的水平&#xff0c;同时&#xff0c;剑士也可以使用强大的双手武器&#xf…

Python最简单的图片爬虫

Python最简单的图片爬虫&#xff0c;20行代码带你爬遍整个网站-腾讯云开发者社区-腾讯云 (tencent.com) import urllib.parse import json import requests import jsonpath url https://www.duitang.com/napi/blog/list/by_search/?kw{}&start{} label 美女 label url…

k8s CNI Calico 网络模式总结【建议收藏】

目录 calico架构图 ​编辑 IPIP模式下的架构图 calico 核心组件 Overlay 网络模式&#xff1a; Vxlan IPIP IpCrossubnet Pod IP对外暴露 不对外暴露&#xff1a; 实现对外暴露的方法&#xff1a; overlay模式下的网络MTU Iptables & ipvs Full-mesh o…

LED发光模组的故障及解决方法

LED发光模组在应用过程中可能会出现各种故障&#xff0c;正确诊断并采取相应的解决方法至关重要&#xff0c;以下是一些常见故障现象及其解决方法的总结&#xff1a; 一、现象&#xff1a;所有的LED闪烁 问题&#xff1a;接触不良 解决方法&#xff1a;检查并重新固定松动处&am…

【洛谷】P9236 [蓝桥杯 2023 省 A] 异或和之和

题目链接 P9236 [蓝桥杯 2023 省 A] 异或和之和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路 1. 暴力求解 直接枚举出所有子数组&#xff0c;求每个子数组的异或和&#xff0c;再对所有的异或和求和 枚举所有子数组的时间复杂度为O&#xff08;N^2&#xff09;&…

CTF之矛盾

这一题就是php的弱比较“” 这里要求输入的不是数字&#xff0c;并且输入要为1才打印flag 那我们就输入一个1后面接随便什么字符&#xff0c;因为php的弱比较将字符与数字进行比较的时候&#xff0c;会把字符转换成数字再比较&#xff0c;当转换到字符时后面便都为空了 flag{…

BUUCTF---新年快乐(reverse)

1.题目描述&#xff1a;下载一个压缩包&#xff0c;里面有个exe文件 2.用PE查壳&#xff08;有个32位的UPX壳&#xff09; 3.脱壳&#xff0c;使用工具万能脱壳器&#xff0c;下载地址&#xff1a;https://www.onlinedown.net/soft/634046.htm 4.脱壳完后&#xff0c;生成新的e…

感染了后缀为.jayy勒索病毒如何应对?数据能够恢复吗?

导言&#xff1a; 在当今数字化的世界中&#xff0c;网络安全已经成为了每个人都需要关注的重要议题。而勒索病毒作为网络安全领域中的一大威胁&#xff0c;不断地演变和升级&#xff0c;给个人和组织带来了严重的损失和困扰。近期&#xff0c;一种名为.jayy的勒索病毒引起了广…

含风电-光伏-光热电站电力系统N-k安全优化调度模型

目录 1 主要内容 2 部分程序 3 部分结果 4 下载链接 1 主要内容 该程序参考《光热电站促进风电消纳的电力系统优化调度》光热电站模型&#xff0c;主要做的是考虑N-k安全约束的含义风电-光伏-光热电站的电力系统优化调度模型&#xff0c;从而体现光热电站在调度灵活性以及经…

电池二次利用走向可持续大循环周期的潜力和挑战(第一篇)

一、背景 当前&#xff0c;气候变化是全球可持续发展面临的重大挑战。缓解气候变化最具挑战性的目标是在本世纪中期实现碳中和&#xff08;排放量低到足以被自然系统安全吸收&#xff09;&#xff0c;其中电动汽车&#xff08;EV&#xff09;的引入是一项关键举措。电动汽车在…

cesium entity默认的点击事件

一、单击事件 点击entity&#xff0c;屏幕出现一个绿色的框&#xff0c;不想显示这个绿色框有两个办法 1、在创建viewer的时候&#xff0c;设置selectionIndicator为false // 初始化地图容器viewer new Cesium.Viewer(cesiumContainer, {contextOptions: {webgl: {alpha: tru…

LED点阵屏与LCD1602

目录 LED点阵屏 点阵屏的介绍 LED点阵屏分类 点阵屏的显示原理 点阵案例 静态案例 电路图 keil文件 动态案例 电路图 keil文件 LCD1602 LCD1602概述 LCD1602内部结构 存储器结构 LCD引脚及应用电路 时序结构 LCD1602指令集 LCD1602编程 初始化 显示字符 …

前端三剑客 —— CSS (第五节)

目录 内容回顾&#xff1a; 特殊样式 特殊样式 CSS变量 常见函数 倒影效果 页面布局 Table 布局&#xff08;了解即可&#xff09; DIVCSS布局 弹性布局 1&#xff09;不使用弹性布局&#xff0c;而是使用DIVCSS 2&#xff09;使用弹性布局实现导航菜单 内容回顾…

中非绿色能源合作走深走实

近日&#xff0c;第十六届非洲能源大会在南非立法首都开普敦举行&#xff0c;探讨实现非洲能源转型的可持续解决方案。近年来&#xff0c;中国与非洲国家不断加强绿色能源合作&#xff0c;促进双方优势资源互补&#xff0c;逐步探索合作共赢的绿色能源合作方案。 势头良好 近年…

SystemC入门学习Demo用例的工程化配置

背景&#xff1a;对不同的用例文件&#xff0c;使用CMakeLists.txt进行工程化管理的演示&#xff0c;这样开发者可以更加关注在代码开发上。 1&#xff0c;首先安装好系统环境的systemC库&#xff1a;ubuntu系统安装systemc-2.3.4流程-CSDN博客 2&#xff0c;准备好一个demo用…

开源免费的MySQL和MariaDB图形化管理软件

2024年4月7日&#xff0c;周日凌晨 有很多开源免费的MySQL和MariaDB图形化管理界面可供选择。 以下是一些常用的工具&#xff1a; phpMyAdmin&#xff1a;phpMyAdmin 是一个用 PHP 编写的免费开源的 MySQL 和 MariaDB 管理工具&#xff0c;它提供了一个基于 Web 的界面&#…

PCF8591(ADDA转换芯片)

工具 1.Proteus 8 仿真器 2.keil 5 编辑器 原理图 讲解 PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591具有4个模拟输入、1个模拟输出和1个串行IC总线接口。PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程&#xff0c;允许在同个I2C总线上接…