如何将LiDAR坐标系下的3D点投影到相机2D图像上
将激光雷达点云投影到相机图像上做数据层的前融合,或者把激光雷达坐标系下标注的物体点云的3d bbox投影到相机图像上画出来,都需要做点云3D点坐标到图像像素坐标的转换计算,也就是LiDAR 3D坐标转像素坐标。
看了网上一些文章都存在有错误或者把公式推导说的含混不清有误导人的地方(如果你完全按那些列出来的公式去计算,发现投影结果在图像上怎么都不对!),在此结合我经过验证是正确的代码详细解释一下,也供备忘,插图是对网上的原图做了正确修改的。
对于普通无畸变平面相机,坐标转换涉及到的主要是两个参数矩阵:用于激光雷达坐标系到相机坐标系转换的外参矩阵和用于相机坐标系到像素坐标系转换的相机本身的内参矩阵。
针孔相机模型下,相机坐标系下的三维空间中点P(X,Y,Z),对应在相机成像平面的图像坐标系(注意不是像素坐标系!)中的坐标点是p(x,y),焦距f是焦点到成像平面之间的距离,Z是P点到焦点的距离。

根据相似三角形原理,有:

由此可得,根据相机坐标系下的坐标(X,Y,Z)计算相机图像坐标系下的坐标(x,y)的计算公式:

注意,此处的x、y、f都是实际空间尺寸,单位一般是mm,如果将等式两边都除以图像每像素对应的实际尺寸,等式仍然成立,此时的x、y、和f就都是像素了,所以我们平时看到的数据集里camera的焦距值都是像素值,正是我们计算像素为单位的x和y所需的,然后,因为把图像坐标坐标系下的像素为单位的坐标(x,y)转像素坐标系下的坐标(u,v),只需加上图像横纵方向各自的偏移量cx和cy即可(因为图像坐标系的原点是在图像的中央,而像素坐标系的原点是在图像的左上顶点),另外相机的纵横方向的焦距稍有差异,不是同一个f值,所以还区分为fx和fy,至此,稍做推导可以得出相机坐标系下的坐标P(X,Y,Z)转换为图像上的像素坐标的计算公式为:

整理成矩阵运算形式就是:

其中相机内参矩阵就是:

将激光雷达坐标系下的3D点坐标转换到相机图像上的像素坐标的过程就是先将激光雷达坐标系下的3D坐标(Xw,Yw,Zw)(此处假设雷达不动或者我们只关注激光雷达坐标系下的坐标转换到像素坐标,(Xw,Yw,Zw)表示激光雷达坐标系下的坐标,此处的(Xw,Yw,Zw)不是激光雷达坐标而是导航用的世界坐标系下的坐标的话,下面的表示是错的,需要先用global2ego之类参数矩阵将坐标转换到雷达坐标系下),左乘以lidar2camera相机外参矩阵转换到相机坐标系下的3D坐标(Xc,Yc,Zc):

然后如上面所述使用相机本身的内参矩阵将相机坐标系下的3D坐标点转换到像素坐标系下的像素坐标,于是整个计算可以合并表示为(此处的fx、fy、Cx、Cy都是像素为单位的值!):

很多文章里列出连乘公式都漏了这个重要的
,导致计算出来的像素坐标根本不对(把像素坐标点在图像上画出来看图像上的点投影效果比较直观),包括一些BEV模型的实现代码都犯了这个错误,效果能好才怪!这个转换关系(K是相机内参矩阵)要记住:

这里的Z就是相机坐标系下的3D坐标(Xc,Yc,Zc)的Zc,上面倒数第三个公式里也说明了。那么我们代码实现时,对于普通无畸变平面相机,非常简单,就是 (1/Z) X 相机的内参矩阵 X 相机的外参矩阵(lidar2camera),即可由激光雷达下的3D坐标计算出像素坐标系下的坐标,用我写的经过验证是正确的C++代码作为示例,借助Eigen库实现非常简单:
Eigen::Vector4f pointVec;pointVec << point3D, 1.0;Eigen::Vector4f cam_point3D = K * extrinK * pointVec;Eigen::Vector3f point = cam_point3D.head<3>();float x = point.x();float y = point.y();float z = point.z();if (z < 1e-6) {return;}int u = static_cast<int>(x / z);int v = static_cast<int>(y / z);
上面K是4x4奇次相机内参矩阵,extrinK是4x4奇次外参矩阵,算出来的部分u,v值有的可能超出了相机图像的范围,用图像的cols和rows最大值最小值过滤一下就可以了,对于BEV模型,投影到6个相机的图像上使用各个相机各自的内外参矩阵依次做上述计算即可。
对于有畸变的平面相机,则不能使用内参和外参矩阵连乘,而是需要先左乘以外参矩阵把点云3D坐标转到相机坐标系下的坐标(Xc,Yc,Zc)后,把Zc小于等于0的坐标过滤掉,然后需要把Xc和Yc除以Zc,得到未校正的相机坐标系下的2D坐标(Xc/Zc,Yc/Zc),然后根据相机厂家给出的计算公式使用相机的畸变系数对此2D坐标做校正得出校正后的坐标(X,Y),再将此坐标扩展为奇次坐标后转置再左乘以相机的3x3原始内参矩阵,得出像素坐标(u,v),可以参考相机标定之畸变矫正与反畸变计算 - 达达MFZ - 博客园这篇文章给出的去畸变算法的实现,代码如果借助Eigen矩阵运算,可以写得更简洁点,不过他这样写的好处就是比较容易看清楚。车载森云相机的去畸变就是这样的算法,我们借助Eigen库自己实现的代码涉及到商业秘密就不列出来了。
多说一点就是,根据上面的计算公式:

假设我们知道每个像素的深度值Zc的话,可以由下面的公式计算出图像里每个像素对应的相机坐标系下的3D点坐标(Xc,Yc,Zc) :

这就是LSS模型预测每像素的深度后获得对应的3D点坐标的计算原理,从而实现2D到3D的对应转换,进而Splat成BEV。
相关文章:
如何将LiDAR坐标系下的3D点投影到相机2D图像上
将激光雷达点云投影到相机图像上做数据层的前融合,或者把激光雷达坐标系下标注的物体点云的3d bbox投影到相机图像上画出来,都需要做点云3D点坐标到图像像素坐标的转换计算,也就是LiDAR 3D坐标转像素坐标。 看了网上一些文章都存在有错误或者…...
JAVA就业笔记6——第二阶段(3)
课程须知 A类知识:工作和面试常用,代码必须要手敲,需要掌握。 B类知识:面试会问道,工作不常用,代码不需要手敲,理解能正确表达即可。 C类知识:工作和面试不常用,代码不…...
02.04、分割链表
02.04、[中等] 分割链表 1、题目描述 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 2、解题思路 本题要求将链表分隔…...
Excel 中根据患者的就诊时间标记病例为“初诊”或“复诊”
1. 假设: 患者表:包含患者的基本信息,如患者 ID 和患者姓名。 病例表:包含病例信息,如患者 ID、就诊时间和就诊状态。 2. 操作步骤: 合并数据: 确保病例表中有一列包含患者 ID,以…...
遇到“mfc100u.dll丢失”的系统错误要怎么处理?科学修复mfc100u.dll
遇到“mfc100u.dll丢失”的系统错误会非常麻烦,因为mfc100u.dll是Microsoft Visual C 2010 Redistributable Package的重要部分,许多应用程序和游戏在运行时都需要调用这个文件。如果这个文件缺失,可能会导致相关软件或游戏启动失败。面对这种…...
[Linux] 逐层深入理解文件系统 (1)—— 进程操作文件
标题:[Linux] 文件系统 (1)—— 进程操作文件 个人主页水墨不写bug (图片来源于网络) 目录 一、进程与打开的文件 二、文件的系统调用与库函数的关系 1.系统调用open() 三、内存中的文件描述符表 四、缓冲区…...
RT-Thread 互斥量的概念
目录 概述 1 互斥量定义 1.1 概念介绍 1.2 线程优先级翻转问题 2 互斥量管理 2.1 结构体定义 2.2 函数接口介绍 2.2.1 rt_mutex_create函数 2.2.2 rt_mutex_delete 函数 2.2.3 初始化和脱离互斥量 概述 本文主要介绍互斥量的概念,实现原理。还介绍RT-Thre…...
6.计算机网络_UDP
UDP的主要特点: 无连接,发送数据之前不需要建立连接。不保证可靠交付。面向报文。应用层给UDP报文后,UDP并不会抽象为一个一个的字节,而是整个报文一起发送。没有拥塞控制。网络拥堵时,发送端并不会降低发送速率。可以…...
Windows应急响蓝安服面试
Windows应急响应 蓝队溯源流程 学习Windows应急首先要站在攻击者的角度去学习一些权限维持和权限提升的方法.,文章中的方法其实和内网攻防笔记有类似l红队教你怎么利用 蓝队教你怎么排查 攻防一体,应急响应排查这些项目就可以 端口/服务/进程/后门文件都是为了权限维持,得到s…...
PCL 点云配准-4PCS算法(粗配准)
目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 加载点云数据 2.1.2 执行4PCS粗配准 2.1.3 可视化源点云、目标点云和配准结果 2.2完整代码 三、实现效果 3.1原始点云 3.2配准后点云 PCL点云算法汇总及实战案例汇总的目录地址链接…...
12、论文阅读:利用生成对抗网络实现无监督深度图像增强
Towards Unsupervised Deep Image Enhancement With Generative Adversarial Network 摘要介绍相关工作传统图像增强基于学习的图像增强 论文中提出的方法动机和目标网络架构损失函数1) 质量损失2) 保真损失3)身份损失4)Total Loss 实验 摘要 提高图像的…...
Axure重要元件三——中继器表单制作
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 本节课:中继器表单制作 课程内容:利用中继器制作表单 应用场景:台账、表单 案例展示: 步骤一:建立一个背景区…...
DMAIC赋能智能家居:解锁未来生活新篇章!
从清晨自动拉开的窗帘,到夜晚自动调暗的灯光,每一处细节都透露着科技的温度与智慧的光芒。而在这场智能革命的浪潮中,DMAIC(定义Define、测量Measure、分析Analyze、改进Improve、控制Control)作为六西格玛管理的核心方…...
代码随想录算法训练营第二天| 209.长度最小的子数组 59.螺旋矩阵II 区间和 开发商购买土地
209. 长度最小的子数组 题目: 给定一个包含正整数的数组 nums 和一个正整数 target ,找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 ,并返回其长度。如果不存在符合条件的子数组,返回 0。 示例: 示例 1…...
mysql隐藏索引
1. 什么是隐藏索引? 在 MySQL 8 中,隐藏索引(Invisible Indexes)是指一种特殊类型的索引,它并不真正被删除,而是被标记为“不可见”。当索引被标记为不可见时,查询优化器在生成查询计划时将忽略…...
etcd入门到实战
概述:本文将介绍etcd特性、使用场景、基本原理以及Linux环境下的实战操作 入门 什么是etcd? etcd是一个分布式键值存储数据库 关键字解析: 键值存储:存储协议是 key—value 的形式,类似于redis分布式:…...
Build an Android project and get a `.apk` file on a Debian 11 command line
You can build an Android project and get a .apk file on a Debian 11 command line without using Android Studio. The process involves using the Android SDK command-line tools (sdkmanager, adb, and gradle). Here’s a step-by-step guide to building the ???…...
解读 Java 经典巨著《Effective Java》90条编程法则,第4条:通过私有构造器强化不可实例化的能力
文章目录 【前言】欢迎订阅【解读《Effective Java》】系列专栏java.lang.Math 类的设计经验总结 【前言】欢迎订阅【解读《Effective Java》】系列专栏 《Effective Java》是 Java 开发领域的经典著作,作者 Joshua Bloch 以丰富的经验和深入的知识,全面…...
Vivado HLS学习
视频链接: 6课:数据类型的转换_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1bt41187RW?spm_id_from333.788.videopod.episodes&vd_sourcea75d5585c5297210add71187236ec90b&p6 目录 1.数据类型的转换 2.自动类型转换 2.1隐式数据转换 2.2…...
一款AutoXJS现代化美观的日志模块AxpLogger
简介 Axp Logger是一款基于autox.js的现代化日志模块,具备窗口事件穿透、拖拽和缩放功能。 Axp Logger文档 特性现代化的UI设计支持点击穿透模式(不影响脚本运行)监听音量-键切换模式支持窗口操作模式窗口拖拽移动窗口自由缩放清空日志关闭日…...
从5V电源到485通信:一个工业级PT100温度变送器的全链路DIY搭建实录
从5V电源到485通信:一个工业级PT100温度变送器的全链路DIY搭建实录 在工业自动化领域,温度监测的可靠性和精度往往直接关系到生产安全与质量控制。传统温度变送器虽然成熟稳定,但对于需要定制化功能或特殊安装环境的场景,自主搭建…...
告别CUDA!用OpenAI Triton写GPU Kernel,Python开发者也能玩转高性能计算
用Python解锁GPU算力:OpenAI Triton实战指南 当Python遇上GPU计算,传统路径总是绕不开CUDA C的陡峭学习曲线。但现在,OpenAI Triton正在改写这一规则——它让开发者能够用熟悉的Python语法编写高性能GPU内核,像操作NumPy数组一样自…...
OpenClaw+Qwen2.5-VL-7B学术助手:论文图表解析与摘要生成
OpenClawQwen2.5-VL-7B学术助手:论文图表解析与摘要生成 1. 为什么需要AI学术助手 作为一名经常需要阅读大量文献的研究人员,我长期被三个问题困扰:首先是PDF论文中的图表数据提取困难,手动转录既耗时又容易出错;其次…...
一键部署你的私人知识大脑:MindMap + Docker Compose 极速搭建指南
1. 为什么你需要一个私人知识大脑? 不知道你有没有这样的经历:电脑里存了几百个PDF文档,收藏夹里塞满了网页链接,手机相册里全是截图的知识点,但要用的时候死活找不到。更糟心的是,有些在线笔记工具突然收费…...
[特殊字符] 第72课:杨辉三角
想系统提升编程能力、查看更完整的学习路线,欢迎访问 AI Compass:https://github.com/tingaicompass/AI-Compass 仓库持续更新刷题题解、Python 基础和 AI 实战内容,适合想高效进阶的你。📖 第72课:杨辉三角模块:动态规划 | 难度:…...
别再死记硬背Payload了:用BUUCTF Basic靶场案例拆解漏洞利用的本质逻辑
别再死记硬背Payload了:用BUUCTF Basic靶场案例拆解漏洞利用的本质逻辑 在网络安全领域,很多学习者都会陷入一个误区:过度依赖现成的Payload和工具脚本,而忽视了漏洞利用背后的核心逻辑。这种学习方式虽然能快速解决特定题目&…...
解码AMD EPYC CPU命名规则:从数字到性能的全面解析
1. AMD EPYC CPU命名规则入门指南 第一次看到AMD EPYC处理器的型号时,我完全被那一串数字字母搞懵了。EPYC 7763、EPYC 75F3、EPYC 7313P...这些看似随机的组合其实藏着重要信息。就像汽车型号能看出排量和配置一样,EPYC的命名规则也能让我们快速判断处理…...
反向海淘商家必看!精细拍照服务,帮你降本留客不踩坑
做反向海淘生意的商家都懂,最头疼的莫过于用户投诉与跨境退货——海外用户担心货不对版不敢下单,下单后因实物与图片不符发起退货,高额跨境运费、人力成本,不仅压缩利润,还会拉低店铺口碑,甚至流失核心客群…...
Chat Smith 7.1.0 vs 原生ChatGPT:哪个更适合你的日常AI需求?
Chat Smith 7.1.0与原生ChatGPT深度评测:如何选择你的AI助手? 在AI助手遍地开花的今天,选择一款适合自己的工具就像在糖果店挑选最合口味的糖果——眼花缭乱却难以抉择。Chat Smith 7.1.0和原生ChatGPT无疑是当前最受关注的两款产品ÿ…...
三极管基极电阻设计与工程实践
1. 三极管基极电阻的必要性解析在电子电路设计中,三极管作为最基础的半导体器件之一,其基极电阻的配置往往被初学者忽视。实际上,这两个电阻(限流电阻和上拉/下拉电阻)的设计直接影响着电路的可靠性和稳定性。以常见的…...
