• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
MKT-porter
博客园    首页    新随笔    联系   管理    订阅  订阅

MASt3R (2)安装和测试

模型下载位置

image

 

 

 

image

 

 

例子1 单独的匹配

image

 

image

 

from mast3r.model import AsymmetricMASt3R
from mast3r.fast_nn import fast_reciprocal_NNs
import mast3r.utils.path_to_dust3r
from dust3r.inference import inference
from dust3r.utils.image import load_images
import cv2
import numpy as np
import torch
import time

class FeatureMatcher_Mast3r:
    def __init__(self, model_path, device='cuda'):
        """
        初始化特征匹配器
        Args:
            model_path: MASt3R模型路径
            device: 运行设备
        """
        self.device = device
        self.model = self.load_mast3r_model(model_path)
    
    def load_mast3r_model(self, model_path):
        """加载MASt3R模型"""
        print("开始加载MASt3R模型")
        model = AsymmetricMASt3R.from_pretrained(model_path).to(self.device)
        print("MASt3R模型加载完成")
        return model
    
    def load_and_preprocess_images(self, image_paths, size=512):
        """
        加载并预处理图像
        Args:
            image_paths: 图像路径列表
            size: 图像尺寸
        Returns:
           预处理后的图像数据
        """
        images = load_images(image_paths, size=size)
        
        # 图像标准化
        image_mean = torch.as_tensor([0.5, 0.5, 0.5], device='cpu').reshape(1, 3, 1, 1)
        image_std = torch.as_tensor([0.5, 0.5, 0.5], device='cpu').reshape(1, 3, 1, 1)
        
        viz_imgs = []
        for i, view in enumerate([images[0], images[1]]):
            rgb_tensor = view['img'] * image_std + image_mean
            viz_img = rgb_tensor.squeeze(0).permute(1, 2, 0).cpu().numpy()
            viz_imgs.append(viz_img)
        
        # 转换为OpenCV格式
        img0_bgr = (viz_imgs[0][:, :, ::-1] * 255).astype(np.uint8)
        img1_bgr = (viz_imgs[1][:, :, ::-1] * 255).astype(np.uint8)
        
        return images, img0_bgr, img1_bgr

    def mast3r_feature_matching(self, images):
        """
        MASt3R特征匹配
        Args:
            images: 输入图像
        Returns:
           匹配结果、关键点坐标和耗时
        """
        start_time = time.time()
        
        # 执行推理
        output = inference([tuple(images)], self.model, self.device, batch_size=1, verbose=False)
        
        view1, pred1 = output['view1'], output['pred1']
        view2, pred2 = output['view2'], output['pred2']
        desc1, desc2 = pred1['desc'].squeeze(0).detach(), pred2['desc'].squeeze(0).detach()

        # 快速互最近邻匹配
        matches_im0, matches_im1 = fast_reciprocal_NNs(desc1, desc2, subsample_or_initxy1=8,
                                                       device=self.device, dist='dot', block_size=2**13)
        
        # 有效点筛选
        H0, W0 = view1['true_shape'][0]
        valid_matches_im0 = (matches_im0[:, 0] >= 3) & (matches_im0[:, 0] < int(W0) - 3) & (
            matches_im0[:, 1] >= 3) & (matches_im0[:, 1] < int(H0) - 3)

        H1, W1 = view2['true_shape'][0]
        valid_matches_im1 = (matches_im1[:, 0] >= 3) & (matches_im1[:, 0] < int(W1) - 3) & (
            matches_im1[:, 1] >= 3) & (matches_im1[:, 1] < int(H1) - 3)

        valid_matches = valid_matches_im0 & valid_matches_im1
        matches_im0, matches_im1 = matches_im0[valid_matches], matches_im1[valid_matches]
        
        end_time = time.time()
        total_time = end_time - start_time
        
        return matches_im0, matches_im1, total_time

    def estimate_pose(self, src_pts, dst_pts, method='homography'):
        """
        估计位姿变换(旋转矩阵R和平移向量t)
        Args:
            src_pts: 源点
            dst_pts: 目标点
            method: 估计方法
        Returns:
            R, t, 内点数量
        """
        if len(src_pts) < 4:
            print("匹配点数量不足,无法计算位姿")
            return None, None, 0
        
        if method == 'homography':
            # 使用单应性矩阵估计
            H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
            if H is not None:
                num_inliers = np.sum(mask)
                return H, None, num_inliers
            else:
                return None, None, 0
                
        elif method == 'essential':
            # 使用本质矩阵估计(需要相机内参)
            camera_matrix = np.array([[1000, 0, 512],
                                      [0, 1000, 512],
                                      [0, 0, 1]])
            
            E, mask = cv2.findEssentialMat(src_pts, dst_pts, camera_matrix, cv2.RANSAC, 0.999, 1.0)
            if E is not None:
                points, R, t, mask = cv2.recoverPose(E, src_pts, dst_pts, camera_matrix)
                num_inliers = np.sum(mask)
                return R, t, num_inliers
            else:
                return None, None, 0
        
        return None, None, 0

    def visualize_results(self, mast3r_matches_im0, mast3r_matches_im1, img0_bgr, img1_bgr, n_viz=100):
        """
        可视化MASt3R匹配结果
        """
        h0, w0 = img0_bgr.shape[:2]
        h1, w1 = img1_bgr.shape[:2]
        max_height = max(h0, h1)
        
        # 创建结果图像
        result_img = np.zeros((max_height, w0 + w1, 3), dtype=np.uint8)
        
        # 放置原始图像
        result_img[:h0, :w0] = img0_bgr
        result_img[:h1, w0:w0+w1] = img1_bgr
        
        # 绘制MASt3R匹配点
        colors = [
            (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255)
        ]
        
        n_viz_actual = min(n_viz, mast3r_matches_im0.shape[0])
        indices = np.round(np.linspace(0, mast3r_matches_im0.shape[0] - 1, n_viz_actual)).astype(int)
        
        for i, idx in enumerate(indices):
            color = colors[i % len(colors)]
            pt1 = mast3r_matches_im0[idx]
            pt2 = mast3r_matches_im1[idx]
            
            x1, y1 = int(pt1[0]), int(pt1[1])
            x2, y2 = int(pt2[0]) + w0, int(pt2[1])
            
            cv2.line(result_img, (x1, y1), (x2, y2), color, 1, cv2.LINE_AA)
            cv2.circle(result_img, (x1, y1), 3, color, -1)
            cv2.circle(result_img, (x2, y2), 3, color, -1)
        
        # 添加标签
        cv2.putText(result_img, "MASt3R Feature Matching", (10, 30), 
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
        
        return result_img

def main():
    """主函数"""
    # 初始化参数
    model_path = "checkpoints/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric.pth"
    image_paths = [
        '/media/r9000k/DD_XS/2数据/2_1精灵4无rtk/2圆楼_滨湖_白天_原图/DJI_0516.JPG',
        '/media/r9000k/DD_XS/2数据/2_1精灵4无rtk/v4_2圆楼_滨湖_拍照模式测曝光/DJI_0146.JPG'
    ]
    
    # 创建特征匹配器实例
    matcher_Mast3r = FeatureMatcher_Mast3r(model_path)
    
    # 加载图像
    images, img0_bgr, img1_bgr = matcher_Mast3r.load_and_preprocess_images(image_paths)
    
    # MASt3R特征匹配
    mast3r_pts1, mast3r_pts2, mast3r_time = matcher_Mast3r.mast3r_feature_matching(images)
    
    # 位姿估计
    print("\n=== MASt3R位姿估计结果 ===")
    R_mast3r, t_mast3r, mast3r_inliers = matcher_Mast3r.estimate_pose(mast3r_pts1, mast3r_pts2)
    print(f"MASt3R内点数量: {mast3r_inliers}")
    print(f"MASt3R算法耗时: {mast3r_time:.4f} 秒")
    print(f"匹配点数量: {mast3r_pts1.shape[0]}")
    
    # 可视化结果
    result_img = matcher_Mast3r.visualize_results(mast3r_pts1, mast3r_pts2, img0_bgr, img1_bgr, n_viz=20)
    
    # 显示结果
    cv2.namedWindow('MASt3R Feature Matching Results', cv2.WINDOW_NORMAL)
    cv2.imshow('MASt3R Feature Matching Results', result_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

  

 

 

image

 

 

 

 

效果对比

 

image

 

image

 

 

原始版本

https://github.com/naver/mast3r

 

加速版本  必须cuda12.1 装不上

peedy MASt3R 在不影响准确率的情况下,将推理时间缩短了 54%(每对图像从 198 毫秒缩短至 91 毫秒)。这一改进实现实时 3D 理解,助力MR导航和大规模 3D 场景重建等应用。

 https://github.com/ASU-ESIC-FAN-Lab/speedy_mast3r
 
  1. 克隆 MASt3R。
git clone --recursive https://github.com/naver/mast3r
cd mast3r
git submodule update --init --recursive
  1. 创建环境,这里我们以 conda 为例进行说明。 注意适配自己的环境
conda create -n mast3r python=3.11 cmake=3.14.0
conda activate mast3r 
conda install pytorch=2.7 torchvision pytorch-cuda=11.8 -c pytorch -c nvidia  # use the correct version of cuda for your system
pip install -r requirements.txt
pip install -r dust3r/requirements.txt
# Optional: you can also install additional packages to:
# - add support for HEIC images
# - add required packages for visloc.py
pip install -r dust3r/requirements_optional.txt

  

  1. 编译并安装 ASMK
pip install cython

git clone https://github.com/jenicek/asmk
cd asmk/cython/
cythonize *.pyx
cd ..
pip install backports.tarfile
conda update setuptools pip backports
conda install -c conda-forge libgcc-ng libstdcxx-ng

sudo apt-get update
sudo apt-get install libc6-dev

python3 setup.py build_ext --inplace
或者
pip install . cd ..

  

1报错libpthread找不到

查找系统库文件位置:首先需要确认您系统的 libpthread.so.0文件在哪里。

sudo find / -name libpthread.so.0 2>/dev/null

/home/r9000k/anaconda3/pkgs/sysroot_linux-64-2.17-h0157908_18/x86_64-conda-linux-gnu/sysroot/lib64/libpthread.so.0
/home/r9000k/anaconda3/envs/pyslam/x86_64-conda-linux-gnu/sysroot/lib64/libpthread.so.0
/usr/lib/i386-linux-gnu/libpthread.so.0
/usr/lib/x86_64-linux-gnu/libpthread.so.0
/usr/lib32/libpthread.so.0
/opt/deepinwine/runtime-i386/lib/i386-linux-gnu/libpthread.so.0
/opt/deepinwine/runtime-i386/lib/x86_64-linux-gnu/libpthread.so.0
/snap/snapd/25202/usr/lib/x86_64-linux-gnu/libpthread.so.0
/snap/snapd/25577/usr/lib/x86_64-linux-gnu/libpthread.so.0
/snap/core20/2686/usr/lib/i386-linux-gnu/libpthread.so.0
/snap/core20/2686/usr/lib/x86_64-linux-gnu/libpthread.so.0
/snap/core20/2682/usr/lib/i386-linux-gnu/libpthread.so.0
/snap/core20/2682/usr/lib/x86_64-linux-gnu/libpthread.so.0
/snap/core22/2163/usr/lib/i386-linux-gnu/libpthread.so.0
/snap/core22/2163/usr/lib/x86_64-linux-gnu/libpthread.so.0
/snap/core22/2193/usr/lib/i386-linux-gnu/libpthread.so.0
/snap/core22/2193/usr/lib/x86_64-linux-gnu/libpthread.so.0
/snap/core18/2976/lib/i386-linux-gnu/libpthread.so.0
/snap/core18/2976/lib/x86_64-linux-gnu/libpthread.so.0
/snap/core18/2959/lib/i386-linux-gnu/libpthread.so.0
/snap/core18/2959/lib/x86_64-linux-gnu/libpthread.so.0
/snap/core24/1237/usr/lib/i386-linux-gnu/libpthread.so.0
/snap/core24/1237/usr/lib/x86_64-linux-gnu/libpthread.so.0
/snap/core24/1243/usr/lib/i386-linux-gnu/libpthread.so.0
/snap/core24/1243/usr/lib/x86_64-linux-gnu/libpthread.so.0

  查找你的Conda环境中的系统库路径。通常它在 $CONDA_PREFIX/x86_64-conda-linux-gnu/sysroot/下。你可以使用以下命令查找:

find $CONDA_PREFIX -name "libpthread.so.0" 2>/dev/null

  

image

 假设找到了相关文件,使用 cp -inr命令将Conda环境中的库文件复制到系统库目录(注意:使用 -n参数避免覆盖已有文件)

sudo cp -inr $CONDA_PREFIX/x86_64-conda-linux-gnu/sysroot/lib64/* /lib64/
sudo cp -inr $CONDA_PREFIX/x86_64-conda-linux-gnu/sysroot/usr/lib64/* /usr/lib64/

  

成功新安装看起来你的编译过程已经成功了!

image

 报错2 

(可选)编译 RoPE 的 cuda 内核(如 CroCo v2 中那样)先跳过

这个错误是因为你的 CUDA 11.8 与当前使用的 GCC 编译器版本不兼容。错误信息明确提示:unsupported GNU version! gcc versions later than 11 are not supported!。这意味着 CUDA 11.8 不支持版本高于 11 的 GCC 编译器。

conda install -c conda-forge gxx_linux-64=11.*

  

下载权重

mkdir -p checkpoints/
wget https://download.europe.naverlabs.com/ComputerVision/MASt3R/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric.pth -P checkpoints/

  

image

 

选择是否用cpu或者cuda 

选择模型是在线读取还是离线文件

 

    #device = 'cpu'
    device = 'cuda'
    #model_name = "naver/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric"
    model_name = "checkpoints/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric.pth"
    

    
    print("开始加载MASt3R模型")
    model = AsymmetricMASt3R.from_pretrained(model_name).to(device)
    print("加载模型完毕,加载图")

  

 

 

测试代码1 双图匹配matplotlib显示

自动下载全红和数据 

from mast3r.model import AsymmetricMASt3R
from mast3r.fast_nn import fast_reciprocal_NNs

import mast3r.utils.path_to_dust3r
from dust3r.inference import inference
from dust3r.utils.image import load_images

if __name__ == '__main__':
    device = 'cuda'
    model_name = "naver/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric"
    # you can put the path to a local checkpoint in model_name if needed
    model = AsymmetricMASt3R.from_pretrained(model_name).to(device)
    images = load_images(['dust3r/croco/assets/Chateau1.png', 'dust3r/croco/assets/Chateau2.png'], size=512)
    output = inference([tuple(images)], model, device, batch_size=1, verbose=False)

    # at this stage, you have the raw dust3r predictions
    view1, pred1 = output['view1'], output['pred1']
    view2, pred2 = output['view2'], output['pred2']

    desc1, desc2 = pred1['desc'].squeeze(0).detach(), pred2['desc'].squeeze(0).detach()

    # find 2D-2D matches between the two images
    matches_im0, matches_im1 = fast_reciprocal_NNs(desc1, desc2, subsample_or_initxy1=8,
                                                   device=device, dist='dot', block_size=2**13)

    # ignore small border around the edge
    H0, W0 = view1['true_shape'][0]
    valid_matches_im0 = (matches_im0[:, 0] >= 3) & (matches_im0[:, 0] < int(W0) - 3) & (
        matches_im0[:, 1] >= 3) & (matches_im0[:, 1] < int(H0) - 3)

    H1, W1 = view2['true_shape'][0]
    valid_matches_im1 = (matches_im1[:, 0] >= 3) & (matches_im1[:, 0] < int(W1) - 3) & (
        matches_im1[:, 1] >= 3) & (matches_im1[:, 1] < int(H1) - 3)

    valid_matches = valid_matches_im0 & valid_matches_im1
    matches_im0, matches_im1 = matches_im0[valid_matches], matches_im1[valid_matches]

    # visualize a few matches
    import numpy as np
    import torch
    import torchvision.transforms.functional
    from matplotlib import pyplot as pl

    n_viz = 20
    num_matches = matches_im0.shape[0]
    match_idx_to_viz = np.round(np.linspace(0, num_matches - 1, n_viz)).astype(int)
    viz_matches_im0, viz_matches_im1 = matches_im0[match_idx_to_viz], matches_im1[match_idx_to_viz]

    image_mean = torch.as_tensor([0.5, 0.5, 0.5], device='cpu').reshape(1, 3, 1, 1)
    image_std = torch.as_tensor([0.5, 0.5, 0.5], device='cpu').reshape(1, 3, 1, 1)

    viz_imgs = []
    for i, view in enumerate([view1, view2]):
        rgb_tensor = view['img'] * image_std + image_mean
        viz_imgs.append(rgb_tensor.squeeze(0).permute(1, 2, 0).cpu().numpy())

    H0, W0, H1, W1 = *viz_imgs[0].shape[:2], *viz_imgs[1].shape[:2]
    img0 = np.pad(viz_imgs[0], ((0, max(H1 - H0, 0)), (0, 0), (0, 0)), 'constant', constant_values=0)
    img1 = np.pad(viz_imgs[1], ((0, max(H0 - H1, 0)), (0, 0), (0, 0)), 'constant', constant_values=0)
    img = np.concatenate((img0, img1), axis=1)
    pl.figure()
    pl.imshow(img)
    cmap = pl.get_cmap('jet')
    for i in range(n_viz):
        (x0, y0), (x1, y1) = viz_matches_im0[i].T, viz_matches_im1[i].T
        pl.plot([x0, x1 + W0], [y0, y1], '-+', color=cmap(i / (n_viz - 1)), scalex=False, scaley=False)
    pl.show(block=True)

  

报错

这个警告和错误提示表明您的 DUSt3R 项目遇到了两个主要问题:RoPE2D 的 CUDA 内核编译失败,以及 Qt 图形界面库的兼容性问题。下面我来帮您分析和解决。

问题分析

从您的输出信息可以看到两个核心问题:
  1. RoPE2D CUDA 内核编译失败:Warning, cannot find cuda-compiled version of RoPE2D, using a slow pytorch version instead
  2. Qt 图形界面库加载失败:导致程序崩溃

 

pip install "numpy<2"

 

 

测试代码2 纯opencv显示

image

 

image

 

默认从云端加载权重

    print("开始加载模型")
    model = AsymmetricMASt3R.from_pretrained(model_name).to(device)
    #model_path = "./checkpoints/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric" 
    #model = AsymmetricMASt3R.from_pretrained(model_name).to(device)

  

 

 

from mast3r.model import AsymmetricMASt3R
from mast3r.fast_nn import fast_reciprocal_NNs
import mast3r.utils.path_to_dust3r
from dust3r.inference import inference
from dust3r.utils.image import load_images
import cv2
import numpy as np
import torch

if __name__ == '__main__':
    device = 'cuda'
    model_name = "naver/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric"
    print("开始加载模型")
    model = AsymmetricMASt3R.from_pretrained(model_name).to(device)
    #model_path = "./checkpoints/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric" 
    #model = AsymmetricMASt3R.from_pretrained(model_name).to(device)
    print("加载模型完毕,加载图")
    images = load_images(['dust3r/croco/assets/Chateau1.png', 'dust3r/croco/assets/Chateau2.png'], size=512)
    output = inference([tuple(images)], model, device, batch_size=1, verbose=False)
     
    view1, pred1 = output['view1'], output['pred1']
    view2, pred2 = output['view2'], output['pred2']
    desc1, desc2 = pred1['desc'].squeeze(0).detach(), pred2['desc'].squeeze(0).detach()

    matches_im0, matches_im1 = fast_reciprocal_NNs(desc1, desc2, subsample_or_initxy1=8,
                                                   device=device, dist='dot', block_size=2**13)
    print("匹配结束么,开始挑选有效点")
    H0, W0 = view1['true_shape'][0]
    valid_matches_im0 = (matches_im0[:, 0] >= 3) & (matches_im0[:, 0] < int(W0) - 3) & (
        matches_im0[:, 1] >= 3) & (matches_im0[:, 1] < int(H0) - 3)

    H1, W1 = view2['true_shape'][0]
    valid_matches_im1 = (matches_im1[:, 0] >= 3) & (matches_im1[:, 0] < int(W1) - 3) & (
        matches_im1[:, 1] >= 3) & (matches_im1[:, 1] < int(H1) - 3)

    valid_matches = valid_matches_im0 & valid_matches_im1
    matches_im0, matches_im1 = matches_im0[valid_matches], matches_im1[valid_matches]

    # 可视化部分 - 使用OpenCV替代Matplotlib
    n_viz = 20
    num_matches = matches_im0.shape[0]
    match_idx_to_viz = np.round(np.linspace(0, num_matches - 1, n_viz)).astype(int)
    viz_matches_im0, viz_matches_im1 = matches_im0[match_idx_to_viz], matches_im1[match_idx_to_viz]

    image_mean = torch.as_tensor([0.5, 0.5, 0.5], device='cpu').reshape(1, 3, 1, 1)
    image_std = torch.as_tensor([0.5, 0.5, 0.5], device='cpu').reshape(1, 3, 1, 1)

    viz_imgs = []
    for i, view in enumerate([view1, view2]):
        rgb_tensor = view['img'] * image_std + image_mean
        viz_imgs.append(rgb_tensor.squeeze(0).permute(1, 2, 0).cpu().numpy())
    
    # OpenCV可视化代码
    print('OpenCV可视化代码')
    img0_bgr = (viz_imgs[0][:, :, ::-1] * 255).astype(np.uint8)
    img1_bgr = (viz_imgs[1][:, :, ::-1] * 255).astype(np.uint8)

    h0, w0 = img0_bgr.shape[:2]
    h1, w1 = img1_bgr.shape[:2]
    max_height = max(h0, h1)
    concatenated_img = np.zeros((max_height, w0 + w1, 3), dtype=np.uint8)
    concatenated_img[:h0, :w0] = img0_bgr
    concatenated_img[:h1, w0:w0+w1] = img1_bgr

    colors = [
        (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255),
        (0, 255, 255), (128, 0, 0), (0, 128, 0), (0, 0, 128), (128, 128, 0),
        (128, 0, 128), (0, 128, 128), (192, 192, 192), (255, 165, 0), (255, 192, 203),
        (138, 43, 226), (165, 42, 42), (188, 143, 143), (95, 158, 160), (100, 149, 237)
    ]

    for i, (pt0, pt1) in enumerate(zip(viz_matches_im0, viz_matches_im1)):
        color = colors[i % len(colors)]
        x0, y0 = int(pt0[0]), int(pt0[1])
        x1, y1 = int(pt1[0]), int(pt1[1])
        
        cv2.line(concatenated_img, (x0, y0), (x1 + w0, y1), color, 2, cv2.LINE_AA)
        cv2.circle(concatenated_img, (x0, y0), 5, color, -1)
        cv2.circle(concatenated_img, (x1 + w0, y1), 5, color, -1)

    cv2.imshow('Feature Matches', concatenated_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

  

 测试3 对比orb 效果

image

 

cpu模式

image

gpu模式 占用3.6G

开始以前

image

 结束以后

image

 

 

 

优化代码

image

 

image

 

 

image

 

image

 ======================

 

错误匹配

先放一个正确的

image

 

 

image

 

image

 

image

 

正确匹配

同高度不同时间 不同高度

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

 

image

不同高度

image

 

image

 

image

 

 

image

 

image

 

image

 

image

 

image

 

白天和夜晚

 

 120米  光圈2.8  曝光时间1/30秒 增益1600 对比3200

image

 

 

image

 

image

 

image

 

 120米  光圈2.8  曝光时间1/30秒 增益3200 

image

 

image

 

 

 

image

 

image

 

 

image

 


image

 120米  光圈2.8  曝光时间1/30秒 增益1600

image

 

image

 

image

 

 

 

 

image

 

 

image

 

image

 

image

 

image

 

 

image

 

image

 

image

 

 

 

image

 

image

 

image

 

 120米  光圈2.8  曝光时间1/30秒 增益3200

import cv2

import numpy as np

import random

draw_num=20 # 画出来前多少个点

line_thickness_set=4
font_scale = 2.5
color_title = (255, 255, 255)  # 蓝 绿 红
thickness = 6


colors_Line = [
    (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255),(0, 255, 255),(0, 100, 255),(0, 255, 100)
]

def generate_pastel_color():
    """生成柔和的随机颜色"""
    r = random.randint(30, 255)  # 限制在较亮的范围
    g = random.randint(30, 255)
    b = random.randint(30, 255)
    return (r, g, b)

def draw_ORB_matches(img1, kp1, img2, kp2, top_n_matches, line_thickness=line_thickness_set):
    """
    根据匹配质量(distance)动态分配颜色:高质量→绿蓝色,低质量→橘红色
    修改版本:直接接受已筛选的top_n_matches列表
    
    参数:
        img1: 第一张图像
        kp1: 第一张图像的关键点
        img2: 第二张图像  
        kp2: 第二张图像的关键点
        top_n_matches: 已经筛选好的匹配列表(通常是按距离排序后的前N个)
        line_thickness: 连线粗细
    """
    # 获取图像尺寸
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]
    
    # 创建拼接图像
    new_width = w1 + w2
    new_height = max(h1, h2)
    result = np.zeros((new_height, new_width, 3), dtype=np.uint8)
    result[0:h1, 0:w1] = img1
    result[0:h2, w1:w1+w2] = img2
    
    # 检查匹配列表是否为空
    if len(top_n_matches) == 0:
        print("警告:匹配列表为空")
        return result
    
    # 获取所有匹配的距离值
    distances = [m.distance for m in top_n_matches]
    min_dist = min(distances)
    max_dist = max(distances)
    
    print(f"绘制 {len(top_n_matches)} 个匹配点")
    print(f"最小距离: {min_dist:.2f}, 最大距离: {max_dist:.2f}")
    
    # 绘制每个匹配对
    for i, match in enumerate(top_n_matches):
    #     # 获取关键点坐标
        pt1 = (int(kp1[match.queryIdx].pt[0]), int(kp1[match.queryIdx].pt[1]))
        pt2 = (int(kp2[match.trainIdx].pt[0] + w1), int(kp2[match.trainIdx].pt[1]))
        
        # # 根据匹配质量计算颜色
        # # 归一化距离 (0-1范围,0表示最好,1表示最差)


        # # # 根据匹配质量计算颜色(距离越小,匹配质量越高)
        # norm_dist = (match.distance - min_dist) / (max_dist - min_dist + 1e-5)
        
        # # 颜色映射:高质量→绿色,中等质量→黄色,低质量→红色
        # if norm_dist < 0.33:  # 最好的33% - 绿色系
        #     blue = 0
        #     green = 255
        #     red = int(255 * (norm_dist / 0.33))
        # elif norm_dist < 0.66:  # 中间33% - 黄色到橙色
        #     blue = 0
        #     green = int(255 * (1 - (norm_dist - 0.33) / 0.33))
        #     red = 255
        # else:  # 最差的33% - 橙色到红色
        #     blue = 0
        #     green = int(128 * (1 - (norm_dist - 0.66) / 0.34))
        #     red = 255
        
        color = colors_Line[i % len(colors_Line)]
        
        #color=generate_pastel_color()
        # 绘制连线
        cv2.line(result, pt1, pt2, color, line_thickness)
        
        # # 可选:在关键点旁边显示匹配质量和序号
        # if line_thickness > 1:  # 只在连线较粗时显示文本,避免过于拥挤
        #     cv2.putText(result, f"{i}", (pt1[0]+5, pt1[1]), 
        #                cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
        #     cv2.putText(result, f"{match.distance:.1f}", (pt2[0]+5, pt2[1]), 
        #                cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)

    return result

# 使用示例
def Orb_Match(image_paths):


    img1 = cv2.imread(image_paths[0])
    img2 = cv2.imread(image_paths[1])
    
    # 初始化ORB检测器
    orb = cv2.ORB_create()
    start_time = time.time()
    # 检测关键点并计算描述子
    kp1, des1 = orb.detectAndCompute(img1, None)
    kp2, des2 = orb.detectAndCompute(img2, None)
    
    # 创建BFMatcher并进行匹配
    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    matches = bf.match(des1, des2)
    
    # 计算绘制时间
    pipei_time = time.time() - start_time
    pipei_time=round(pipei_time,3)


    # 按距离排序并选择前N个最佳匹配
    matches_sorted = sorted(matches, key=lambda x: x.distance)
    top_n_matches = matches_sorted[:draw_num]  # 选择前30个最佳匹配
    
    # 使用修改后的函数绘制匹配结果
    result_img = draw_ORB_matches(img1, kp1, img2, kp2, top_n_matches)

    cv2.putText(result_img, "ORB-time:"+str(pipei_time), (30, 90),  cv2.FONT_HERSHEY_SIMPLEX, font_scale, color_title, thickness)
    return result_img



def sift_feature_matching(image_paths, top_n=30, match_ratio=0.7):
    """
    使用SIFT算法进行图像特征匹配
    
    参数:
        image_paths: 包含两个图像路径的列表
        top_n: 选择最佳匹配的数量
        match_ratio: 比率测试的阈值(用于FLANN匹配器)
    
    返回:
        result_img: 绘制匹配结果的图像
        match_count: 成功匹配的数量
        avg_distance: 平均匹配距离
    """
    # 读取图像
    img1 = cv2.imread(image_paths[0])
    img2 = cv2.imread(image_paths[1])
    
    if img1 is None or img2 is None:
        print("错误:无法读取图像文件")
        return None, 0, 0
    
    # 转换为灰度图
    gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    
    # 创建SIFT特征检测器
    sift = cv2.SIFT_create(
        nfeatures=2000,           # 保留的最佳特征点数量(0表示保留所有)
        nOctaveLayers=3,       # 每个八度空间的层数
        contrastThreshold=0.04, # 对比度阈值
        edgeThreshold=10,      # 边缘阈值
        sigma=1.6              # 高斯滤波器参数
    )

    start_time = time.time()
    
    # 检测关键点并计算描述符
    kp1, des1 = sift.detectAndCompute(gray1, None)
    kp2, des2 = sift.detectAndCompute(gray2, None)
    
    print(f"图像1检测到 {len(kp1)} 个关键点")
    print(f"图像2检测到 {len(kp2)} 个关键点")
    
    if des1 is None or des2 is None:
        print("错误:无法计算特征描述符")
        return None, 0, 0
    
    # 创建FLANN匹配器
    index_params = dict(algorithm=1, trees=5)  # KD树算法
    search_params = dict(checks=50)             # 搜索次数
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    
    # 使用kNN匹配,k=2
    matches = flann.knnMatch(des1, des2, k=2)
    
    # 计算绘制时间
    pipei_time = time.time() - start_time
    pipei_time=round(pipei_time,3)



    # 应用比率测试筛选优质匹配
    good_matches = []
    for m, n in matches:
        if m.distance < match_ratio * n.distance:
            good_matches.append(m)
    
    print(f"原始匹配数量: {len(matches)}")
    print(f"经过比率测试后的优质匹配数量: {len(good_matches)}")
    
    # 按匹配距离排序并选择前N个最佳匹配
    good_matches_sorted = sorted(good_matches, key=lambda x: x.distance)
    top_matches = good_matches_sorted[:top_n]
    
    # 计算匹配质量指标
    if len(top_matches) > 0:
        avg_distance = sum(m.distance for m in top_matches) / len(top_matches)
        min_distance = min(m.distance for m in top_matches)
        max_distance = max(m.distance for m in top_matches)
        
        print(f"最佳匹配距离: {min_distance:.2f}")
        print(f"最差匹配距离: {max_distance:.2f}")
        print(f"平均匹配距离: {avg_distance:.2f}")
    else:
        avg_distance = 0
        print("警告:没有找到足够的优质匹配")
    
    # 绘制匹配结果
    result_img = draw_sift_matches(img1, kp1, img2, kp2, top_matches)
    cv2.putText(result_img, "SIFT-time:"+str(pipei_time), (30, 90),  cv2.FONT_HERSHEY_SIMPLEX, font_scale, color_title, thickness)
    return result_img, len(top_matches), avg_distance

def draw_sift_matches(img1, kp1, img2, kp2, matches, line_thickness=line_thickness_set):
    """
    绘制SIFT匹配结果,根据匹配质量使用渐变色
    
    参数:
        img1, img2: 输入图像
        kp1, kp2: 关键点列表
        matches: 匹配结果
        line_thickness: 连线粗细
    
    返回:
        绘制匹配结果的图像
    """
    # 获取图像尺寸
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]
    
    # 创建拼接图像
    new_width = w1 + w2
    new_height = max(h1, h2)
    result = np.zeros((new_height, new_width, 3), dtype=np.uint8)
    result[0:h1, 0:w1] = img1
    result[0:h2, w1:w1+w2] = img2
    
    if len(matches) == 0:
        print("警告:匹配列表为空")
        return result
    
    # 获取所有匹配的距离值用于颜色映射
    distances = [m.distance for m in matches]
    min_dist = min(distances)
    max_dist = max(distances)
    
    print(f"绘制 {len(matches)} 个匹配点")
    
    # 绘制每个匹配对
    for i, match in enumerate(matches):
        # 获取关键点坐标
        pt1 = (int(kp1[match.queryIdx].pt[0]), int(kp1[match.queryIdx].pt[1]))
        pt2 = (int(kp2[match.trainIdx].pt[0] + w1), int(kp2[match.trainIdx].pt[1]))
        
        # # # 根据匹配质量计算颜色(距离越小,匹配质量越高)
        # norm_dist = (match.distance - min_dist) / (max_dist - min_dist + 1e-5)
        
        # # 颜色映射:高质量→绿色,中等质量→黄色,低质量→红色
        # if norm_dist < 0.33:  # 最好的33% - 绿色系
        #     blue = 0
        #     green = 255
        #     red = int(255 * (norm_dist / 0.33))
        # elif norm_dist < 0.66:  # 中间33% - 黄色到橙色
        #     blue = 0
        #     green = int(255 * (1 - (norm_dist - 0.33) / 0.33))
        #     red = 255
        # else:  # 最差的33% - 橙色到红色
        #     blue = 0
        #     green = int(128 * (1 - (norm_dist - 0.66) / 0.34))
        #     red = 255
        color = colors_Line[i % len(colors_Line)]
        #color = (blue, green, red)
        #color=generate_pastel_color()
        # 绘制连线
        cv2.line(result, pt1, pt2, color, line_thickness)
        
        # 绘制关键点
        cv2.circle(result, pt1, 5, color, -1)
        cv2.circle(result, pt2, 5, color, -1)
        
        # 显示匹配序号和距离
        if line_thickness > 1 and len(matches) <= 50:  # 避免文本过于拥挤
            cv2.putText(result, f"{i}", (pt1[0]+5, pt1[1]), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
            cv2.putText(result, f"{match.distance:.1f}", (pt2[0]+5, pt2[1]), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
    

            
    return result




from mast3r.model import AsymmetricMASt3R
from mast3r.fast_nn import fast_reciprocal_NNs
import mast3r.utils.path_to_dust3r
from dust3r.inference import inference
from dust3r.utils.image import load_images
import cv2
import numpy as np
import torch
import time




class FeatureMatcher_Mast3r:
    def __init__(self, model_path, device='cuda'):
        """
        初始化特征匹配器
        Args:
            model_path: MASt3R模型路径
            device: 运行设备
        """
        self.device = device
        self.model = self.load_mast3r_model(model_path)
    
    def load_mast3r_model(self, model_path):
        """加载MASt3R模型"""
        print("开始加载MASt3R模型")
        model = AsymmetricMASt3R.from_pretrained(model_path).to(self.device)
        print("MASt3R模型加载完成")
        return model
    
    def load_and_preprocess_images(self, image_paths, size=512):
        """
        加载并预处理图像
        Args:
            image_paths: 图像路径列表
            size: 图像尺寸
        Returns:
           预处理后的图像数据
        """
        images = load_images(image_paths, size=size)
        
        # 图像标准化
        image_mean = torch.as_tensor([0.5, 0.5, 0.5], device='cpu').reshape(1, 3, 1, 1)
        image_std = torch.as_tensor([0.5, 0.5, 0.5], device='cpu').reshape(1, 3, 1, 1)
        
        viz_imgs = []
        for i, view in enumerate([images[0], images[1]]):
            rgb_tensor = view['img'] * image_std + image_mean
            viz_img = rgb_tensor.squeeze(0).permute(1, 2, 0).cpu().numpy()
            viz_imgs.append(viz_img)
        
        # 转换为OpenCV格式
        img0_bgr = (viz_imgs[0][:, :, ::-1] * 255).astype(np.uint8)
        img1_bgr = (viz_imgs[1][:, :, ::-1] * 255).astype(np.uint8)
        
        return images, img0_bgr, img1_bgr

    def mast3r_feature_matching(self, images):
        """
        MASt3R特征匹配
        Args:
            images: 输入图像
        Returns:
           匹配结果、关键点坐标和耗时
        """
        start_time = time.time()
        
        # 执行推理
        output = inference([tuple(images)], self.model, self.device, batch_size=1, verbose=False)
        
        view1, pred1 = output['view1'], output['pred1']
        view2, pred2 = output['view2'], output['pred2']
        desc1, desc2 = pred1['desc'].squeeze(0).detach(), pred2['desc'].squeeze(0).detach()

        # 快速互最近邻匹配
        matches_im0, matches_im1 = fast_reciprocal_NNs(desc1, desc2, subsample_or_initxy1=8,
                                                       device=self.device, dist='dot', block_size=2**13)
        
        # 有效点筛选
        H0, W0 = view1['true_shape'][0]
        valid_matches_im0 = (matches_im0[:, 0] >= 3) & (matches_im0[:, 0] < int(W0) - 3) & (
            matches_im0[:, 1] >= 3) & (matches_im0[:, 1] < int(H0) - 3)

        H1, W1 = view2['true_shape'][0]
        valid_matches_im1 = (matches_im1[:, 0] >= 3) & (matches_im1[:, 0] < int(W1) - 3) & (
            matches_im1[:, 1] >= 3) & (matches_im1[:, 1] < int(H1) - 3)

        valid_matches = valid_matches_im0 & valid_matches_im1
        matches_im0, matches_im1 = matches_im0[valid_matches], matches_im1[valid_matches]
        
        end_time = time.time()
        total_time = end_time - start_time
        print("Mast3R 耗费时间:",total_time)
        
        return matches_im0, matches_im1, total_time


    def visualize_results(self, mast3r_matches_im0, mast3r_matches_im1, img0_bgr, img1_bgr, n_viz=100):
        """
        可视化MASt3R匹配结果
        """
        h0, w0 = img0_bgr.shape[:2]
        h1, w1 = img1_bgr.shape[:2]
        max_height = max(h0, h1)
        
        # 创建结果图像
        result_img = np.zeros((max_height, w0 + w1, 3), dtype=np.uint8)
        
        # 放置原始图像
        result_img[:h0, :w0] = img0_bgr
        result_img[:h1, w0:w0+w1] = img1_bgr
        
        # 绘制MASt3R匹配点
        colors = [
            (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255),(0, 255, 255),(0, 100, 255),(0, 255, 100)
        ]
        print('总点数',mast3r_matches_im0.shape[0])
        n_viz_actual = min(n_viz, mast3r_matches_im0.shape[0])
        indices = np.round(np.linspace(0, mast3r_matches_im0.shape[0] - 1, n_viz_actual)).astype(int)
        
        for i, idx in enumerate(indices):
            color = colors[i % len(colors)]
            pt1 = mast3r_matches_im0[idx]
            pt2 = mast3r_matches_im1[idx]
            
            x1, y1 = int(pt1[0]), int(pt1[1])
            x2, y2 = int(pt2[0]) + w0, int(pt2[1])
            
            cv2.line(result_img, (x1, y1), (x2, y2), color, 1, cv2.LINE_AA)
            cv2.circle(result_img, (x1, y1), 3, color, -1)
            cv2.circle(result_img, (x2, y2), 3, color, -1)
        
        # # 添加标签
        # cv2.putText(result_img, "MASt3R-Time:"+str(), (30, 200), 
        #             cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)
        
        return result_img
    



if __name__ == "__main__":

    # =============================1 执行ORB匹配
    # 读取图像
    image_paths = [
        '/media/r9000k/DD_XS/2数据/2_1精灵4无rtk/2圆楼_滨湖_白天_resize/DJI_0516.JPG',
        '/media/r9000k/DD_XS/2数据/2_1精灵4无rtk/v4_2圆楼_滨湖_拍照模式测曝光_resize/DJI_0146.JPG'
    ]
    orb_result_img = Orb_Match(image_paths)


    # 显示结果
    # cv2.namedWindow('orb', cv2.WINDOW_NORMAL)
    # cv2.imshow('orb', orb_result_img)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()



    # =============================2 执行SIFT匹配
    sift_result_img, match_count, avg_distance = sift_feature_matching(
        image_paths, 
        top_n=draw_num,      # 显示前30个最佳匹配
        match_ratio=0.7 # 比率测试阈值
    )

    # 显示结果
    # cv2.namedWindow('SIFT Feature Matching', cv2.WINDOW_NORMAL)
    # cv2.imshow('SIFT Feature Matching', sift_result_img)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()



    # =============================3 执行Mast3r匹配
    # 创建特征匹配器实例
    Mast3r_model_path = "checkpoints/MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric.pth"
    matcher_Mast3r = FeatureMatcher_Mast3r(Mast3r_model_path)
    
    # 加载图像
    images, img0_bgr, img1_bgr = matcher_Mast3r.load_and_preprocess_images(image_paths)
    
    # MASt3R特征匹配
    mast3r_pts1, mast3r_pts2, mast3r_time = matcher_Mast3r.mast3r_feature_matching(images)

    # 可视化结果
    mast3r_result_img = matcher_Mast3r.visualize_results(mast3r_pts1, mast3r_pts2, img0_bgr, img1_bgr, n_viz=draw_num)

        # 添加标签
    cv2.putText(mast3r_result_img, "MASt3R-Time:"+str(round(mast3r_time,3)), (10, 30), 
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, color_title, 2)
    
    
    # 显示结果
    # cv2.namedWindow('MASt3R Feature Matching Results', cv2.WINDOW_NORMAL)
    # cv2.imshow('MASt3R Feature Matching Results', mast3r_result_img)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()


    # =============================4 可视化对比图
    # 如果使用 PyQt5
    from PyQt5.QtCore import QLibraryInfo
    import os
    os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = QLibraryInfo.location(QLibraryInfo.PluginsPath)


    import matplotlib.pyplot as plt
    import matplotlib.image as mpimg
    # 将三张图片存入列表方便处理
    images = [orb_result_img, sift_result_img, mast3r_result_img]
    titles = ['ORB', 'SIFT', 'Mast3R']  # 可自定义每个子图的标题

    # 2. 创建子图布局:3行1列
    fig, axs = plt.subplots(3, 1, figsize=(20, 20))  # figsize可调整整个窗口大小

    # 3. 在每个子图中显示图片
    for i, ax in enumerate(axs):
        result_img_rgb = images[i][:, :, ::-1]  # BGR转RGB OpenCV 的 BGR 格式与 matplotlib 的 RGB 格式
        ax.imshow(result_img_rgb)  # 显示图片
        ax.set_title(titles[i])  # 设置标题
        ax.axis('off')  # 关闭坐标轴,让图片更清爽


    # 4. 调整布局并显示
    plt.tight_layout()
    plt.show()

  

posted on 2026-01-01 01:54  MKT-porter  阅读(87)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3