OpenCV实现的F矩阵+RANSAC原理与实践

2023-09-20 09:24:16

1 RANSAC 筛选

1.1 大致原理

Random sample consensus (RANSAC),即随机抽样一致性,其是一种用于估计模型参数的迭代方法,特别适用于处理包含离群点(outliers)的数据集

RANSAC 的主要思想是随机采样数据点,用这些采样点拟合一个模型,然后计算其他数据点到这个模型的拟合误差;根据误差和阈值,将数据点分为内点(inliers)外点(outliers);重复这个过程多次,选择具有最多内点的模型作为最终的估计

大致步骤为:

  1. 随机采样

    从数据集中随机选择一小部分数据点,通常是与所估计模型参数数量相等的数据点

  2. 模型拟合

    使用这些随机采样的数据点拟合一个模型,例如直线

  3. 内点检测

    计算所有数据点到模型的拟合误差,并将与模型拟合误差小于某个阈值的数据点标记为内点

  4. 判断是否满足条件

    如果内点的数量达到了某个设定的阈值,且模型参数的估计是合理的(例如,模型的拟合误差小于一定阈值),则认为找到了一个满足条件的模型

  5. 重复迭代

    重复上述过程多次(通常几百甚至上千次),并且记录具有最多内点的模型,以及该模型估计的内点

  6. 输出最佳模型

    在所有迭代中,选择具有最多内点的模型作为最终的估计模型。这些内点被认为是满足模型的数据点

1.2 优缺点

RANSAC 的一个优点是它能够对模型参数进行鲁棒估计,能够在存在大量噪声的情况下找到合适的模

RANSAC 的缺点是计算这些参数所需的时间没有上限,其需要进行大量的随机采样和模型拟合,因此对于大规模数据集来说,计算复杂度较高,可能需要较长的时间来运行

1.3 实践效果

请见 3 实践操作 步骤二

2 F矩阵

2.1 基本原理

基本矩阵(Fundamental Matrix)描述了两个摄像机之间的基本几何关系

在对极几何中,对于立体图像对中对应点的齐次图像坐标 p1p2F*p1描述了另一图像上的对应点 p2 必须位于其上的线(对极线)这意味着,对于所有对应点对都成立:
p 2 T ∗ F ∗ p 1 = 0 p_2^T * F * p_1 = 0 p2TFp1=0

2.2 函数实现

在 OpenCV 中,提供了 findFundamentalMat 函数,用于估计两幅图像之间的F矩阵

以下是 findFundamentalMat 函数的基本用法:

Mat fundamental_matrix = findFundamentalMat(points1, points2, mask, method, ransacReprojThreshold, confidence);

其中各参数的含义如下:

  • points1points2:两幅图像中的匹配点坐标,通常是 vector<Point2f> 类型,表示两幅图像中匹配点的像素坐标
  • mask:是一个输出数组通常是 vector<uchar> 类型,用于指示哪些匹配点被视为内点(inliers)和哪些被视为外点(outliers
  • method:用于指定计算基本矩阵的方法,可以是以下几种选项之一:
    • cv::FM_RANSAC:使用 RANSAC 算法进行估计,用于排除离群点
    • cv::FM_LMEDS:使用最小中值误差估计方法
    • cv::FM_8POINT:使用 8 点法估计基本矩阵
  • ransacReprojThreshold:RANSAC 算法中的重投影误差阈值,用于判断内点和外点,通常需要根据具体问题来选择适当的阈值,默认值为 3.0
  • confidence:置信度,通常为默认值 0.99

3 实践操作

之前通过SIFT+Flann+ratio=0.7的图像匹配和初步筛选,得到了 good_matchesvector<DMatch> 类型)

步骤一:保存匹配点对坐标

vector<DMatch> 类型的 good_matches 中的坐标信息提取出来存入 vector<Point2f> 类型的 matchedPoints1/2 ;以便于后续的几何计算将匹配点对的坐标提取出来,以便于后续的几何计算

// 声明用于保存匹配点对的容器
vector<Point2f> matchedPoints1, matchedPoints2;
for (int i = 0; i < good_matches.size(); ++i)
{
    matchedPoints1.push_back(keypoints1[good_matches[i].queryIdx].pt);
    matchedPoints2.push_back(keypoints2[good_matches[i].trainIdx].pt);
}

步骤二:进行基本矩阵F的估计

直接使用 findFundamentalMat 函数

这个函数在内部已经包含了 RANSAC 筛选步骤;具体来说,findFundamentalMat 函数会计算基本矩阵 F,同时使用 RANSAC 迭代方法来排除离群点,确保得到的基本矩阵对于图像匹配是稳健的

// 使用RANSAC进行基本矩阵F的估计
Mat F;
vector<uchar> inliers;
F = findFundamentalMat(matchedPoints1, matchedPoints2, inliers, RANSAC);
  • 为了查看 RANSAC 筛选的效果我们进行如下操作
    inliers 是一个二进制向量,指示哪些匹配点对被视为内点(符合基本矩阵约束),哪些被视为外点;我们接下来使用内点来进一步筛选匹配,只保留通过 RANSAC 筛选的匹配对在ransac_filtered_matches中,并打印出来

    // 进一步筛选匹配
    vector<DMatch> ransac_filtered_matches;
    for (int i = 0; i < inliers.size(); ++i)
    {
        if (inliers[i])
        {
            ransac_filtered_matches.push_back(good_matches[i]);
        }
    }
    // ransac_filtered_matches 包含了通过RANSAC筛选后的匹配对
    Mat ransac_filtered_img_matches;
    drawMatches(img1, keypoints1, img2, keypoints2, ransac_filtered_matches, ransac_filtered_img_matches);    
    imwrite(SAVE_PATH, ransac_filtered_img_matches);
    
    匹配点数量图像效果
    RANSAC 筛选前2949img_matches_3&4
    RANSAC 筛选后2581img_matches_3&4_

    可以明显看出,在 RANSAC 筛选后,消除了 368 对离群匹配,达到了非常好的匹配效果

更多推荐

最佳实践:TiDB 业务写变慢分析处理

作者:李文杰数据架构师,TUG广州地区活动组织者在日常业务使用或运维管理TiDB的过程中,每个开发人员或数据库管理员都或多或少遇到过SQL变慢的问题。这类问题大部分情况下都具有一定的规律可循,通过经验的积累可以快速的定位和优化。但是有些情况下不一定很好排查,尤其涉及到内核调优等方向时,如果事先没有对各个组件的互访关系、

实现高效消息传递:使用RabbitMQ构建可复用的企业级消息系统

文章目录前言1.安装erlang语言2.安装rabbitMQ3.内网穿透3.1安装cpolar内网穿透(支持一键自动安装脚本)3.2创建HTTP隧道4.公网远程连接5.固定公网TCP地址5.1保留一个固定的公网TCP端口地址5.2配置固定公网TCP端口地址前言RabbitMQ是一个在AMQP(高级消息队列协议)基础上完

GODIVA论文阅读

论文链接:GODIVA:GeneratingOpen-DomaInVideosfromnAturalDescriptions文章目录摘要引言相关工作Video-to-videogenerationText-to-imagegenerationText-to-videogenerationGODIVA方法逐帧视频自动编码

【Java 基础篇】Executors工厂类详解

在多线程编程中,线程池是一项重要的工具,它可以有效地管理和控制线程的生命周期,提高程序的性能和可维护性。Java提供了java.util.concurrent包来支持线程池的创建和管理,而Executors工厂类是其中的一部分,它提供了一些方便的方法来创建不同类型的线程池。本文将详细介绍Executors工厂类的使用方

Android 使用Camera1实现相机预览、拍照、录像

1.前言本文介绍如何从零开始,在Android中实现Camera1的接入,并在文末提供Camera1Manager工具类,可以用于快速接入Camera1。AndroidCamera1API虽然已经被Google废弃,但有些场景下不得不使用。并且Camera1返回的帧数据是NV21,不像Camera2、CameraX那样

【C++】C 语言 和 C++ 语言中 const 关键字分析 ② ( const 常量分配内存时机 | const 常量在编译阶段分配内存 )

文章目录一、const常量内存分配时机二、使用如下代码验证const常量内存分配时机三、分析验证结果-const常量在编译阶段分配内存一、const常量内存分配时机在上一篇博客中,讲到了获取const常量的地址,代码如下://定义常量//该常量定义在了符号表中//符号表不在内存四区中,是另外一种机制constinta=

Pytorch 深度学习实践 day01(背景)

准备线性代数,概率论与数理统计,Python理解随机变量和分布之间的关系人类智能和人工智能人类智能分为推理和预测推理:通过外界信息的输入,来进行的推测预测:例如,看到一个真实世界的实体,把它和抽象概念联系起来人工智能(机器学习):把以前我们用来做推理或预测的大脑,变成算法在机器学习和深度学习中,常用的是监督学习,即有标

【深度学习实验】线性模型(三):使用Pytorch实现简单线性模型:搭建、构造损失函数、计算损失值

目录一、实验介绍二、实验环境1.配置虚拟环境2.库版本介绍三、实验内容0.导入库1.定义线性模型linear_model2.定义损失函数loss_function3.定义数据4.调用模型5.完整代码一、实验介绍使用Pytorch实现线性模型搭建构造损失函数计算损失值二、实验环境本系列实验使用了PyTorch深度学习框架

针对敏感数据的安全转录服务

即便在新冠肺炎疫情期间,继续保持了最高级别的机密性新冠肺炎疫情带来的各种限制向所有服务提供商提出了挑战,促使提供商们想方设法采取更富想象力的新方法来满足客户的需求。澳鹏采用了一种由两种方案组成的工作机制,服务于客户机密材料的转录,既实现了高度的机密性,又保护了员工的安全。大多数转录服务提供商都会采用基本的安全措施,如员

前端深入理解JavaScript中的WeakMap和WeakSet

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录1.WeakMap和WeakSet概述1.1WeakMap1.2WeakSet2.WeakMap深入解析2.1WeakMap的创建和使用2.2WeakMap和内存管理2.3WeakMap和对象私有数据3.Wea

【Linux】Linux环境配置安装

目录一、双系统(特别不推荐)安装双系统的缺点:安装双系统优点(仅限老手):二、虚拟机+centos7镜像(较为推荐推荐)虚拟机的优点:虚拟机的缺点:​下载centos7的镜像文件下载Ubuntu镜像文件Ubuntu镜像文件下载地址三、云服务器Xshell云服务器共享Xshell删除用户四、powershell一、双系统

热文推荐