1. 引言
笔者认为现阶段AR技术的应用是还是比较坑爹的,大都是噱头多但是实用的成分少,拿出来做做DEMO是可以,但是难以在实际的项目中落地产生实际的经济价值。一方面是很难在业务上难以找到合适的应用场景(可能管线相关的项目算一个),另一方面技术上也存在一些难以突破的问题。不管是手持设备还是AR眼镜,这些比较适合AR的硬件性能还是太弱了,导致其重建的空间信息与现实部分的空间信息存在较大的差距,这样的话就谈不上对现实的增强了。
不过笔者最近遇到了一个高空视频全景AR的项目,感觉具有不错的应用价值。具体可以参看这个案例:【数智实践】AR云景全局掌控,为园区装上"智慧之眼",其中一张操作演示的的动态图如下:

这个案例看起来与我们经常碰到的AR应用有所不同,不过笔者认为尽管这个案例可能没有用到像AR Core/AR Kit这样的AR组件,也不是这些组件提供的功能点,但也是AR技术的应用案例。所谓AR(Augmented Reality,增强现实),指的就是虚实结合,以达到对现实增强的效果。在这个案例中,"实"的部分就是高空摄像头视频画面,"虚"的部分就是点的地理位置标注;更关键的地方在于,这些注记随着摄像头画面的移动和缩放而移动,并不是固定在某个像素位置。从GIS的角度上来看,这似乎在操作一张地图,这张地图还是动态的。
在AR Core中,像这样的功能被称为运动追踪,通常通过SLAM(simultaneous localization and mapping,实时定位和地图构建)技术来实现。SLAM主要解决这样的问题:一个机器人在未知环境中,通过计算机视觉等技术让机器人知道自己的具体位置,进而描绘出此环境完全的地图。但是笔者认为这个案例中应该用不到这种比较复杂的技术,基础的GIS+测绘+计算机视觉技术足应该以实现这种效果。
2. 详论
需要实现声明的是,由于高空视频全景AR的案例需要很多硬件上的操作和支持,笔者并没有条件具体实现其中的功能,更多的是从理论上探究其中的实现原理,如果哪里说的不太对,也请读者进行斧正。
2.1 静止画面
首先将这个问题简化成最基本的场景,也就是无操作画面静止的情况(不是画面中的视频不会动,指的是拍摄的位置不会变化)。在这种情况下,注记实际代表的地理位置(物方坐标)是不会变化的,像素的位置(相方坐标)也不会变化,那么我们只需要关心注记的位置如何投射到视频画面中。很显然,这个过程与相机标定的过程非常相似,已知一组点的物方坐标和像方坐标,那么可以标定出相机的内参和外参。具体可以参考笔者的这篇文章《一次实践:给自己的手机摄像头进行相机标定》。
在这个案例中,物方坐标可以通过GPS采集得到。不过GPS采集的点是WGS84地理坐标系的点。WGS84地理坐标系坐标是经纬度坐标,需要将其转换成笛卡尔坐标,也就是ECEF(地心地固直角坐标系)坐标。ECEF坐标的值很大,最好进一步将其转换成ENU(东北天站心坐标系)坐标,具体可参看笔者这篇文章《地心地固坐标系(ECEF)与站心坐标系(ENU)的转换》。理论上使用投影坐标系也可以,不过笔者认为还是ENU坐标系更加严密一点。另一方面,像方坐标可以在视频画面中刺点得到。由于获取像方点比较容易有误差,因此最好选取一些有明显特征的位置作为控制点来获取物方坐标和像方坐标。
经过笔者验证,即使获取了一些控制点的物方坐标和像方坐标,仍然不能像《一次实践:给自己的手机摄像头进行相机标定》这篇文章一样直接使用calibrateCamera
接口计算出摄像头的内参和外参。这个接口要求对于非平面标定,需要一个初始的相机内参矩阵作为输入。因为标定解算是一个非线性方程组迭代求解的过程,初始值差距太大就有很大概率求解失败。因此,内参不能在这里进行联合解算,还是要先进行预先解算,将摄像头的内参标定好。
在获取正确的内参之后,这个问题就简化成了PnP(Perspective-n-Points)问题:通过世界空间的一组点(最少3个)的物方坐标和像方坐标,计算出摄像头的外参(位置+姿态)。这个问题可以通过OpenCV的cv::solvePnP
接口来解决。实际上,这个过程就是《摄影测量学》中的后方交会。通过摄像机内参加上一组点的物方坐标和像方坐标,可以得到摄像机的外参,通常是三个旋转参数,三个平移参数。
总结下来就是,对于不用操作摄像头的静止画面,需要预先标定一次内参,然后通过实际的控制点标定一次外参。
2.2 缩放画面
摄像头静止不动的单幅画面比较好解决,但现在即使是一个普通的监控摄像头,也具备调焦和旋转角度的能力,当进行这些操作的时候,画面的注记是如何随着摄像头的动作而移动呢?
如果我们知道一点三维可视化的知识就知道,三维场景中如果要放大一个区域,有两种方式,一种是移动摄像头,将其与物体靠近;另外一种就是调整摄像头的焦距。调整焦距更加符合现实摄像头的操作,一般来说,将焦距调的越长,能看清的场景就可以越远,视场角也就会越小,就会产生画面放大的效果。基于这个思想,我们只需要实时获取在画面缩放时的焦距,加上其他内参和外参,就可以解算出注记点物方坐标对应的相方坐标,并在画面上做出调整即可。
话是这样说,不过另外一个问题在于前面我们说到摄像头的包括焦距的内参是通过相机标定的操作来实现的。我们要知道摄像头执行了调焦操作,必须使用接入摄像头对应的SDK,SDK会传出具体的焦距吗?传出的焦距与我们标定的焦距不一致该怎么办?对于专业相机,可能会提前将相机标定做好,会在SDK中传出具体的内参包括焦距值。但是对于普通相机,可能就还是需要自己进行标定了。
根据笔者的设想,调整焦距的操作并不是线性调整而是调整到几个固定的值,例如相机参数中经常可以看到的x0.5,x1.0,x2.0,x4.0这些值。也就是说,我们只需要在这些具体的调焦值时候进行相机标定,得到具体的摄像头内参;然后通过SDK获取这些调焦值,将其与摄像头内参关联上,就可以解算出正确的相方坐标,从而实现注记随缩放的动态移动。
总结:对于进行实时调焦的摄像头,要保证视频画面中的注记随着画面缩放而移动,需要预先进行有限次数的内参标定,然后通过实际的控制点标定一次外参。
2.3 缩放+旋转
内参的问题比较好解决,最多预先在不同情况下标定内参的值就可以了。但是如果要实现旋转就有点麻烦了,因为旋转参数是外参,是无法事先进行标定的。另外,旋转参数的获取也很麻烦,要知道摄像头旋转了必须通过摄像头SDK来获取,但是SDK获取的就一定的是我们想要的旋转参数吗?甚至我很怀疑是否能提供旋转参数。
在接着进行论述之前,笔者需要先介绍一下线性代数的理论知识。在线性代数的观点下,矩阵其实就是向量的张成空间。例如旋转变换决定的旋转矩阵,它的三个列向量这个三维坐标空间的三个基向量也就是基向量。旋转矩阵可以看成是这样的一个三维空间,一个X轴(1,0,0)、Y轴(0,1,0)、Z轴(0,0,1)标准笛卡尔坐标系经过旋转变换后形成的坐标系空间,旋转矩阵的三个列向量X、Y、Z轴的轴向量。
为了实时知道摄像头是否旋转了以及具体的旋转量,我们假设摄像头SDK一定会提供旋转参数,通常是是类似于"左右旋转-160度到+160度,上下旋转-75度到+75度"之间这样描述的数值。虽然我们并不知道这个摄像头给出的旋转决定的三维空间坐标系(相机坐标系)是如何描述的,但是我们可以将其复位到单位矩阵,也就是"左右旋转0度,上下旋转0度的位置"。在这个姿态下标定一次外参,令这个外参决定的旋转矩阵为\(R_a\)。接下来,摄像头旋转到任意位置的旋转参数都可以基于这个\(R_a\)来计算,因为在这种情况下,相当于是从世界坐标系通过旋转变换到了相机坐标系,世界坐标系下的\(R_a\)就是相机坐标系的单位矩阵。
如果"左右旋转X度,上下旋转Y度的位置"得到了一个旋转矩阵为\(R_b\),那么摄像头在任意旋转姿态的旋转矩阵就是:
\[R = R_b \cdot R_a \]
注意这里使用的右乘,先变换的变换在矩阵级联的右边。并且忽略了一些细节,比如左右手坐标系,坐标系的X、Y、Z轴的轴对应关系。
总结:对于摄像头缩放+旋转的情况,需要预先进行有限次数的内参标定,然后通过实际的控制点在相机坐标系原点位置标定一次相对外参标定,从而得到任意姿态的绝对外参。
2.3 移动+旋转+缩放画面
虽然笔者讨论了只存在摄像头旋转的情况,但是笔者很怀疑一般的相机摄像头是不是能做到只旋转而不移动位置。传感器是非常精密的设备,即使从外观上看摄像机是旋转了角度,但是很可能传感器的成像中心已经产生了偏移。其实高空摄像头可能会搭配专门的移动和旋转的装置,也就是所谓的"云台",可以通过电机驱动实现摄像头的移动、旋转、倾斜和俯仰。这样的话,外参的平移量也需要我们自己进行标定了。
我们还是假设高空云台相机的SDK能提供旋转参数和平移参数,因为毕竟你要知道摄像头动起来,就必须从摄像头的SDK获取参数。不过很显然了,从摄像头的SDK获取参数不是摄像头真正的外参,这里的外参应该是相对于整个高空云台相机局部坐标系的参数。在这种情况与只旋转的解法也比较相似,只不过计算要复杂一点。在平移参数为0,旋转参数为0的位置标定外参,假设得到的结果是平移量决定的平移矩阵\(T_a\),旋转量决定的旋转矩阵\(R_a\)。之后通过云台移动相机,得到相对平移矩阵\(T_b\),相对旋转矩阵\(R_b\),那么摄像头在任意位置姿态的外参矩阵就是:
\[M ={T_b} \cdot {R_b} \cdot {T_a} \cdot {R_a} \]
其实这里有个问题,这里SDK提供的相对外参\(T_b\)和\(R_b\)怎么来的呢?其实也是要通过预先标定得到,不过相对外参的标定就有点复杂了,有时间再研究一下。
总结:对于摄像头移动+旋转+缩放的情况,需要预先进行有限次数的内参标定,然后通过实际的控制点在高空云台相机局部坐标系的原点位置标定一次相对外参标定,从而得到任意位态的绝对外参。
3. 总结
从本文提供的解决思路可以看到,这个技术方案与摄像头硬件的关系很大,不是纯软件能够搞定的事情。因此最好能使用专业的工业级相机与计算机视觉相机,能够通过SDK提供的API动态获取这些内参和外参,它们分别是:
- 内参:焦距、主点、像素尺寸、畸变系数。
- 外参:三个位置参数,三个姿态参数。通常是相对于局部的相机坐标系的外参。
如果是普通相机,需要做的工作就多了,主要是:
- 进行内参和外参标定。
- 开发SDK输出标定的内参和外参。
- 项目部署前在局部的相机坐标系的原点位置,通过一组控制点进行一次相对外参标定。
- 得到任意位态的绝对外参,加上内参实时计算出任意注记点的像素坐标。