1、坐标系基础:理解世界坐标系、相机坐标系、图像坐标系与像素坐标系的关系

做接口适配,说白了就是在不同坐标系之间来回倒腾。你想想看,一个3D物体怎么变成屏幕上那个像素点?中间经历了什么?我刚开始接触这块时,也觉得不就是坐标变换嘛,但真正上手做项目才发现,这里面的坑一个接一个。

今天我们就把这四个坐标系彻底捋清楚。它们分别是:世界坐标系、相机坐标系、图像坐标系和像素坐标系。嗯,这四个家伙串起来,就是一条完整的成像流水线。

世界坐标系 (Xw, Yw, Zw) 相机坐标系 (Xc, Yc, Zc) 图像坐标系 (x, y) 像素坐标系 (u, v) 刚体变换 投影变换 仿射变换 3D世界 → 3D相机 → 2D图像 → 2D像素 旋转 + 平移 透视投影 缩放 + 平移 离散化

1.1 世界坐标系——一切物体的"老家"

世界坐标系,就是整个3D场景的绝对参考系。你可以把它想象成一个巨大的三维网格,场景里所有物体——相机、灯光、模型——都在这张网格里有一个固定的位置。

我个人习惯把世界坐标系的原点放在场景的中心,或者某个方便测量的角落。比如我做车载摄像头标定时,就把世界坐标系原点定在车辆后轴中心的地面上。这样后续算距离、算角度都方便。

关键点:世界坐标系是唯一的绝对坐标系。其他所有坐标系都是相对它定义的。单位通常是米或毫米,看你的应用场景。

1.2 相机坐标系——以"眼睛"为原点

相机坐标系,说白了就是把相机当作观察世界的原点。它的Z轴通常指向相机的前方(也就是镜头朝向的方向),X轴向右,Y轴向下(或者向上,取决于你用的约定)。

从世界坐标系到相机坐标系,需要做一次刚体变换。说白了就是旋转加平移。为什么?因为相机在世界里有一个位置和一个朝向,你得把整个场景"搬"到相机跟前,并且转成相机看世界的角度。

我记得有一次做AR项目,相机装在无人机上,世界坐标系是固定的,但无人机一直在飞、在转。每次都要重新算相机的外参矩阵,那个头疼啊……后来我写了个自动标定脚本,才把这活给自动化了。

小技巧:相机外参矩阵就是世界→相机的变换矩阵。它由旋转矩阵R(3×3)和平移向量t(3×1)组成。写成齐次形式就是4×4矩阵。

1.3 图像坐标系——从3D到2D的"投影"

图像坐标系,是相机坐标系里的3D点投影到成像平面后得到的2D坐标。单位是毫米,原点通常在图像的中心(也就是主点位置)。

这里用到的就是透视投影。你想想看,一个3D点(Xc, Yc, Zc)怎么变成2D点(x, y)?其实就是相似三角形原理:

x = f * Xc / Zc
y = f * Yc / Zc

其中f是焦距。嗯,就这么简单。但实际做的时候,镜头畸变会让这个关系变得复杂。我踩过这个坑——直接用针孔模型算投影,结果边缘区域偏差好几个像素,后来加了畸变校正才搞定。

注意:图像坐标系的单位是毫米,不是像素!很多新手在这里搞混。图像坐标系描述的是物理成像平面上的位置,跟传感器像素大小还没关系。

1.4 像素坐标系——最终"数字化"

像素坐标系,就是你在屏幕上看到的那个(u, v)坐标。原点通常在图像的左上角,u轴向右,v轴向下。单位是像素。

从图像坐标系到像素坐标系,做的是仿射变换。说白了就是缩放加平移:

u = x / dx + u0
v = y / dy + v0

其中dx和dy是每个像素在x和y方向上的物理尺寸(毫米/像素),(u0, v0)是主点的像素坐标。

这里有个细节:dx和dy不一定相等。我做工业相机标定时遇到过,有些相机的像素不是正方形,是矩形的。这时候如果不分别处理,算出来的坐标就会变形。

完整变换链:

世界坐标 (Xw, Yw, Zw) → 刚体变换 → 相机坐标 (Xc, Yc, Zc) → 透视投影 → 图像坐标 (x, y) → 仿射变换 → 像素坐标 (u, v)

这一整套变换,最终可以合并成一个3×4的投影矩阵P。你给它一个世界坐标,它直接给你像素坐标。

1.5 实战中的坑与经验

说了这么多理论,来点实际的。我在做多相机拼接项目时,遇到过这么几个问题:

  • 坐标系方向搞反:有的相机Y轴向上,有的Y轴向下。如果不统一,拼接出来的图像是倒的。我建议一开始就定好规范,比如统一用OpenCV的坐标系约定。
  • 单位不统一:世界坐标系用米,但标定板尺寸是毫米。算出来结果差1000倍,找了半天bug。后来我所有输入数据都强制转成同一单位。
  • 主点偏移:理论上主点在图像中心,但实际生产有偏差。我见过偏差10个像素的相机,如果不标定直接用理论值,投影误差会很大。

避坑指南:我曾经在标定相机时,发现重投影误差总是降不下去。折腾了两天,最后发现是标定板打印尺寸跟代码里设置的不一致。嗯,从那以后我每次标定前都会拿尺子量一遍标定板的实际尺寸。

1.6 小结

这四个坐标系的关系,说白了就是一条流水线:世界里的3D物体,经过相机"看"到它,投影到成像平面,最后变成屏幕上的像素。每一步都有对应的变换矩阵,搞清楚了这些矩阵,接口适配里的坐标变换问题就迎刃而解了。

下次你遇到坐标对不上的问题,别急着怀疑代码。先问问自己:我当前在哪个坐标系?目标坐标系是哪个?中间的变换矩阵写对了没有?八成问题就出在这里。

可可精品资料库