AR 是什么?是一种现实增强技术(Augmented Reality),可以将虚拟效果应用到真实世界。AR 技能加身后,商家应用可以帮你给你的真实世界加点料。
去年双十一前夕,商家应用团队和欧莱雅集团深度合作,双方团队基于阿里统一小程序容器把 Modiface 试妆引擎在手淘上成功落地。目前已经支持了 YSL 和 Armani 两个顶级品牌的 AR 试妆应用在对应的官方旗舰店里上线。体验方式可以见文末。
Modiface 是欧莱雅集团旗下的美妆科技公司,Modiface 本次推出的 AR 试妆应用,专为手淘环境量身定制,利用小程序容器提供的基础设施能力,把自己的 AR 美妆引擎 “搬到“ 手淘上了。
那么,商家应用 + AR 是如何支撑 Modiface 试妆引擎落地的?下文将给大家分享我们双方在技术合作过程中一些心得。
链路概览
整体玩法链路是这样的,品牌方提供整体应用的设计和交互玩法,Modiface 试妆引擎基于商家应用提供的基础能力上实现自己的试妆引擎,然后统一输出给商家应用服务商,再装修到对应的品牌店铺上。
可以注意到,这个项目中涉及到多方协作,小程序容器提供的是非常基础的能力,AR 引擎负责的是能力的上层组合和自身算法能力的集成,商家应用服务商负责承接品牌的业务诉求并制作商家应用,再由品牌商的店铺装修同学把商家应用装修到对应的店铺上。
品牌方本身可以和 AR 引擎合作来定制自身的个性化需求,再把这种个性化的体验带给自家店铺的用户。
技术架构
为了支撑商家应用 AR 业务,我们在架构设计上以 API 和组件方式提供了非常多的标准原子化能力。
通过能力的组装和调用,AR 引擎可以快速验证自身的算法&渲染能力,我们支持以 MNN 方式或者 TensorFlow.js 的方式来运行推理 AR 引擎的算法,我们支持标准的 WebGL 接口和 Canvas2D API 以供业务绘制。我们也支持摄像头数据的采集和相机帧的透出。除此之外,我们还提供了非常多的底层能力来供上层引擎调用。
基于这些小程序容器提供的基础能力,上层 AR 引擎服务商可以构建出丰富的应用场景,包括但不限于虚拟试妆,虚拟穿戴,虚拟家居等等。
那么,Modiface 试妆引擎究竟是如何在小程序容器里运作的,我们来一起看一下整个链路。
品牌试妆应用加载 Modiface 试妆插件,插件会调用小程序容器的 Camera 组件来打开相机并监听来自 Native的相机帧数据,插件也会初始化一个 WebGL Canvas 组件来执行 TensorFlow.js。
Modiface 试妆引擎拥有两个人脸模型,分别是轮廓检测模型和 Landmark 检测模型,前者运行在 TensorFlow.js 环境中,后者使用 MNN 插件来运行推理。(后续将全部迁移至 MNN 实现)
轮廓模型检测到当前相机帧中存在人脸后,会切换至 Landmark 检测,此时会进行人脸精确点的采集推理。
同步会采集相机帧中的环境光线的强度以调整美颜算法。
提取要绘制的区域位置点阵,譬如人脸嘴唇位置,在 WebGL 的 shader 里开始渲染上妆,并把所有像素绘制 在 WebGL Canvas 组件上。
如当前检测不到人脸,则不执行渲染上妆逻辑。
整个试妆流程链路绝大部分运行在小程序容器的 JavaScript 环境里,并通过 JS binding 的方式和 Native 容器进行交互。
商家应用 + AR 的前提是 AR 引擎动态化,相对以往手淘 AR Case 最大的变化是:
整个 AR 引擎全部运行在小程序容器的 JavaScript 环境里,在不依赖手淘发版的情况下可以大量快速复制给不同的品牌,并且支持动态定制效果。
商家应用 + AR 支持各种不同的行业引擎接入,AR 引擎层和业务层是分离的,通过架构的解耦来支撑各种行业场景。容器底层专注于垂直能力的建设,上层业务快速迭代发展。
核心能力
我们基于 JavaScript 引擎提供了三种核心能力,分别是实时相机帧能力、深度学习推理加速能力、渲染能力。下面将分别介绍三种核心能力的技术细节。
实时相机帧能力
实时相机帧的输出是一切 AR 效果的开始,小程序目前已经支持实时相机帧的输出,在开发者接口层是 ArrayBuffer 类型的二进制数据。
实时相机帧能力在小程序里以标准原生 Camera 组件作为载体,在小程序里使用了同层渲染的方式嵌入在 WebView 中,可以实现原生组件和 WebView 组件混合使用的效果。
鉴于 iOS / Android 系统原生并没有提供相机帧的提取接口,加上 WebRTC 的标准在小程序体系里不适用,我们目前的做法是将从系统相机回调中把帧取出来,利用 OpenGL 的接口绘制在一块离屏的 FBO 上 ,这是一块自定义的帧缓冲区,我们可以利用这块帧缓冲区来实现包括 YUV 和 RGB 颜色空间的转换加速,再从这块 FBO 中读取像素点获取位图数据。
JavaScript 语言会以数组的语法处理二进制数据,我们一般会使用 ArrayBuffer 对象,我们需要实现原生 ByteData -> ArrayBuffer 的链路,这里有很大一部分工作是由 JavaScript 虚拟机( JSC / V8 )承担的,但是在部分 JavaScript 虚拟机不支持特性的 OS 版本下,譬如iOS9,我们使用了社区的开源方案 expo ① 来完成了 TypedArray 的构建。
目前的相机组件使用方式是使用了一个 1px*1px 大小的元素作为占位,用户不可见该组件,后续我们会支持离屏相机组件的创建。
目前在低端机上,小程序的实时帧率输出能达到 30 FPS,能满足绝大部分场景需要。
深度学习推理加速能力
利用小程序的深度学习推理加速能力,非常多的算法能力能够被集成到手淘里来。目前我们支持两种推理引擎 MNN 和 TensorFlow.js,在手淘环境上我们建议使用 MNN 来作为推理引擎加速,在 Modiface 场景下实测能比 TensorFlow.js 快1倍以上。
MNN:
MNN 是一个阿里开源的轻量级的深度学习端侧推理引擎,核心解决深度神经网络模型在端侧推理运行问题,涵盖深度神经网络模型的优化、转换和推理,其前身为 AliNN ②。MNN 更注重在推理时的加速和优化,解决在模型部署的阶段的效率问题,从而在移动端更高效地实现模型背后的业务。
MNN 小程序插件在 JavaScript 环境中动态完成了模型结构的搭建,MNN 本身使用 flatbuffer 作为模型描述工具,而 flatbuffer 支持 JavaScript 后端,可以利用 flatc 从描述文件生成 JavaScript 的模型加载代码,使用这部分代码就可以从引擎传入的 ByteArray 解析出模型数据了;在MNN的小程序插件中,使用 MNN 提供的表达式语句,根据模型数据动态构建出完整的模型图,在这之后的推理,也可以直接调用表达式来完成。
在小程序里,MNN 目前支持了常用的 20+ 种 op ,可以覆盖绝大部分推理场景。在 JS binding 的能力之上,MNN 可以调用手淘里的 Native MNN SDK 来加速推理,MNN 相比 TensorFlow.js 占用内存会低非常多,此外推理速度也通常是 TensorFlow.js 的数倍。
TensorFlow.js:
TensorFlow.js 是一个 Google 开源的基于硬件加速的 JavaScript 库,用于在浏览器和 Node.js 环境训练和部署机器学习模型。现在,TensorFlow.js 也能支持在购物小程序里来推理模型了。Web 开发者也可以在小程序环境里使用熟悉的 JavaScript 来进行机器学习。
在小程序里,TensorFlow.js 的 backend 是我们的 WebGL Canvas组件,在小程序里利用 WebGL 的能力,TensorFlow.js 可以使用 GPU 来加速机器学习的运行。
渲染能力
承载小程序渲染能力的 Canvas 组件是一个原生组件,同样是利用同层渲染方式实现的。Canvas 组件的后端渲染 backend 是 GCanvas③。
Canvas 组件既可以作为画布来绘制像素,也可以作为推理加速的 backend 来做计算。在小程序里我们实现了 WebGL 1.0 接口和 Canvas2D 的标准API,以降低开发者的使用成本。
为什么不用 WebView 的 Canvas?
在小程序架构下,Worker 和 Render 是分离的,也就是说 运行 JavaScript 的虚拟机和负责渲染的虚拟机对象不是同一个,目前两者间通信是通过 Native 容器作为 bridge。如果有高频且数据量巨大的 WebGL 调用,需要每次涉及 Render 和 Worker 之间的交互,这个通信成本非常高。
我们的解决方案是在Native 实现了符合 W3C 标准的 WebGL 标准和 Canvas2D 的接口,无缝支持各种渲染框架对接。
能力演进
在旗舰店2.0之中,商家应用实现了店铺开放的可能性,AR 更是给商家应用带来了新的技术营销的方式。商家应用 + AR 会继续演进并支撑更多能力,我们也非常欢迎各类 AR 算法引擎和品牌方找我们合作提供好的创意和想法。
我们后续会继续在目前的基础能力上做更多的优化工作,主要分为几方面:
引擎 WebAssembly 化:目前的 AR 引擎是使用 JavaScript 语言构建的,使用 WebAssembly 技术我们可以补充 JavaScript 本身性能不够理想带来的影响,并方便开发者移植已经成熟的 C++/C 工程到 Web 。
在包括很多对密集运算要求很高的场景下,譬如游戏引擎,物理引擎,音视频处理,加密算法等,我们使用 WebAssembly 可以直接把 JavaScript 运算带来的性能开销降低。此外,在小程序场景下,WebAssembly 可以大大降低小程序包的体积大小,降低用户加载时长。WebAssembly 在代码安全性上相较 JavaScript 也具备一定优势。问题现在在于 JavaScript 和 WebAssembly 之间函数调用是非常慢的,针对这个场景我们参考了业界的一些实现④,以特定类型的通信接口来维护 WebAssembly 和 JavaScript 之间的交互。
图形性能优化:我们计划在目前使用 WebGL1.0 的场景下,继续增加 WebGL2.0 的标准接口,并计划切换至 Metal / Vulkan 的底层图形能力,以帮助开发者享受到最新的 OpenGL ES 3.0 的特性,包括延迟渲染、色调映射、GPU 粒子效果等等。
通信优化:目前 Camera 组件的帧数据是每一帧都从 Native 发送至 JavaScript 里的,巨量且频繁的通信对于性能的消耗是非常巨大的,消费帧数据的对象一般都是 Canvas 组件和 MNN 插件,这里面是存在很大的优化空间的。针对这个场景我们重新设计了一个方案,我们利用纹理共享的机制,将相机采集到的所有图像信息写入一块共享的纹理中,这块共享纹理的textureid 可以在 JavaScript 侧被获取到使用,开发者在无需感知具体图像内容的情况下调用 Canvas 组件的 textureid 接口可以直接取出共享纹理的数据并绘制在画布上。
此外在避免了 JavaScript 的通信成本之后,我们还可以继续优化 GPU -> CPU -> GPU这个链路的性能,熟悉图形学的同学都知道,CPU 和GPU 之间的资源交换是非常耗时的,通常从CPU拷贝/读取数据到GPU的操作很昂贵,耗时一般是几十到数百毫秒级的,我们可以利用 sharegroup 的特性在 Camera 组件和 Canvas 组件之间共享 OpenGL 上下文环境,最大限度减低 CPU 和 GPU 之间的通信成本。
能力提供:我们后续会计划提供更多的基础能力,包括但不限于如下:
目标跟踪:虚拟内容固定在图片上或者定位在空间中,实现目标跟踪能力;
图片识别:识别平面图片渲染内容;可以基于此识别商标、产品包装图案、活动海报、宣传册等平面物料,进行品牌数字化内容展示和互动;
手势识别:识别手势渲染内容;定制手势,与用户互动,更好的将品牌与消费者连接;
姿势检测:检测出人体姿势;可以基于此实现很多强肢体的互动能力,适合线下互动;
空间识别:识别真实物理空间;可基于此能力实现 AR 红包等更具沉浸式的 AR 互动体验;
人脸检测:检测出人脸;可以基于此实现虚拟试妆,虚拟试戴等体验;
...
总而言之,商家应用向全行业全品牌开放的 AR 能力还有多种可能性。
体验
现在可以通过访问 YSL 旗舰店首页或者 Armani 旗舰店首页来体验两个试妆应用,或者通过淘口令 Or 二维码访问:
【阿玛尼试妆小程序】https://m.tb.cn/h.Vb7mGmC?sm=f2d5ea ¥6ecb1XsL4xt¥
【YSL试妆小程序】https://m.tb.cn/h.V0CMole?sm=bea5e9 ¥8ryX1XsKjrq¥
相关链接:
1.https://github.com/expo/expo/blob/master/packages/expo-gl-cpp/cpp/EXJSConvertTypedArray.c
2.重磅| 淘宝轻量级的深度学习端侧推理引擎 MNN 开源
3.5分钟带你看懂 GCanvas渲染引擎的演进
4.https://hacks.mozilla.org/2018/10/calls-between-javascript-and-webassembly-are-finally-fast-%f0%9f%8e%89/
One More Thing
Hey,我们现在也在招人。业界领先的淘宝小程序团队现在致力于推进小程序底层能力建设和创意互动项目,我们非常欢迎具备 iOS / Android / C++ / Flutter / 渲染引擎 / JS引擎等技术栈的同学加入我们。
欢迎简历投递 Email:fangxi.fx@alibaba-inc.com