点击下方标题,阅读 Xtreme1《融合标注的映射原理与实践》系列的往期文章:
一
前言
平台对需要做融合标注的数据有一定格式要求,下面我们来详细地说明。
二
文件目录结构
数据可以以压缩包(.zip、.rar)的形式上传。
压缩包里的文件目录可以是多级目录,但包含 pcd 文件、2D 图像文件和相机参数的文件夹必须是下图的结构:

详细说明如下:
point_cloud 文件夹存放 pcd 文件(.pcd),camera_config 文件夹存放相机参数文件(.json),image 文件夹存放 2D 图像文件;
文件夹名称必须和上图一模一样(除了图片文件夹最后的编号);
一张 pcd 有几个方向的 2D 图像,就要创建几个 3d_img 文件夹,编号从 0 开始自增,并把图片分别放入这些文件夹;
这三种文件夹里的文件名需要一一对应(例如 3d_url 里的 abc.pcd 对应 3d_img0 里的 abc.jpg 对应 3d_img1 里的 abc.jpg 对应 camera_config 里的 abc.json)
缺少 3d_img 文件夹或者 camera_config 文件夹均会导致在标注平台无法显示 2D 图像。
连续帧目录结构:

同一连续帧的数据需要放在相同的"frame_series n"目录下,否则以离散帧进行展示。
三
相机参数格式
[{ //首个列表元素对应 image0 视角相机的参数"camera_internal": { //相机内参"fx": 933.4667,"fy": 934.6754,"cx": 896.4692,"cy": 507.3557},"width": 1920, //图像分辨率(可省略)"height": 1080,"camera_external": [ //外参0.15507753102020391,-0.98789957539112982,0.0023212743705054379,-17.735591210606444,0.0020841948660559892,-0.0020225262130809719,-0.99999578275084622,1.2557627497829358,0.9879001040107438,0.15508171500774312,0.0017453268516608449,-56.1919827741359,0.0,0.0,0.0,1.0]},{...},...}
1. 同一个pcd文件对应的2D图像的相机参数均存放在一个参数文件内,且要注意参数在列表中的索引顺序需要和2D图片文件夹相对应;
2. 内参的键叫 "camera_external", 值为一个字典,字典的键为 fx,fy,cx,cy,这四个量在第一期已经介绍过,注意键名不可更改;外参的键叫 "camera_external",值为一个列表,列表中的元素为 4×4 外参矩阵转为列表得到。
四
外参矩阵构造
很多时候我们的外参不是矩阵形式,或是已有的外参矩阵不符合平台要求,这些都需要后续做转换。
4.1 产生旋转矩阵
在整个外参中,一般表示平移变换的参数都是一个三维向量,主要的不同点在旋转变换部分。
4.1.1 旋转向量→旋转矩阵
旋转向量到旋转矩阵的转换用 Python 实现如下, 我们下面用绕 z 轴旋转 180 度举例。
第一个参数为旋转向量,第二个 degrees 参数,表示使用角度还是弧度,默认 False,弧度。
from scipy.spatial.transform import Rotation as RR.from_rotvec([0, 0, 180], degrees=True).as_matrix()
的数其实就是 0:
array([[-1.00000000e+00, -1.22460635e-16, 0.00000000e+00],[ 1.22460635e-16, -1.00000000e+00, 0.00000000e+00],[ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])
R.from_rotvec([0, 0, np.pi], degrees=False).as_matrix()
array([[-1.00000000e+00, -1.22460635e-16, 0.00000000e+00],[ 1.22460635e-16, -1.00000000e+00, 0.00000000e+00],[ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])

4.1.2 MRPs→旋转矩阵
MRPs 到旋转矩阵的转换用 Python 实现如下,我们继续用z 轴旋转 180 度举例。
R.from_mrp([0, 0, 1]).as_matrix()
array([[-1., 0., 0.],[ 0., -1., 0.],[ 0., 0., 1.]])
4.1.3 欧拉角→旋转矩阵
欧拉角到旋转矩阵的转换用 Python 实现如下,以动态欧拉角(内旋),先绕自身 X 轴旋转 90°,再绕自身 Y 轴旋转 90° 为例。
from scipy.spatial.transform import Rotation as Rimport numpy as npR.from_euler('XYZ', [np.pi/2, np.pi/2, 0], degrees=False).as_matrix()
array([[ 2.22044605e-16, 0.00000000e+00, 1.00000000e+00],[ 1.00000000e+00, 2.22044605e-16, -2.22044605e-16],[-2.22044605e-16, 1.00000000e+00, 0.00000000e+00]])
我们也可以自己验证,注意内旋时矩阵右乘。结果和上面一致:

4.1.4 四元数→旋转矩阵
四元数到旋转矩阵的转换用Python实现如下。
注意此时的四元数顺序为 x,y,z,w,其中 w 为实部。
from scipy.spatial.transform import Rotation as Rimport numpy as npR.from_quat([0, 0, 0, 1]).as_matrix()
结果当然是一个单位矩阵:
array([[1., 0., 0.],[0., 1., 0.],[0., 0., 1.]])
a = R.from_quat([1.5, 2.5, 3.5, 4.5]).as_matrix()b = R.from_quat([3.0, 5.0, 7.0, 9.0]).as_matrix()print(a == b)
结果:
[[ True True True][ True True True][ True True True]]
4.2 外参矩阵的注意事项
到了现在,我们手里已经有了外参矩阵,但仍要注意它是否满足以下的要求。
4.2.1 外参排列
参数文件里的外参矩阵是上期提到的格式,即旋转矩阵R和平移向量 t 左右排列

平台要求的外参是一个列表,相当于上面的元素如下排列:
4.2.2 映射方向
参数文件里的外参矩阵指的是雷达坐标系到相机坐标系的变换,不能反过来。
如果手里的外参对应相机坐标系到雷达坐标系的变换,那么在构造好外参矩阵后,可以取它的逆矩阵,Python 实现如下:
import numpy as npext_inv = np.linalg.inv(ext)

如果大家对 Xtreme1 感兴趣,欢迎关注 GitHub Repo:
https://github.com/basicai/xtreme1/

往期回顾





