利用SuperGlue算法实现跨尺度金字塔特征点的高效匹配(含py代码)

news2024/11/26 23:41:43

       在计算机视觉领域,特征点匹配是一个基础而关键的任务,广泛应用于图像拼接、三维重建、目标跟踪等方向。传统的特征点匹配方法通常基于相同尺度下提取的特征进行匹配,然而在实际场景中,由于成像距离、分辨率等因素的差异,待匹配图像间存在显著的尺度变化,直接利用原始尺度的特征难以获得理想的匹配效果。为了克服这一难题,构建图像金字塔并在不同层级进行特征提取和匹配成为一种行之有效的策略。本文将给出如何使用图神经网络匹配算法SuperGlue的代码,实现跨金字塔层级的特征点高效匹配,充分利用不同尺度信息,显著提升匹配的准确性和鲁棒性。

1. 文件结构

2. 具体代码 

#! /usr/bin/env python3
import cv2
import torch   # 这一句
torch.set_grad_enabled(False) # 这一句
from models.matching import Matching # 这一句
from models.utils import (frame2tensor) # 这一句
import numpy as np

config = {
    'superpoint': {
        'nms_radius': 4,
        'keypoint_threshold': 0.005,
        'max_keypoints': -1
    },
    'superglue': {
        'weights': 'outdoor',
        'sinkhorn_iterations': 20,
        'match_threshold': 0.2,
    }
}
#
# device = 'cuda' if torch.cuda.is_available() else 'cpu'
device = 'cuda'
matching = Matching(config).eval().to(device)     # 这一句
keys = ['keypoints', 'scores', 'descriptors']
######################################################################################################
def match_frames_with_super_glue(frame0,frame1):
    print("正在调用基于 superGlue 匹配的函数进行特征点匹配...")  # 添加了print语句
    # 将参考帧和当前帧转换为PyTorch张量格式
    frame_tensor0 = frame2tensor(frame0, device)
    frame_tensor1 = frame2tensor(frame1, device)

    # 使用SuperPoint网络提取参考帧的特征点
    last_data = matching.superpoint({'image': frame_tensor0})

    # 将提取到的参考帧特征点数据转换为字典格式
    last_data = {k + '0': last_data[k] for k in keys}
    last_data['image0'] = frame_tensor0

    # 获取参考帧的特征点坐标
    kpts0 = last_data['keypoints0'][0].cpu().numpy()

    # 使用SuperGlue网络在参考帧和当前帧之间进行特征点匹配
    pred = matching({**last_data, 'image1': frame_tensor1})

    # 获取当前帧的特征点坐标
    kpts1 = pred['keypoints1'][0].cpu().numpy()

    # 获取特征点匹配结果和匹配置信度
    matches = pred['matches0'][0].cpu().numpy()
    confidence = pred['matching_scores0'][0].cpu().numpy()

    # 筛选出有效的匹配对
    valid = matches > -1
    mkpts0 = kpts0[valid]
    mkpts1 = kpts1[matches[valid]]

    # 打印匹配结果
    #
    # print(f"----已经完成帧间的关键点匹配----")
    for i, (kp0, kp1) in enumerate(zip(mkpts0, mkpts1)):
        print(f"Match {i}: ({kp0[0]:.2f}, {kp0[1]:.2f}) -> ({kp1[0]:.2f}, {kp1[1]:.2f})")

        # 确保两个图像都是三通道
    if len(frame0.shape) == 2:
        vis_frame0 = cv2.cvtColor(frame0, cv2.COLOR_GRAY2BGR)
    else:
        vis_frame0 = frame0.copy()

    if len(frame1.shape) == 2:
        vis_frame1 = cv2.cvtColor(frame1, cv2.COLOR_GRAY2BGR)
    else:
        vis_frame1 = frame1.copy()

    # 绘制第一个输入图像及其特征点
    vis_frame0_with_kpts = vis_frame0.copy()
    for kp in kpts0:
        cv2.circle(vis_frame0_with_kpts, (int(kp[0]), int(kp[1])), 3, (0, 255, 0), -1)
    cv2.imshow("Input Frame 0 with Keypoints", vis_frame0_with_kpts)

    # 绘制第二个输入图像及其特征点
    vis_frame1_with_kpts = vis_frame1.copy()
    for kp in kpts1:
        cv2.circle(vis_frame1_with_kpts, (int(kp[0]), int(kp[1])), 3, (0, 255, 0), -1)
    cv2.imshow("Input Frame 1 with Keypoints", vis_frame1_with_kpts)

        # 绘制特征点
    for kp in mkpts0:
        cv2.circle(vis_frame0, (int(kp[0]), int(kp[1])), 3, (0, 255, 0), -1)
    for kp in mkpts1:
        cv2.circle(vis_frame1, (int(kp[0]), int(kp[1])), 3, (0, 255, 0), -1)

        # 调整高度一致,通过在较短的图像上下填充黑色背景
    max_height = max(vis_frame0.shape[0], vis_frame1.shape[0])
    if vis_frame0.shape[0] < max_height:
        diff = max_height - vis_frame0.shape[0]
        pad_top = np.zeros((diff // 2, vis_frame0.shape[1], 3), dtype=np.uint8)
        pad_bottom = np.zeros((diff - diff // 2, vis_frame0.shape[1], 3), dtype=np.uint8)
        vis_frame0 = np.vstack((pad_top, vis_frame0, pad_bottom))
    if vis_frame1.shape[0] < max_height:
        diff = max_height - vis_frame1.shape[0]
        pad_top = np.zeros((diff // 2, vis_frame1.shape[1], 3), dtype=np.uint8)
        pad_bottom = np.zeros((diff - diff // 2, vis_frame1.shape[1], 3), dtype=np.uint8)
        vis_frame1 = np.vstack((pad_top, vis_frame1, pad_bottom))

        # 计算右侧图像的垂直偏移量
        right_pad_top = pad_top.shape[0]

        # 绘制匹配线段
        concat_frame = np.hstack((vis_frame0, vis_frame1))
        for kp0, kp1 in zip(mkpts0, mkpts1):
            pt0 = (int(kp0[0]), int(kp0[1]))
            pt1 = (int(kp1[0]) + vis_frame0.shape[1], int(kp1[1]) + right_pad_top)
            cv2.line(concat_frame, pt0, pt1, (0, 255, 0), 1)

        # 缩小可视化窗口大小
        scale_factor = 1
        resized_frame = cv2.resize(concat_frame, None, fx=scale_factor, fy=scale_factor)

        # 显示可视化结果
        cv2.imshow("Matched Features", resized_frame)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    return mkpts0, mkpts1, confidence[valid]

def build_pyramid(image, scale=1.2, min_size=(30, 30)):
    pyramid = [image]
    while True:
        last_image = pyramid[-1]
        width = int(last_image.shape[1] / scale)
        height = int(last_image.shape[0] / scale)
        if width < min_size[0] or height < min_size[1]:
            break
        next_image = cv2.resize(last_image, (width, height))
        pyramid.append(next_image)
    return pyramid

if __name__ == "__main__":
    # 读取两帧图像
    frame0 = cv2.imread("/home/fairlee/786D6A341753F4B4/KITTI/sequences_kitti_00_21/01/image_0/000630.png", 0)
    frame1 = cv2.imread("/home/fairlee/786D6A341753F4B4/KITTI/sequences_kitti_00_21/01/image_0/000631.png", 0)

    # 构建 frame1 的金字塔
    pyramid1 = build_pyramid(frame1, scale=1.2)

    # # # 显示金字塔层
    # for i, layer in enumerate(pyramid1):
    #     cv2.imshow(f"Layer {i}", layer)
    #     cv2.waitKey(500)  # 显示500毫秒
    # cv2.destroyAllWindows()

    # 选择合适的金字塔层作为 frame1 的替代
    frame1_substitute = pyramid1[2]  # 例如,选择第二层

    # 调用match_frames_with_super_glue函数进行特征点匹配
    mkpts0, mkpts1, confidence = match_frames_with_super_glue(frame0, frame1_substitute)

    # 打印匹配结果
    print(f"第一帧的特征点匹配到的特征点数量: {len(mkpts0)}")
    print(f"第二帧的特征点匹配到的特征点数量: {len(mkpts1)}")
    print(f"匹配置信度的长度为: {len(confidence)}")

3. 运行结果

       代码实现展示了该方法的具体流程,通过选取合适的金字塔层作为待匹配图像的替代,实现了跨尺度的特征点匹配。实验结果表明,该方法能够有效地处理存在显著尺度变化的图像,获得数量可观且置信度较高的匹配点对,为后续的图像拼接、三维重建等任务提供了重要的基础。该方法的优越性在于巧妙地结合了图像金字塔的多尺度表示和SuperGlue的强大匹配能力,为解决复杂场景下的特征匹配难题提供了新的思路和方案。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像处理ASIC设计方法 笔记29 场景自适应校正算法

P152 7.2.3 场景自适应校正算法 (一)Scribner提出的神经网络非均匀性校正算法 非均匀性校正(Non-Uniformity Correction,简称NUC)算法是红外成像技术中非常重要的一个环节。它主要用于校正红外焦平面阵列(Infrared Focal Plane Arrays,简称IRFPA)中的固定模式噪声,以提…

《python程序语言设计》2018版第5章第46题均值和标准方差-下部(本来想和大家说抱歉,但成功了)

接上回&#xff0c;5.46题如何的标准方差 本来想和大家说非常抱歉各位同学们。我没有找到通过一个循环完成两个结果的代码。 但我逐步往下的写&#xff0c;我终于成功了&#xff01;&#xff01; 这是我大前天在单位找到的公式里。x上面带一横是平均值。 我不能用函数的办法…

高考后志愿填报信息采集系统制作指南

在高考的硝烟散去之后&#xff0c;每位学生都面临着一个重要的任务——志愿填报。老师们如何高效、准确地收集和整理这些信息&#xff0c;成为了一个棘手的问题。难道我们只能依赖传统的手工登记方式&#xff0c;忍受其繁琐和易错吗&#xff1f; 易查分是一个简单易用的在线工具…

基于GTX 8B10B编码的自定义PHY上板测试(高速收发器十四)

前文整理了GTX IP&#xff0c;完成了自定义PHY协议的收发模块设计&#xff0c;本文将通过光纤回环&#xff0c;对这些模块上板测试&#xff0c;首先需要编写一个用于生成测试数据的用户模块。 1、测试数据生成模块 本模块用于生成自定义PHY协议的测试数据&#xff0c;通过axi_…

XUbuntu24.04之ch9344(usb转串口芯片)安装驱动(二百四十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

DVB-S系统发射端Matlab仿真及FPGA实现

DVB标准 Digital Video Broadcasting&#xff08;数字视频广播&#xff09;是一个完整的数字电视解决方案&#xff0c;其中包括DVB-C&#xff08;数字电视有线传输标准&#xff09;&#xff0c; DVB-T&#xff08;数字电视地面传输标准&#xff09;&#xff0c;DVB-S&#xff…

什么是突发性耳聋?

72小时内突然发生、原因不明的感音神经性听力损失&#xff0c;至少在相邻的两个频率听力下降≥20dBHL。 特点&#xff1a; 1发生在数分钟、数小时或3天以内的听力下降&#xff1b; 2原因不明&#xff1b; 3多发生于单侧&#xff0c;可伴有耳鸣、耳堵塞感及耳周麻木感&#…

C#操作MySQL从入门到精通(21)——删除数据

前言: 谈到数据库,大家最容易脱口而出的就是增删改查,本文就是来详细介绍如何删除数据。 本文测试使用的数据库如下: 1、删除部分数据 使用delete 关键字,并且搭配where条件使用,否则会导致表中数据全部被删除 string sql = string.Empty;if (radioButton_DeletePart…

vivado HW_ILA

HW_ILA 描述 集成逻辑分析器&#xff08;ILA&#xff09;调试核心允许您执行系统内监控 通过对内核上的调试探针&#xff0c;在实现的设计中对信号进行处理。您可以配置 ILA核心实时触发特定硬件事件&#xff0c;并在 以系统速度探测。 ILA调试核心可以通过从IP目录实例化ILA核…

MyBatisPlus总结二

MybatisPlus总结一在这&#xff1a; MybatisPlus总结1/2-CSDN博客 六、分页查询&#xff1a; 6.1.介绍&#xff1a; MybatisPlus内置了分页插件&#xff0c;所以我们只需要配置一个分页拦截器就可以了&#xff0c;由于不同的数据库的分页的方式不一样&#xff0c;例如mysql和…

问题汇总:MPU6050(软件iic)

以下为个人问题汇总&#xff0c;排查点汇总可能大有缺陷&#xff0c;如有错误&#xff0c;欢迎指正。 排查点汇总 检查软件iic的时序操作用示波器或逻辑分析仪检查波形 无法使用逻辑分析仪进行I/O引脚波形分析 充当SDA、SCL的引脚要配置为推挽输出; 另外&#xff0c;逻辑分…

【全开源】安心护送非急救救护车转运平台小程序(FastAdmin+ThinkPHP+Uniap

&#x1f691;安心护送非急救救护车转运平台小程序——您的健康守护者&#x1f496; 安心护送转运平台小程序是一款基于FastAdminThinkPHPUniapp开发的非急救救护车租用转运平台小程序系统&#xff0c;可以根据运营者的业务提供类似短途接送救护服务&#xff0c;重症病人转运服…

一文学会Spring 实现事务,事务的隔离级别以及事务的传播机制

目录 一.Spring (Spring Boot) 实现事务 1.通过代码的方式手动实现事务 (手动档的车) 2.通过注解的方式实现声明式事务 (自动挡的车) 二.事务的4大特性(ACID) 三.事务的隔离级别 ①Mysql的事务隔离级别: ②Spring的事务隔离级别: 四.事务的传播机制 ①事务传播机制的概…

Git配置 安装及使用

团队开发的神 找工作必备 环境变量 配置好环境后 打开终端环境 winr cmd 我习惯在桌面打开&#xff0c;然后进入相应的文件夹 &#xff08;文件夹结构&#xff09; &#xff08;个人感觉能用cmd不用git&#xff0c;cmd更好用一些&#xff09; 进入对应的文件夹 填写自己对…

Valgo,类型安全,表达能⼒强的go验证器

valgo 是一个为 Go 语言设计的类型安全、表达性强且可扩展的验证库。该库的特点包括&#xff1a; github.com/cohesivestack/valgo 类型安全&#xff1a;利用 Go 语言的泛型特性&#xff08;从 Go 1.18 版本开始支持&#xff09;&#xff0c;确保验证逻辑的类型安全。表达性&a…

Docker高级篇之Docker微服务实战

文章目录 1. 构建一个简单的微服务项目2. 编写Dockerfile发布微服务部署到docker容器 1. 构建一个简单的微服务项目 创建一个SpringBoot项目 创建一个Controller RestController public class OrderController {Value("${server.port")private String port;Reques…

深入分析 Android BroadcastReceiver (二)

文章目录 深入分析 Android BroadcastReceiver (二)1. 深入理解 BroadcastReceiver 的高级使用和优化2. 有序广播&#xff08;Ordered Broadcasts&#xff09;2.1 实现有序广播 3. 粘性广播&#xff08;Sticky Broadcasts&#xff09;3.1 使用粘性广播 4. 本地广播&#xff08;…

阿里云 MQTT 服务器搭建与测试(上传和下发数据finish)

一、 MQTT 概念 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于 TCP/IP协议上,由 IBM 在 1999 年发布。MQTT 最大优点在于,可以以极少的代码和有限的带宽,…

CNCF项目全景图介绍

本文首发在个人博客上&#xff0c;欢迎来踩&#xff01; 云原生计算基金会&#xff08;CNCF&#xff09;介绍 CNCF(Cloud Native Computing Foundation)官网链接&#xff1a;https://www.cncf.io/ 官方的介绍如下&#xff1a; 云原生技术有利于各组织在公有云、私有云和混合…

Mysql(一):深入理解Mysql索引底层数据结构与算法

众所众知&#xff0c;MySql的查询效率以及查询方式&#xff0c;基本上和索引息息相关&#xff0c;所以&#xff0c;我们一定要对MySql的索引有一个具体到数据底层上的认知。 这一次也是借着整理的机会&#xff0c;和大家一起重新复习一下MySql的索引底层。 本节也主要有一下的…