【代码阅读】SSC:Semantic Scan Context for Large-Scale Place Recognition

一、主函数

官方开源的代码提供了四个主函数,其中eval_pair.cpp和eval_top1.cpp是一组,分别用于计算两帧的相似度分数以及一帧点云在所有的51帧点云中相似度最高的25帧的相似度分数。eval_seq.cpp是在eval_top1.cpp的基础上,给了一堆序列,遍历序列中的每一帧点云并输出与之相似度足够高的25帧点云的相似度分数。eval_angle.cpp这个文件比较迷,readme里面也没有提,代码里面提到的那几个config文件开源代码也没有给出,从代码内容来看,更像是在eval_pair.cpp的基础上,计算了好多帧对的位姿变换。

主函数的具体操作基本都是类似的,先初始化config对象读取配置文件,同时利用这个配置文件初始化ssc对象,之后根据不同的需求,调用重载了许多次的ssc对象里面的getScore函数,进行相似度分数的计算。因此整个ssc算法其实都在ssc对象中实现。

二、ssc.cpp

算法的实现基本都在这个文件中,文件的结构本身不复杂,除了构造函数和析构函数,还有三个形式的读取点云函数、投影函数、计算ssc描述子的函数、两个形式的全局ICP函数、计算相似度的函数以及六个形式的计算相似度分数的函数。
在这里插入图片描述

getScore()

根据主函数的执行顺序,这里我们也从getScore函数开始。从重载函数的形式看,六个getScore函数传入的参数本质上都是一样的:两帧点云、两帧点云的语义以及变换矩阵,不同之处在于传入的形式,一二个函数传入的是XYZL形式的点云,这是pcl库中一个点云形式,L表示的是label,相当于直接把语义信息存储了进去,三四个函数这是传入了点云文件的路径和语义推理结果的路径,在函数内部分别读取两个文件,最后两个函数则是直接读取标记过语义的点云文件。从执行的路径来看,最后面四个函数都是在调整文件格式之后,转而调用了前面两个函数,所以这里我们主要看第一个形式的getScore函数。

project()

进入函数后首先对位姿变化的量进行初始化,同时利用project函数对标记过语义的点云进行投影。与其叫做投影,其实更多地是在做格式的转换。

函数首先对场景划分了扇区,扇区只对轴向做了大小的限制,而没有对径向进行约束。从代码来看,一帧点云共计划分了360个扇区,也就是每个扇区在轴向上是一度,之后初始化一个360*1的向量,每个元素为一个32位的浮点数,初始值为0。
在这里插入图片描述
之后对于函数传入下采样后的点云filtered_pointcloud,遍历点云中的每个点,对类别标记为13、14、16、18、19的这五类点进行点云格式的转换。查看配置文件可以发现,选择的这五类对应的是building、fence、trunk、pole以及traffic-sign,而没有选择出现次数更多的ground等类别。对于水平距离足够远的点,算法会根据夹角信息计算其所在的扇区,之后将这个点到中心的距离、点在水平方向上的坐标以及该点的语义坐标进行存储。
在这里插入图片描述
这里就存在一个问题,代码183行到186行对点信息的存储,是直接覆盖的,而不是添加的,这就会导致当一个扇区内同时存在13、14、16、18、19这五类点的时候扇区的描述子实际上是多次覆盖过的。查看作者的原论文,里面写的是每个扇区内部只保留距离中心最近的点,个人推测这里的这种写法是作者默认了filtered_pointcloud是从远到近的排列,因为在之前看的salsanext代码中有对点云重排列的部分,这里作者并没有调用下采样相关的内容,可能是在输入的bin文件点云中已经调整过顺序。

globalICP()

回到getScore函数,在计算出ssc_dis之后,函数进入到globalICP进行一个位姿的计算。这里对应的是原论文中的全局语义ICP的部分。由于描述子的设计具有旋转不变性,所以代码首先利用描述子径向上的距离,计算了两帧点云描述子轴向上应该旋转的量。

具体来说其实就是一个遍历的过程,对于下面的这个二层循环,第一层的i表示的是轴向上应该移动的距离,单位是扇区数量,第二层则是遍历每个扇区,之后计算旋转后两个描述子第一维也就是到中心的距离的差,差值统计在dis_count这个量里面,取最小值对应的i作为两个描述子或者两帧点云在轴向上应该旋转的值。
在这里插入图片描述
得到角度之后,就可以将描述子进行旋转,让两个描述子对齐,描述子一共四个维度,其中第一个和最后一个是不用换的,只有第二和第三个的坐标需要调整,也就是根据正弦和余弦进行投影。
在这里插入图片描述
之后进行共计100轮次的迭代计算,这一次需要计算的是xy方向上面的平移。每一次迭代首先会取出一个合法扇区,根据先前计算出的径向旋转得到投影之后的位置,在这个位置左右各10个扇区共计20个扇区范围内进行检验。指标是扇区描述子中二三维度对应的点的几何距离。
在这里插入图片描述
对于当前扇区找到的最近点扇区,如果坐标偏差足够小则进行统计计算,对偏移量进行累计。
在这里插入图片描述
遍历完所有扇区之后,对所有的偏差取平均,作为当前这一次迭代计算中的偏差值,这个偏差值表示的是xy方向上面的偏移,直接对点云描述子进行校正,同时存储所有的偏差。
在这里插入图片描述
最终,diff_x、diff_y以及angle将会被作为两帧点云之间的位姿差异传递回主函数。从这整个过程来看,globalICP这个函数就是将位姿变化拆分为旋转和平移进行计算,旋转依赖的是扇区描述子的旋转不变性,通过最小化轴向距离差值来找到最优的径向旋转偏差,而平移则是依靠得到的旋转结果进行调整,在计算平移时则不利用径向距离,而是利用扇区之间点的几何距离来调优,迭代100轮以完成平移的计算。这里说一下个人感觉不足的地方,在整个的计算过程中点云被十分过分地简化,按照代码默认的配置文件,径向上划分了360个扇区,就算不考虑语义类别,一帧点云顶天也就只保留了360个点用于构建这个描述子,从数量上来看简化的有些过分。另外,在计算的过程中,旋转和平移被完全拆分开来,先只计算旋转,计算平移时则固定旋转,这种方法未必得到的就是最优的结果,平移和旋转应该作为整体共同进行调整,而没见过这种拆分固定的计算策略。最后,扇区的划分很大程度上会影响结果的准确性,由于选择扇区的点的时候只考虑了到中心点的距离这一个指标,而在径向上完全不做考虑,这就有可能导致一个扇区对应的1度范围内,0.0001度的点和0.9999度的点进行了对齐,有可能导致径向上1度的偏差,而在固定旋转调整平移的过程中更有可能错上加错,所以这一策略或者是开源的代码本身其实是有较大的不足的。

calculateSSC()

回到getScore函数,globalICP计算出的位姿变化会被存储在diff_x、diff_y以及angle中,利用这个变化关系将点云进行投影,之后调用calculateSSC函数进行ssc描述子的计算。这个函数要简单的多,首先初始化径向和轴向的步长,得到一个二维的矩阵用于存放描述子,矩阵的每个位置存放的都是一个8位无符号数。
在这里插入图片描述
之后遍历点云内的每个点,对于需要统计的类别的点,确定其在二维矩阵中的位置。原论文提到了写在描述子中的类别是根据优先级选择的,从代码来看,这个优先级就是提前写死在order_vec里面了。在确定好的位置中,如果已经写入了,就根据优先级进行判断,选择覆盖或者保持不变。
在这里插入图片描述

calculateSim()

完成ssc描述子的计算后,就会调用calculateSim函数计算相似度,相似度的计算也很简单,就是比较相同位置上的语义是否相同,最终统计相同的占总合法位置中的比例,这个比例会作为相似度分数返回给主函数。
在这里插入图片描述

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

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

相关文章

PointPillars安装

PointPillars使⽤Pillar Feature Net (PFN)将原始点云数据转换为伪图像(pseudo-image)。 以KITTI的激光 雷达坐标系为例,若输入点云的截取范围[x_min, y_min, z_min, x_max, y_max, z_max]为[0, -39.68, -3, 69.12, 39.68, 1], 且每个pillar…

暑期工作闭坑指南

**暑期工作闭坑指南** 随着暑期的到来,许多学生都会选择利用这段时间进行实习或兼职工作,以增加实践经验、提升个人能力,并赚取一定的收入。然而,在寻找暑期工作时,往往会遇到一些陷阱和风险。为了帮助大家避开这些坑…

must be built with the ios 17 sdk or later,included in Xcode 15 or later.

2024.4.29 号开始,苹果又开始搞开发者了。 Xcode - 支持 - Apple Developer xcode可以从这里下载, Sign In - Apple 电脑不支持,头疼,必须 macOS Ventura 13.5 或以上才能支持。 电脑哪里搞,再买一台吗? 用…

图解布隆过滤器(Bloom Filter)

布隆过滤器详解 布隆过滤器(Bloom Filter)是一种空间效率非常高的随机数据结构,由布隆(Burton Howard Bloom)在1970年提出,用于检验一个元素是否属于一个集合。 具体来说,它可能会错误地认为一个…

java读取txt,doc,docx文档格式的文本内容

读取txt,doc,docx文档格式的文本内容,通过不同格式,读取逻辑不同,避免造成文本内容乱码问题, 这里需要安装Maven: 版本最好统一 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</v…

Jmeter插件管理器,websocket协议,Jmeter连接数据库,测试报告的查看

目录 1、Jmeter插件管理器 1、Jmeter插件管理器用处&#xff1a;Jmeter发展并产生大量优秀的插件&#xff0c;比如取样器、性能监控的插件工具等。但要安装这些优秀的插件&#xff0c;需要先安装插件管理器。 2、插件的下载&#xff0c;从Availabale Plugins中选择&#xff…

day 65 图论part02 99.岛屿数量 深搜 99.岛屿数量 广搜 100.岛屿的最大面积

99.岛屿数量 深搜 每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 本题思路&#xff0c;是用遇到一个没有遍历过的节点陆地&#xff0c;计数器就加一&#xff0c;然后把该节点陆地所能遍历到的陆地都标记上。 在遇到标记过的陆地节点和海洋节点的时候直接跳过…

Vue3 响应式 reactive全家桶

reactive()与ref()的区别和连续 ref和reactive都是把变量变成响应式对象 reactive底层使用了泛型约束&#xff0c;只能对引用类型使用。 而ref支持所有的类型&#xff0c;ref取值和赋值都需要 .value 而reactive 不需要 对象 <template><div><form><…

【运维】在 Docker 容器中指定 UTF-8 编码:方法与技巧

在 Docker 容器中指定 UTF-8 编码&#xff1a;方法与技巧 在日常开发中&#xff0c;我们常常需要确保应用程序能正确处理各种字符编码&#xff0c;尤其是 UTF-8 编码。在 Docker 容器中运行应用程序时&#xff0c;正确设置字符编码尤为重要&#xff0c;因为容器通常是跨平台、…

达梦(DM8)数据库表空间的备份与还原(联机备份) 四

一、表空间的备份 1、备份表空间的命令操作 backup tablespace main backupset /home/dmdba/dmdata/DAMENG/bak/full_back_01 ; 2、检查表空间的备份文件 select sf_bakset_check(disk,/home/dmdba/dmdata/DAMENG/bak/full_back_01); 二、表空间的还原 1、修改表空间位脱机…

AI播客下载:Edge of AI (AI最新应用场景和发展趋势)

Edge of AI Podcast 是一个探讨人工智能&#xff08;AI&#xff09;最新应用和发展的播客。该播客由Ron Levy主持&#xff0c;他于2023年8月9日接任新主持人。每个节目都会邀请专家分享他们在AI领域的见解和经验&#xff0c;探索最新的应用场景和发展趋势。例如&#xff0c;在第…

PointCloudLib 法线微分算法(Don)分割点云 C++版本

0.实现效果 原始点云 不同尺度上计算法向量 计算出don特征量,并以颜色显示在点云上 可以看出平面中心的点 不涉及到周围点变化的点 特征量比较低,以红色表示 边缘过渡剧烈的点,特征量比较高,以蓝色表示 以Don算法特征量来作为阈值分割点云,可以分割出剧烈变化的点云 欧…

《Linux Vim急救手册:E138错误全攻略与故障排除秘籍》

标题&#xff1a;《Linux Vim急救手册&#xff1a;E138错误全攻略与故障排除秘籍》 引言 在Linux世界中&#xff0c;Vim是编辑器的瑞士军刀&#xff0c;以其轻量级、强大和灵活著称。然而&#xff0c;即使是最熟练的Vim用户也可能遇到令人头疼的E138错误。本文将深入探讨Vim …

国标GB28181视频汇聚平台EasyCVR设备展示数量和显示条数不符的原因排查与解决

国标GB28181/GA/T1400协议/安防综合管理系统EasyCVR视频汇聚平台能在复杂的网络环境中&#xff0c;将前端设备统一集中接入与汇聚管理。智慧安防/视频存储/视频监控/视频汇聚EasyCVR平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级…

WDF驱动开发-特定于KMDF的技术(三)

支持特殊文件 特殊文件 包括分页文件、转储文件和休眠文件。 如果驱动程序的目标设备是系统可能用于这些文件的存储设备&#xff0c;则驱动程序必须执行以下操作&#xff1a; 调用 WdfDeviceSetSpecialFileSupport 以启用或禁用对每种类型的特殊文件的支持。 默认情况下禁用每…

Django教程(002):模板语法的使用

目录 1 字符串2 列表3 字典4 列表中是字典5 if语句6 案例&#xff1a;使用爬虫爬取联通新闻并在页面渲染 模板语法本质上&#xff1a;在HTML中写一些占位符&#xff0c;由数据对这些占位符进行替换和处理。模板语法主要是方便对方法返回的数据在前端进行渲染&#xff0c;这些数…

什么野指针(c++)

野指针定义 野指针&#xff08;Wild Pointer&#xff09;是指向不确定位置或者非法地址的指针。当一个指针指向的内存被释放后&#xff0c;如果没有将其设置为NULL&#xff0c;那么这个指针就变成了野指针。使用野指针会导致未定义行为&#xff0c;可能引发程序崩溃或数据损坏…

【Proteus仿真】【Arduino单片机】寻迹避障蓝牙遥控小车

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使LCD1602液晶&#xff0c;L298电机&#xff0c;直流电机&#xff0c;HC05/06蓝牙模块&#xff0c;HCSR04超声波&#xff0c;红外寻迹模块等。 主…

grafana Not saving new dashboard due to restricted database access

报错 grafana 容器日志报错 logger=provisioning.dashboard t=2024-06-21T07:40:54.307719612Z level=warn msg="dashboards provisioning provider has no database write permissions because of duplicates" provider=default orgId=1

数据结构与算法-【算法专项】Hash算法-2(HashMap+设计Hash+Hash应用+Hashmap常用方法)

数据结构与算法-Hash算法-2 5 HashMap&#xff08;又双叒叕提到红黑树动态扩容&#xff09;6 如何设计Hash7 Hash的应用8 HashMap源码&#xff08;又双叒叕&#xff09;构造方法&#xff1a;put方法&#xff1a;get方法&#xff1a;equals方法&#xff1a;containsKey方法&…