前段时间合伙人想做一个早教类的 AR 项目,并且扔给了我一个小册子:
大概的功能是:
折腾了一段时间,基本做完了上述功能,可以在这里看到演示的效果。
整个项目从第一次提交代码,到最后出演示效果,花了两天的时间,看了下 git commit 累计花了 25 小时(主要是炒股浪费了不少时间,也浪费了不少钱,此处略过不谈)。
看上去最终的开发时间不多,不过前面还是做了不少功课,在此简单的记录一下。
文章中不会涉及任何工具的具体使用过程,所有的基础操作在官方文档里都有详细的讲解。
正式开发之前,先做了一些准备工作,主要是搜索 AR 相关的功能,看看有哪些工具可供使用。一番调研对比和测试之后整理了以下待选方案:
在正式开发之前,先使用 OpenCV for Python 开发图像识别原型,看看这个项目好不好搞。
在 Jupyter Notebook 里开发图像识别项目真是一种非常流畅的体验:
加载图像之后,只需要在新的 Cell 里写图像识别相关的算法就可以了,图像数据已经被加载到了内存里,不需要每次运行都执行全部脚本。然后 OpenCV 进行图像处理, numpy 进行像素运算, matplotlib 展示图像,一条龙服务,十分方便。
具体的图像识别算法不再赘述,上一篇《 使用 OpenCV 识别 QRCode 》里基本都已包含。
搞定了积木识别之后,接下来就是做 AR 功能,主要有两个方案可供选择: iOS App 或者 Unity3D 。
先用 iOS App 试一下效果。
Vuforia 官方的 Sample Code 里已经包含了一个可以完整运行的项目,可以下载体验一下。然后用 pod 'OpenCV'
就能装好 OpenCV for C++ ,基本的开发环境就齐全了。折腾了一段时间之后我决定放弃使用 iOS App 的方案,因为实在是太繁琐了。
首先需要一个 ARSession
对象来管理 AR 的相关事务,里面包括了视频的渲染(需要等比拉伸并裁切之后渲染在屏幕上)、资源的回收和处理(比如手机退到后台)、线程切换(绘图需要在主线程)等等;然后需要一个 ARViewController
对象来负责具体页面的显示,里面包括基础资源的加载、识别模型的激活与切换、设备相关的事件监听等等;然后需要一个 ARImageTargetGLView
来渲染 AR 场景,包括 buffer 的维护、model 的加载、shader 的渲染等等。
而最让我崩溃的,是 Swift、Objective-C、C++ 的混写,项目中大量这样的代码:
[self setFramebuffer];
glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)buildingModel.vertices);
glEnableVertexAttribArray(textureCoordHandle);
if (offTargetTrackingEnabled) {
glBindTexture(GL_TEXTURE_2D, augmentationTexture[3].textureID);
} else {
glBindTexture(GL_TEXTURE_2D, augmentationTexture[targetIndex].textureID);
}
glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE, (const GLfloat*)&modelViewProjection.data[0]);
glUniform1i(texSampler2DHandle, 0);
glDisableVertexAttribArray(textureCoordHandle);
最后发现写了上千行代码,几十个文件,才只是搭建好了一个基础的 AR 开发环境,还不包括自定义的 3D 效果和自己的业务代码,细思恐极,赶紧放弃。
接下来就是转投 Unity3D 的怀抱。
遇到的第一个问题是如何集成 OpenCV ,通过买买买这个 OpenCV for Unity 插件解决了,它是基于 OpenCV for Java 的,所以接口和 Python C++ 相比略有些变化,不过基本是相同的。在整合 OpenCV 和 Vuforia 的时候看到了 OpenCV for Unity 开发团队的 Voforia with OpenCV for Unity Sample 这个示例项目,再结合自带的 Samples 文件基本就没问题了。
遇到的第二个问题是 Unity3D 中如何处理项目文件的问题,在 iOS 项目里我的项目目录是这样的:
在 Unity3D 里,由于项目里不止是代码文件,还包括 fbx 之类的模型文件、 mat 等材质文件、prefab 等预设文件、unity 等场景文件,如果都放在一起十分混乱,很难检索。
参照 iOS 的项目目录,现在 Unity3D 里的项目里是这样安排的:
基本上单个场景的目录结构和 General 的目录结构是一致的, General 像是所有场景的『基类』。
遇到的第三个问题是 OpenCV 绘图如何处置的问题。我希望能够将 OpenCV 的一些 Debug 信息绘制在屏幕上,比如找到的 contours 、比如计算出的方差/均值、比如画面里的积木总数等等,可以很方便的了解图像识别的情况,找到出现问题的原因。本来是通过注释掉绘图代码的方式进行状态切换,后来发现实在是太麻烦了,于是在所有的 OpenCV 绘图方法外面套了一层,放在 CVUtil
里:
public class CVUtil {
public class Draw {
public static void Text(Mat mat, string str, double x, double y,
double fontScale = 1, Scalar color = null,
LogLevel level = LogLevel.Debug) {
if (level >= Global.CurrentLogLevel) {
Imgproc.putText (mat, str, new Point (x,y), Core.FONT_HERSHEY_PLAIN, fontScale, color);
}
}
public static void Rectangle(Mat mat, OpenCVForUnity.Rect rect,
Scalar color = null, int thickness = 1,
LogLevel level = LogLevel.Debug) {
if (level >= Global.CurrentLogLevel) {
Imgproc.rectangle (mat, rect.tl (), rect.br (), color, thickness);
}
}
}
}
然后这样只要切换全局变量 Global.CurrentLogLevel
就能控制 Debug 内容的显示和隐藏了。
整个 Unity3D 项目里,AR 相关的渲染完全不用操心,只需要把 Vuforia 里的 ImageTarget 这个 Prefab 脱拽到场景中就能实现基础的 AR 功能。
就简单的写这么多啦,没什么干货,只是简单回顾一下自己的开发过程。
AR 开发的技术门槛并不是很高,目前现成的 SDK 很多,可以自行选择。而如何通过 AR 做出有趣的产品,这才是核心所在。
玩得开心。