增强现实原理浅析

这是篇旧文了= =没有什么格式

增强现实这货,挺有意思,至少我这种准技术宅对它颇有好感。 想象你大爱的动漫人物啪的跳到你的桌子上,我找过一个初音跳舞的demo, 想象你带上类似google glass之类的东西,轻松导航,识人辫物, 想象你拿着摄像头对着女朋友,啪的长出两只猫耳朵,一个猫尾巴,(好恶趣味,机器人笔记里看到的),,,

总之不多想了,增强现实也可以说是混合现实,是将虚拟事物与现实事物的混合, 相对于沉浸式的完全虚拟世界的虚拟现实, 增强现实保留了现实这一元素,作为一种桥梁般的存在。

下面还是简称AR吧, 这些日子,研究了不少AR的开源库, ARToolKit/plus,ARTag,PTAM/M,BazAR,Mixare等等,好多Android上的AR OpenSource, 总结起来,大概分两类,一种是基于视觉的AR,一种是基于GPS及方向传感器的AR, 前者又可分为有模板的AR和无模板的AR, 有模板的AR又可分为固定模板AR和自定义模板AR, 呼,好多名词,不过都是我根据其特性起的名字,绝不是神马术语, 固定模板这种模式已经比较成熟了吧,大名鼎鼎的ARToolKit/plus,ARTag都是这方面的佼佼, 我没有通读它们的代码,因为光是各个坐标系变换的原理已经足够让我头疼,

我们尝试用最直白的思路来分析下这个事情, 首先我们考虑下基于固定模板AR要解决的问题是神马? 无非是,先有一张事先定义好的模板,可能是一张白纸上面画着固定图案, OK,我们把这张纸放到桌子上, 那么这个模板就定义了一个贴着桌子的平面,给平面加个z轴,就成了个三维坐标系, 我们拿着摄像头照向这个模板,我们期待此时会有一个3D模型活灵活现的出现在这个三维坐标系里, 以上,需求就说完了。

那么怎么做呢, 首先你摄像头照过去了,程序得先找到模板在哪,ok,这一步叫模板检测, 模板检测你当然希望快准狠, 所以你的模板设计的要好同时检测算法要好,这一点ARToolKit和ARTag各有千秋, ARTag在抗遮挡性上优于ARToolkit,后来的ARToolKitPlus采用的应该时间ARTag的方法。这个暂不详谈。 刚才提到那个三维坐标系,我们得知道原点在哪,不然,学过Opengl的孩子们都知道,没原点你上哪画模型去。 所以说,模板图案的设计得是有说道的,你不能是中心对称的吧, 要不然,我怎么区分你哪个位置是原点,哪个方向是X轴正方向,哪个方向是Y轴正方向。 ok,一旦你找到了模板,确定了原点,和坐标系各轴,也就确定世界坐标系了。 有了世界坐标系,我们就可以在世界坐标系里画3D模型来, 啊,对不起,不小心把自己当成上帝了,我们当然不能突然跃迁到上帝模式,在现实里面的那个模板上画东西, 作为程序猴,我们只能在我们的手持设备屏幕上创造模型, 于是这时候,我们需要变换, 即把你想在世界坐标系里的坐标变换成屏幕上的坐标, 这样你在这样的屏幕里画的东东,看起来就像是在世界坐标系里一样, 你可以把这种变换理解成程序猴由猴到伪上帝的进化, 从一个坐标系到另一个坐标系的变换,怎么做? 下面是线性代数的show time, 没错,想变成Gold - mode,你必须会线性代数,

首先,从世界坐标系到摄像机坐标系的变换, 需要矩阵[R|t],R是旋转矩阵,t是平移向量, 矩阵[R|t]怎么来的,稍后说, 若世界坐标系坐标为X,摄像机坐标系对应坐标为X’, 则X’ = [R|t]X, 接下来是摄像头坐标系到理想屏幕坐标系的变换, 这个时候有个叫摄像头内参矩阵的东东用的上,而上面那个R矩阵被称为外参矩阵, 我们叫它C, C包含摄像头焦距,比例因子,光心位置等信息, 为了不断思路我们还是暂不详述C的求取过程, 这实际上是摄像头定标过程, 比较好的描述可见OpenCV的教材, OK,我们用C[R|t]X便可得到理想屏幕坐标, 理想屏幕坐标系和现实屏幕坐标系之间由于光学畸变还有不同, 我们需要稍作变化,在此不谈, 总的来说就是,世界坐标系X到现实屏幕坐标系S,之间有那么一系列变换, 我们简单写成,S=TX。

好了,我尽量快而简洁的把,从模板所在坐标系到最终屏幕坐标系上的变换讲完了, 实际在程序里如何操作呢,我们如何获得[R|t]呢, 大致上就是已知模板几个关键点在世界坐标系中的坐标后, 先在摄像头捕获的帧里获得模板上对应关键点在屏幕坐标系中的坐标, 根据坐标通过求解线性方程组,便能得到初步的[R|t], 再利用非线性最小二乘法迭代求得一个最优变换矩阵[R|t], 这就是ARToolKit的做法。

我们再说下增强现实的其他的类型:

先从自定义模板AR说起,原理和固定模板AR大致相同, 差就差在模板的检测上,主要是通过检测特征点, 然后根据不同帧间相应特征点的位置变化求得[R|t]矩阵, BazAR便是这种类型的,其他的像下面这个链接,用很少的代码实现了这种类型AR的demo, http://www.morethantechnical.com/2010/11/10/20-lines-ar-in-opencv-wcode/ , 这个代码里用的是光流法进行特征点的跟踪, 下面这个链接的代码,用的是surf特征, http://morethantechnical.googlecode.com/svn/trunk/opencv_ar/, 不管怎么样除了特征不同,坐标系变换的原理都和上一篇文章类似, 在OpenCV中提供了一些函数可以轻松实现坐标系变换, 比如solvePnP和cvFindHomography。

接下来是无模板的AR,这个真的很赞,代表作是PTAM/M, 我还没有研究过,只是把代码运行起来在我的电脑上跑了demo, 效果怎么说呢,没有想象中的好,而且对摄像头要求较高, 为此我还特意买了个广角高清摄像头, 但无模板这个将是AR未来的发展方向,至少我这样认为, 我就不妄自胡咧咧PTAM的原理了 下面主要讲一下基于GPS和方向传感器的AR。

我是基于Mixare进行研究的, Mixare就是该类型AR的一个开源库, 大致的效果就是,开启摄像头,朝着不同的方向看去, 不同方向的景点或是神马地点就会浮现在摄像头View的上面, 点击上去之后会链接到相应地区的Wiki页面, 它获取的地理信息也是从Wiki上获取的, 其实这个效果,让人实在提不起太大兴趣, 显示地理位置的标签,完全是平铺在屏幕上, 各种覆盖,一点木有3D的感觉。。。 说到这,我好像还没有讲基于GPS和方向传感器的AR到底是神马, 这种AR,我的理解就是把真实世界的地理位置映射到一个3D模型或者说一个地图中, 然后把这个3D模型放到摄像头的View上, 然后当我不断改变手机的位置时, 根据手机中的方向传感器我们可以得到手机相对于真实世界地磁方向的位置, 由此来改变刚才那个3D模型在摄像头View中的位置, 好了,蹬蹬蹬蹬,百度手机地图中的AR实景模式,就是上面我说的这个原理, 想体验就去下百度手机地图吧, 话说我不是托,真的不是,只是他那个实景模式做的有点烂, 一来,非常难开启该功能,需要不断摸索,我是百度了一下才知道怎么开启该功能的。。。, 你说他们产品经理是多么不希望用户使用该功能啊, 二来,只支持竖屏手机,(虽然我的竖屏手机不知为啥启动不了该功能,) 但是横屏的平板用该功能时,整个坐标系就乱了,所以我推断它是只支持竖屏手机的, 这个倒也无可厚非,

好了不再说废话了, 总之这些天我把Mixare改的面目全非, 用OpenGL ES重写了他原来的界面显示及坐标变换, 改成了和百度手机地图上类似的效果吧, 原理我已经说了,具体的做法就是,

我从百度地图API中获取数据, 得到周围的一些位置信息,比如周围的银行,周围的旅店神马的, 同时我用GPS或者网络获得我此时的位置,并以我的位置为原点, 建立一个三维的坐标系,其他地理位置只是这个三维坐标系中XY平面上的点, 地磁北极方向作为Y轴正方向,东方作为X轴正方向,Z轴如倚天神剑直指天空, 这种感觉像神马, 创世第一天,我说这世界该有个坐标系,于是世界有了坐标系, 对就是这样,

接下来我用OpenGL ES画一个网格, 把刚才那个坐标系上的点映射到这个网格上, 所谓的映射无非就是把我得到的坐标都按着我画的网格大小除以一个scale, 让网格能装下就ok,

映射完之后要做的事,就是当你不断的改变手机的方向时, 那个网格会像你眼前看着的地图一样, 不断的跟随着你的视角发生变化, 做到这点很简单,通过Android的方向传感器API, 我们可以得到:


侧倾度(围绕 z 轴的旋转角)。这是指设备 y 轴与地磁北极间的夹角。例如,如果设备的 y 轴指向地磁北极则该值为 0,如果 y 轴指向南方则该值为 180。 同理,y 轴指向东方则该值为 90,而指向西方则该值为 270。 俯仰度(围绕 x 轴的旋转角)。当 z 轴的正值部分朝向 y 轴的正值部分旋转时,该值为正。 当 z 轴的正值部分朝向 y 轴的负值部分旋转时,该值为负。取值范围为 -180 度到 180 度。 翻滚度(围绕 y 轴的旋转角)。当 z 轴的正值部分朝向 x 轴的正值部分旋转时,该值为正。 当 z 轴的正值部分朝向 x 轴的负值部分旋转时,该值为负。取值范围为 -90 度到 90 度。


根据这些角度,我们对网格进行相应的旋转就OK, 效果就是,你的眼前有一个你正45度角俯视的平面网格,(这个角度可以自行设定) 然后你摄像头指向方向的地点都会浮现在该网格相应的位置,有远近区分, 当你转动摄像头,网格和上面的地点也会按相反方向转动, 接下来要做的是当你点击你网格上你感兴趣的地点时,会弹出其相关信息神马的, 这就是任意发挥了。

这个过程中我遇到2个问题,

  1. 是OpenGL ES显示文本问题
  2. 是OpenGL ES鼠标点选问题

恰好,Android的samples里面给出了很好的解答, 我用samples里面的LablesMarker解决了文本显示问题, 用samples 里面的Matrix托管解决了鼠标点选问题, 所以,我学会一件事,学习平台相关的东西,最好的方法就是读samples,

我鼠标点选的思路是把模型在三维坐标系上的坐标转换为屏幕坐标, 显示文字是把屏幕坐标传给lablesMarker,让其显示文字, 这个时候遇到了新的问题,就是在我视角之外的地点, 他们的坐标转换为屏幕坐标肯定是不正常的, 这些坐标总会胡乱的显示在当前屏幕上, 该如何解决呢?

这个时候笛卡尔救了我。。。 用二维解析几何! 我把我的屏幕看成是一条世界坐标系中XY平面上的直线, 设为kx-y=0, kx-y>0的点在直线的一侧, kx-y<0的点在直线的另一侧,

所以不在我摄像头朝向的那一侧的文字我显示,另一侧不显示, 就解决了这个问题。

OK,要讲的都讲了,我承认这不是神马普及贴, 根本不是原理浅析,完全像是编码手记, 不管怎么样,还算把最近的做的事情给总结了一下, 有空我会好好整理下的!= =

Published: December 15 2012