RANSAC 配准算法

news2024/12/27 12:57:25

RANSAC 配准算法

  • 1. 简介
  • 2. RANSAC步骤
  • 3. RANSAC原理
  • 4. RANSAC的优缺点
  • 5. 代码实现
  • 6. 参考

1. 简介

先讲一下背景吧。

点云配准(Point Cloud Registration)指的是输入两幅点云 (source 和 target) ,输出一个变换使得变换后的sourcetarget的重合程度尽可能高。

点云配准可以分为粗配准(Coarse Registration)和精配准(Fine Registration)两步。粗配准指的是在两幅点云之间的变换完全未知的情况下进行较为粗糙的配准,目的主要是为精配准提供较好的变换初值;精配准则是给定一个初始变换,进一步优化得到更精确的变换。

而今天我们所要讲的内容就是RANSAC 粗配准算法。RANSAC即Random Sample Consensus,随机采样一致算法。

2. RANSAC步骤

RANSAC算法的核心思想是随机性和假设性。 请记住这句话。

随机性用于减少计算,循环次数是利用正确数据出现的概率。

而假设性,就是说随机抽出来的数据都认为是正确的,并以此去计算其他点,获得其他满足变换关系的点,然后利用投票机制,选出获票最多的那一个变换。

RANSAC广义上讲,是随机选择一个小的数据点子集,然后对其进行拟合,查看有多少其他点匹配到拟合的模型上,迭代这个过程直至有较大的概率找到我们想要拟合的模型。

在点云配准这个应用中,使用 RANSAC 方法,就是在源点云中随机找不共线的三个点,去“蒙”他们和目标点云中的哪三个点相对应,然后计算变换矩阵,并验证是否为最优的变换矩阵。简要流程如下:

  1. 在源点云中随机选择 3 个点。
  2. 分别查找他们在目标点云中特征向量最接近的点。这里的“接近”是欧式距离意义下的,查询数据结构使用 KD 树。当然,这里最接近的点会很多,我们为了在最快的时间内得到最终更好的匹配,通常会加一些约束,比如每组点的连线形成一个三角形,三角形的边不能太短、两个三角形的对应边长不能差太多、优先选择特征向量远离均值的点等等;
  3. 假设2中最接近的点是正确的,计算两组点之间的变换矩阵。本质是求最小二乘法意义下的最优变换矩阵。
  4. 求出源点云在此变换之后的点云,计算其与目标点云的重合度(定义为源点云中阈值半径内存在目标点的点的个数)。
  5. 回到1,重复若干次,返回重合度最高的变换。

以上,RANSAC其实是一种不确定的算法,即有一定的概率得出一个合理的结果,当然也会出现错误的结果。他为了提高时间效率不会去遍历每一个可能性,只是采取随机采样的方式,得到这些随机采样中的最好的结果,这个最好的结果也许是错误的结果,这里面就涉及到概率。如果要提高概率,一个要提高迭代的次数,在一个就是减少数据集离群点的比例。

因此,问题来了,在这算法流程中存在两个重要的参数需要设置,迭代次数(采样次数)和阈值。

迭代的次数我们应该选择多大呢?这个值是否可以事先知道应该设为多少呢?还是只能凭经验决定呢?

我们要剔除离群点 ,需要事先设定阈值,当模型具有明显的物理意义时,这个阈值还比较容易设定,但是若模型比较抽象时,阈值就不那么容易设定了,而且固定阈值不适用于样本动态变化的应用。那我们该怎么设置这个阈值呢?

3. RANSAC原理

我们先从一个小例子开始。

假设我们主观上看出了两个图像之间的点匹配,并且我们认为可以用一些参数去表示两者之间的变换,那么我们该如何去得到这个变换参数呢?

通常的策略是用最小二乘法去估计点之间的关系,但是会存在一个问题,如下图所示:
在这里插入图片描述
像这种离群点其实是不能很好拟合我们的模型的。能够拟合模型的点称为inliers,离群点称为outlier。
在这里插入图片描述
最小二乘估计对这些离群点很敏感,因此一些outlier可能会对结果进行扭曲,如下图所示。
在这里插入图片描述
而且,最小二乘估计只能估计出一个模型,如果存在两个或多个模型,也会对结果进行扭曲。
在这里插入图片描述

因此,为了解决以上问题,需要将估计视为一个两阶段过程:
– 将数据点分类为outlier或inlier
– 将模型拟合到inlier,同时忽略outlier

RANSAC可以解决以上问题,

比如我们要拟合的模型是一条直线,一条直线两个点就可以确定,因为我们需要随机采样两个点,如下图所示。
在这里插入图片描述
我们给一个阈值去评估这个模型,在这个阈值内,有4个点圈进来了。
在这里插入图片描述
如果换其他的采样点呢?
在这里插入图片描述

此时 ,有6个点圈进来了。
在这里插入图片描述
我们再一次迭代,
在这里插入图片描述
此时,有19个点圈进来了。

在这里插入图片描述
继续迭代。
在这里插入图片描述
此时,有13个点圈进来了。
在这里插入图片描述
在这几次迭代中 ,第三次圈进来的点数最多,我们可以认为第三次是目前这几次迭代中的最优解。
在这里插入图片描述
那么,整个算法流程如下,
在这里插入图片描述
那么,回到我们最开始的问题,我们怎么去选择迭代次数N呢?
在这里插入图片描述
注:outlier的概率e通常是一个先验值,P 是我们希望RANSAC得到正确模型的概率。如果事先不知道e的值,可以使用自适应迭代次数的方法。也就是一开始设定一个无穷大的迭代次数,然后每次更新模型参数估计的时候,用当前的outlier比值当成e来估算出迭代次数。

那么我们怎么去选择阈值呢? 通常来说,我们是根据经验去选择。好吧,确实太抽象了,这个就需要多做实验,多try。

4. RANSAC的优缺点

RANSAC的优点是它能鲁棒的估计模型参数。例如,它能从包含大量局外点的数据集中估计出高精度的参数。

RANSAC的缺点是它计算参数的迭代次数没有上限;如果设置迭代次数的上限,得到的结果可能不是最优的结果,甚至可能得到错误的结果。RANSAC只有一定的概率得到可信的模型,概率与迭代次数成正比。RANSAC的另一个缺点是它要求设置跟问题相关的阀值。

RANSAC只能从特定的数据集中估计出一个模型,如果存在两个(或多个)模型,RANSAC不能找到别的模型。

RANSAC可以对局外点进行剔除,这一点是比较好的,但它也并不是完美的,虽然缺点有这么多,但是我们还是离不开它。

5. 代码实现

PCL 库中以随机采样一致性算法( RANSAC) 为核心,实现了五种类似于RANSAC的随机参数估计算法,例如随机采样一致性估计(RANSAC ) 、最大似然一致性估计 (MLESAC ) 、最小中值方差一致性估计 ( LMEDS )等,所有的估计参数算法都符合一致性准则。利用RANSAC可以实现点云分割,目前 PCL 中支持的几何模型分割有 空间平面、直线、二维或三维圆、圆球、锥体等 。 RANSAC的另一应用就是点云的配准对的剔除。

下面用opencv进行代码示例。

#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/features2d/features2d.hpp>
void extracte_orb(cv::Mat input,std::vector<cv::KeyPoint> &keypoint,cv::Mat &descriptor){
    cv::Ptr<cv::ORB> f2d = cv::ORB::create(500);
    f2d->detect(input,keypoint);
    cv::Mat image_with_kp;
    f2d->compute(input,keypoint,descriptor);
    cv::drawKeypoints(input, keypoint, image_with_kp, cv::Scalar::all(-1),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    cv::imwrite("orb"+std::to_string(random())+".png",image_with_kp);
}

void match_two_image(cv::Mat image1,cv::Mat image2, std::vector<cv::KeyPoint> keypoint1,std::vector<cv::KeyPoint> keypoint2,cv::Mat descriptor1,cv::Mat descriptor2){
    cv::BFMatcher matcher(cv::NORM_HAMMING);
    std::vector<cv::DMatch> matches;
    matcher.match(descriptor1,descriptor2, matches);
    cv::Mat good_matches_image;
    cv::drawMatches(image1, keypoint1, image2, keypoint2,
                    matches, good_matches_image, cv::Scalar::all(-1), cv::Scalar::all(-1),
                    std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
    cv::imwrite("good_matches_image.png",good_matches_image);
    {
        std::vector <cv::KeyPoint> RAN_KP1, RAN_KP2;
        std::vector<cv::Point2f> keypoints1, keypoints2;
        for (int i = 0; i < matches.size(); i++) {
            keypoints1.push_back(keypoint1[matches[i].queryIdx].pt);
            keypoints2.push_back(keypoint2[matches[i].trainIdx].pt);
            RAN_KP1.push_back(keypoint1[matches[i].queryIdx]);
            RAN_KP2.push_back(keypoint2[matches[i].trainIdx]);
        }

        std::vector<uchar> RansacStatus;
        cv::findFundamentalMat(keypoints1, keypoints2, RansacStatus, cv::FM_RANSAC);
        std::vector <cv::KeyPoint> ransac_keypoints1, ransac_keypoints2;
        std::vector <cv::DMatch> ransac_matches;
        int index = 0;
        for (size_t i = 0; i < matches.size(); i++)
        {
            if (RansacStatus[i] != 0)
            {
                ransac_keypoints1.push_back(RAN_KP1[i]);
                ransac_keypoints2.push_back(RAN_KP2[i]);
                matches[i].queryIdx = index;
                matches[i].trainIdx = index;
                ransac_matches.push_back(matches[i]);
                index++;
            }
        }
        cv::Mat after_ransac_sift_match;
        cv::drawMatches(image1, ransac_keypoints1, image2, ransac_keypoints2,
                        ransac_matches, after_ransac_sift_match, cv::Scalar::all(-1), cv::Scalar::all(-1),
                        std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
        cv::imwrite("after_ransac_orb_match.png",after_ransac_sift_match);
    }
}

int main(int argc, char *argv[])
{
    cv::Mat image1 = cv::imread(argv[1]);
    cv::Mat image2 = cv::imread(argv[2]);
    std::vector<cv::KeyPoint> keypoint1,keypoint2;
    cv::Mat descriptor1, descriptor2;
    extracte_orb(image1,keypoint1,descriptor1);
    extracte_orb(image2,keypoint2,descriptor2);
    match_two_image(image1,image2,keypoint1,keypoint2,descriptor1,descriptor2);
    return 0;
}

OpenCV RANSAC的效果展示
在这里插入图片描述

6. 参考

Robust Estimation : RANSAC
04-随机采样一致性算法RANSAC
RANSAC

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

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

相关文章

【Unity】苹果(IOS)开发证书保姆级申请教程

前言 我们在使用xcode出包的时候&#xff0c;需要用到iOS证书(.p12)和描述文件(.mobileprovision) 开发证书及对应的描述文件用于开发阶段使用&#xff0c;可以直接将 App 安装到手机上&#xff0c;一个描述文件最多绑定100台测试设备 1.证书管理 进入网站Apple Developer &…

在ubuntu20.04下迁移anaconda的目录,试验不行后,换成软连接

一、原因 随着不断的搭建不同的算法环境&#xff0c;原本在固态硬盘上安装的anaconda上占用空间越来越多。导致可用的固态硬盘空间越来越少&#xff0c;又因安装的环境太多&#xff0c;重新搭建比较费时费力。有没有直接将当前已经搭建好环境的anaconda 迁移到另外的目录呢&…

程序员过了35岁没人要?“这行越老越香”

程序员35岁失业&#xff1f;参加完OceanBase开发者大会&#xff0c;我又悟了&#xff01; 周六参加了OceanBase2024 开发者大会的现场&#xff0c;来之前我其实挺忐忑的&#xff0c;我觉得一个数据库产品的发布会&#xff0c;能有什么新鲜的东西&#xff1f; 踏入酒店的那一刻&…

monorepo搭建记录

最终文件效果 1、准备环境 npm pnpm 2、创建文件夹npm init vite // 名字可以为main&#xff08;自定义&#xff09; 创建主应用main npm init vite // 名字可以为main monorepo下创建文件夹web 创建辅助应用 例&#xff1a;react-demo&#xff0c;具体步骤&#xff1a;1、 cd…

深入理解JavaScript:对象什么时候创建

&#x1f31f; 我们在chrome浏览器中debug程序。为了好debug我们会写一些在日常开发中基本不会采用的代码书写方式。 JavaScript中创建对象有3中方式&#xff1a; 1、对象字面量&#xff1b; 2、new&#xff1b; 3、Object.create(对象)&#xff1b; 1、使用new创建对象 fun…

10分钟揭秘摘要抽取:探索信息提炼的奥秘

Introduction 本文分别介绍摘要数据集&#xff0c;摘要分类&#xff0c;摘要架构&#xff0c;相关baseline。并介绍若干篇EMNLP2023论文&#xff0c;介绍其最新工作。 1.数据集 1.1 文本摘要 DUC/TAC 英文|数据集较小|适用于传统摘要方法的评估 Gigaword 英文|启发式规则构…

【数据结构与算法】8.二叉树的基本概念|前序遍历|中序遍历|后序遍历

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

【Linux】iptables的应用

iptables 防火墙 防火墙是一种网络安全系统&#xff0c;它位于内部网络与外部网络&#xff08;如互联网&#xff09;之间&#xff0c;通过实施预定义的安全策略来控制网络间的通信。防火墙的主要目标是保护内部网络资源免受未经授权的访问、攻击或潜在威胁&#xff0c;同时允…

经风靡全球的 PHP 为何逐渐失去优势?

TIOBE 编程语言人气指数发布更新&#xff0c;并提出“PHP 的魔力是否正在消散&#xff1f;”的灵魂拷问。今年 4 月&#xff0c;PHP 在 TIOBE 编程语言指数榜上仅位列第 17&#xff0c;“成为其有史以来的最低排位”。 暴露 PHP 人气急剧下滑的还不只是 TIOBE 榜单。在年度 Sta…

到底什么是爬虫

1. 引言 在数据驱动的世界里&#xff0c;网络爬虫&#xff08;Web Crawling&#xff09;技术扮演着获取和处理网上数据的关键角色。无论是为了数据分析、机器学习项目的数据集构建还是简单地监测网页变化&#xff0c;学习如何创建一个基本的网页爬虫可以大大提升你的工作效率和…

JAVASE基础语法(异常、常用类)

一、异常 1.1 什么是异常 异常就是指不正常。是指代码在运行过程中可能发生错误&#xff0c;导致程序无法正常运行。 package com.atguigu.exception;public class TestException {public static void main(String[] args) {int[] arr {1,2,3,4,5};System.out.println(&quo…

【c++】探究C++中的list:精彩的接口与仿真实现解密

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章来到list有关部分&#xff0c;这一部分函数与前面的类似&#xff0c;我们简单讲解&#xff0c;重难点在模拟实现时的迭代器有关实现 目录 1.List介绍2.接…

CSS3新增特性(二)

四、2D 转换 • 属性名&#xff1a;transform &#xff08;可用于制作2D转换&#xff0c;也可用于制作3D转转换&#xff1b;2D转换是平面上的转换&#xff0c;3D转换是在三维立体空间的转换&#xff09; • 作用&#xff1a;对元素进行水平或垂直方向的移动、缩放、旋转、拉长…

你们项目日志是如何处理的???

ELK日志采集系统 1.什么是ELK ELK 是一套流行的数据搜索、分析和可视化解决方案&#xff0c;由三个开源项目组成&#xff0c;每个项目的首字母合起来形成了“ELK”这一术语&#xff1a; Elasticsearch (ES): Elasticsearch 是一个基于 Apache Lucene 构建的分布式、实时搜索与…

【Python性能优化】list、array与set

list、array与set 详述测试代码 详述 本文对比 list 与 set 在插入和取值时的性能差异&#xff0c;以提供一条什么时候该选择什么数据类型的建议。先上结果&#xff1a; array 与 list 的不同&#xff1a; 内存方面 array 是 C array 的包装&#xff0c;它直接存储数据&#xf…

Vu3+QuaggaJs实现web页面识别条形码

一、什么是QuaggaJs QuaggaJS是一个基于JavaScript的开源图像识别库&#xff0c;可用于识别条形码。 QuaggaJs的作用主要体现在以下几个方面&#xff1a; 实时图像处理与识别&#xff1a;QuaggaJs是一款基于JavaScript的开源库&#xff0c;它允许在Web浏览器中实现实时的图像…

授权协议OAuth 2.0之通过OIDC实现SSO

写在前面 本文来一起看下OIDC&#xff08;openid connect&#xff09;相关内容。 1&#xff1a;什么是OIDC OIDC的全称是openid connect&#xff0c;和OAuth2.0一样&#xff0c;也是属于协议和规范的范畴。OAuth2.0是一种授权协议&#xff0c;即规定了what you can do的内容…

kafka部分partition的leader=-1修复方案整理

kafka部分partition的leader-1修复方案整理 1. 背景说明2. 修复测试2.1 创建正常的topic并验证生产和消费2.2 停止kafka模拟leader-12.3 修复parition2.4 修复完成验证生产消费是否恢复 3. 疑问和思考3.1 kafka在进行数据消费时&#xff0c;如果有partition的leader-1&#xff…

MySQL基础之多表操作(多表查询,事务,索引)

目录 一、多表关系1.1 一对多1.2 外键约束1.3 一对一1.4 多对多 二、多表查询2.1 测试数据准备2.2 笛卡尔积2.3 内连接2.4 外连接2.5 子查询1.标量子查询2.列子查询3.行子查询4.表子查询 三、事务3.1 问题场景引入3.2 概念3.3 事务操作3.4 事务的四大特性ACID 四、索引4.1 概念…

人工智能高速发展 苹果能否赶上AI热潮

近年来&#xff0c;人工智能高速发展&#xff0c;科技大佬们竞相布局人工智能赛道。苹果虽然“晚”了一步&#xff0c;但也在不断追赶这股热潮。随着AI技术不断发展&#xff0c;这项技术已经成为手机发展的新风向标&#xff0c;甚至2024年被视为AI手机元年。 众多手机厂商铆足了…