深度图本质上是一个图像(或图像通道),它包含了与场景中物体表面与给定视角(在这种情况下,相机本身)的距离有关的信息,在该图像中的每个像素。深度图是各种计算机图形和计算机视觉应用的基本组成部分,如增强现实、肖像模式和三维重建。尽管最近ARCore深度API在深度感应能力方面取得了进展,但网络上的大多数照片仍然缺少相关的深度图。再加上网络社区的用户对在JavaScript中拥有深度功能以增强现有网络应用的兴趣日益浓厚,例如将图像带入现实,将实时AR效果应用于人脸和身体,甚至重建物品以用于VR环境,这些都有助于形成你今天看到的路径。
今天,我们将介绍深度API,这是TensorFlow.js的第一个深度估计API。通过这个新的API,我们还引入了第一个用于肖像的深度模型,ARPortraitDepth,它为一个单一的肖像图像估计深度图。为了展示深度信息许多可能的用途之一,我们还展示了一个计算摄影应用,3D photo,它利用预测的深度,在给定的肖像图像上实现了3D视差效果。试试下面的现场演示,每个人都可以轻松地使他们的社交媒体资料照片变成3D,如下图所示。
ARPortraitDepth:单一图像深度估计
人像深度API的核心是一个深度学习模型,名为ARPortraitDepth,它将单色人像图像作为输入并产生深度图。为了提高计算效率,我们采用了一个轻量级的U-Net架构。如下图所示,编码器逐渐将图像或特征图的分辨率降低一半,而解码器将特征分辨率提高到与输入相同。来自编码器的深度学习特征被串联到解码器中具有相同空间分辨率的相应层,以带来用于深度估计的高分辨率信号。在训练过程中,我们强迫解码器在每一层产生分辨率越来越高的深度预测,并为每一层增加一个与地面真相有关的损失。根据经验,这有助于解码器通过逐渐增加细节来预测准确的深度。
丰富多样的训练数据对于机器学习模型实现整体的良好性能,例如准确性和鲁棒性,是至关重要的。我们用不同的相机配置,如焦距、相机姿势,从高质量性能的捕捉系统捕捉到的三维数字人类中,合成渲染成对的彩色和深度图像,并使用高动态范围环境照度图进行重新照明增强,以增加彩色图像的真实性和多样性,如面部的阴影。我们还使用配备了前置深度传感器的手机收集真实数据,例如谷歌Pixel 4,其中深度质量作为训练的基础事实,并不像我们的合成数据那样准确和完整,但彩色图像在野外的图像上运行时可以有效地提高我们模型的性能。
为了增强对背景变化的鲁棒性,在实践中,我们在将图像送入深度估计的神经网络之前,用MediaPipe和TensorFlow.js运行一个现成的人体分割模型。
人像深度模型可以实现一大批以人体为导向的创意应用,可以推动下一代网络应用。我们向读者推荐ARCore深度实验室,以获得更多灵感。
对于3D照片应用程序,我们创建了一个高性能的渲染管道。它首先使用TensorFlow.js现有的身体分割API生成一个分割的面具。接下来,我们将遮罩后的肖像传入肖像深度API,在GPU上获得深度图。最终,我们在three.js中生成一个深度网格,顶点以规则的网格排列,并通过重新投影相应的深度值进行位移(生成深度网格见下图)。最后,我们将纹理投影应用于深度网格,并将摄像机围绕Z轴旋转一圈。用户可以下载GIF或WebM格式的动画。
人像深度API的安装
人像深度API目前是作为新深度API的一个变体提供的。
要安装API和运行时库,你可以使用
通过NPM:
yarn add @tensorflow/tfjs-core @tensorflow/tfjs-backend-webgl
yarn add @tensorflow/tfjs-converter
yarn add @tensorflow-models/body-segmentation
yarn add @tensorflow-models/depth-estimation
在你的JS代码中引用API,这取决于你如何安装该库。
如果通过脚本标签安装,你可以通过全局命名空间depthEstimation
来引用该库。
如果通过NPM安装,你需要先导入库:
import '@tensorflow/tfjs-backend-core';
import '@tensorflow/tfjs-backend-webgl';
import '@tensorflow/tfjs-converter';
import '@tensorflow-models/body-segmentation;
import * as depthEstimation from '@tensorflow-models/depth-estimation;
自己试试吧!
首先,你需要创建一个估算器:
const model = depthEstimation.SupportedModels.ARPortraitDepth;
estimator = await depthEstimation.createEstimator(model);
const video = document.getElementById('video');
const depthMap = await estimator.estimateDepth(video);
一旦你有了一个估计器,你就可以传入视频流、静态图像或TensorFlow.js的张量来估计深度:
const video = document.getElementById('video');
const estimationConfig = {
minDepth: 0, // The minimum depth value outputted by the estimator.
maxDepth: 1, // The maximum depth value outputted by the estimator.
};
const depthMap = await estimator.estimateDepth(video, estimationConfig);
如何使用输出?
上面的depthMap
结果包含图像中每个像素的深度值。
depthMap
是一个存储底层深度值的对象。然后,你可以利用所提供的异步转换函数,如toCanvasImageSource
,toArray
,和toTensor
,这取决于你想要的输出类型,以提高效率。
应该注意的是,不同的模型对数据有不同的内部表示。因此,从一种形式转换到另一种形式可能是昂贵的。以效率的名义,你可以调用getUnderlyingType
,以确定深度图已经是什么形式,所以你可以选择保持相同的形式以获得更快的结果。
depthMap的语义如下:深度图的大小与输入图像相同。对于阵列和张量表示,每个像素有一个深度值。对于CanvasImageSource
,绿色和蓝色通道总是被设置为0,而红色通道存储深度值。
请看下面的输出片段的例子:
{
toCanvasImageSource(): ...
toArray(): ...
toTensor(): ...
getUnderlyingType(): ...
}
浏览器性能
人像深度模型
MacBook M1 Pro 2021年。 (FPS) |
iPhone 13 Pro (FPS) |
台式电脑 英特尔i9-10900K。Nvidia GTX 1070 GPU。 (FPS) |
|
TFJS运行时间 带有WebGL后端。 |
51 |
22 |
47 |