麦斯科技 · 2021年04月18日

在Unity中构建移动AR过滤器应用

https://community.arm.com/developer/ip-products/processors/b/ml-ip-blog/posts/building-an-ar-mobile-app?utm_source=community&utm_medium=social&utm_campaign=2021_client-ar-vr_mk04-2_arm_na_interests+skills_awa&utm_content=blog

帕维尔·鲁德科(Pavel Rudko)2021年2月11日

AR_2D00_emoji_2D00_app_2D00_post_2D00_image1.jpg_5F00_2D00_5F00_900x506x2.jpg_2D00_900x506x2.jpg

如今,增强现实(AR)移动应用程序非常流行。您可以在诸如Instagram或Snapchat之类的消息传递或照片和视频托管应用程序中找到漂亮的过滤器。这些滤镜可让您更改前置或后置摄像头的真实图像,并添加一些美观且“有趣”的效果。其中一些很简单,只需要后处理即可。但是,更复杂的滤镜(例如美化或向某人的脸上添加有趣的胡须和胡须)依赖于相机图像中的特征识别。这意味着,算法必须知道用户的鼻子或眼睛的位置,头部的旋转以及与相机的距离。

实施此类过滤器可能是一项艰巨的任务,尤其是在使用移动设备并希望实现实时性能时。因此,我们构建了一个过滤器应用程序,以亲眼目睹开发人员可能面临的挑战。

该应用程序允许您替换背景并将虚拟对象附加到人脸,从而可以将用户置于多个虚拟环境或场景中。这是我们发现的。

AR-emoji-app-image1.JPG

该应用程序的屏幕截图

AR的机器学习

为了从摄像机视频流中获取必要的数据并允许程序以人类的方式看世界,我们使用了机器学习(ML)。在不同的数据集上训练的神经网络模型有不同的类型,每种模型都有特定的用途。对于AR过滤器应用,最重要的是:

  • 人脸识别
  • 手或手势识别
  • 人物和背景细分
  • 样式转移。

通过识别和跟踪关键点,可以将对象附着到脸部或手部,控制化身或为手势分配某种行为。

分割提供像素遮罩,突出显示重要区域。然后,我们可以使用这些蒙版替换或模糊背景,或更改某人的头发的阴影。

样式转移从参考图像中获取一些样式特征,并将其应用于源图像。这可以使人看起来像动漫人物,或者使整个图片看起来像是由知名艺术家绘制的。

AR-emoji-app-image2.JPG

使用Prisma Photo Editor进行样式转移的示例。源图像在左侧,结果在右侧。

在设计或选择现有模型时,应考虑质量和性能。该选择可能取决于您是要实时处理视频流还是要应用过滤器并保存或发送图片。

与将数据发送到服务器并接收处理结果相比,通常首选使用设备上的推断。这是因为它不需要Internet连接或昂贵的服务器,并且不太可能公开任何用户数据。

我们使用的技术

我们使用Mediapipe的两个模型 进行人脸和地标检测。Mediapipe是一个高级框架,它提供了经过训练的模型和灵活的图形系统,用于设计AR管道和一些现成的管道。它使用Tensorflow进行推理并支持多个平台。这不仅对交付应用程序有用,而且在开发过程中也非常有用,从而可以在开发计算机上进行测试和调试。

为了进行渲染,我们使用了Unity。Unity还提供了一种新的用于执行神经网络的引擎,称为 梭子鱼。它可以轻松地集成到项目中,并具有简单的界面,该界面允许加载网络,配置运行时和运行推理。梭子鱼仍处于发展阶段。

将ML功能添加到Unity项目的另一种方法是编写自定义C ++插件,并使用神经网络推理库(如 Tensorflow Lite 或 Android NNAPI)。对于我们的项目,我们使用了自己的技术– Arm NN。

Arm NN是一种神经推理引擎,它依赖于较低级的Arm Compute Library(ACL)。两种技术都有助于以最有效的方式利用Arm GPU和CPU,并且这种优化的互连是Arm Total Compute 方法的一部分。

我们的管道

在我们的应用程序中,几个神经网络一起使用。

裁剪来自摄像机的输入图像以获取中央四边形区域,然后将其缩小。

分割网络产生用于背景替换的掩模图像。

地标检测网络可找到人脸关键点(鼻子,眼睛,脸颊)的3D坐标。仅当脸部占据框架的大部分并且垂直对齐时,它才能很好地工作。为了使其正常工作,我们首先运行一个人脸检测模型,然后裁剪包含人脸的帧区域。

您可以看到整个管道的示意图,该示意图已应用于每帧的输入图像。

AR emoji app image3.png-1040x0.png

进一步处理

一旦识别出所需的镜框特征,就需要做一些额外的工作以获得漂亮的结果。

渲染虚拟对象

如果我们知道脸部界标坐标,则可以在脸部上方渲染诸如眼镜,兔耳或帽子之类的虚拟对象。它可以是具有透明度的简单2D精灵,也可以是3D模型。

对于3D渲染,我们可以使用正投影或透视投影。实现正交投影更容易,因为我们只需要X和Y坐标以及对象的比例即可正确定位。使用透视投影,我们可以获得更好的外观效果,但是需要额外的精力来计算3D对象的正确平移,旋转和缩放比例。

同样,渲染3D对象需要考虑遮挡。想象一下在头部上方绘制一个帽子。背面应该不可见,因为在现实生活中它将被遮挡。另一方面,正面应该是可见的,因此不可能简单地将分割蒙版用作深度缓冲区。为了解决这个问题,可以使用不可见的共谋器。这是一个形状类似于人头(球形,胶囊状或更精确的物体)的对象,并且仅渲染到深度缓冲区中。

AR emoji app image4.png-1040x0.png

遮挡网格

寻找或创建资产对AR过滤器非常有用,这可能很困难。重要的是要确保它们不仅看起来不错,而且适合人的头或脸的形状。

相对于头部放置对象可能需要一些调整。代理对象或人体模型可用于以默认的中性姿势设置转换。

帧插值

即使具有良好的模型和高质量的输入图片,当实际图像非常相似时,您也可以期望输出值因帧而异。此问题与模型更相关,模型不考虑先前的框架并完全独立地对其进行处理。我们从人脸标志检测模型获得的坐标不是很稳定,从而导致附加的3D模型摇摆。可以通过平均多个帧的结果来解决。使用加权和将当前帧和前几个帧中的值组合在一起。在这种情况下,考虑变化速度也很重要。如果用户快速移动其头部,则我们将识别出该头部并丢弃先前帧中的值,从而使3D模型立即移动到新位置。


float Interpolate(float timeStamp, float value)
{
    float distance = value - lastValue;
    float duration = timeStamp - lastTimeStamp;
 
    // Find the sum of the values for multiple frames to estimate average velocity.
    float totalDistance = distance;
    float totalDuration = duration;
 
    foreach (var entry in previousValues)
    {
        totalDistance += entry.distance;
        totalDuration += entry.duration;
    }
 
    float velocity = totalDistance / totalDuration;
 
    // velocityScale is constant, which regulates how much the velocity affects the interpolation.
    float alpha = 1.0f - 1.0f / (1.0f + velocityScale * Mathf.Abs(velocity));
 
    previousValues.Enqueue(new Entry { distance = distance, duration = duration });
 
    // We only need to keep track of a few previous frames.
    if (previousValues.Count > maxWindowSize)
    {
        previousValues.Dequeue();
    }
 
    result = value * alpha + lastResult * (1.0f - alpha);
 
    // Save these values for the next frame.
    lastResult = result;
    lastValue = value;
    lastTimeStamp = timeStamp;
 
    return result;
}

神经网络信心

仅当框架中有面部并且我们对框架的功能充满信心时,才必须渲染框架中任何特定于面部的部分(例如,附着到面部的对象)。如果神经网络提供置信度,我们可以设置一个阈值,如果该值低于此阈值,则跳过部分管道。确保它不要太高,否则效果可能会在某些帧中消失,效果会不太好。

硬掩码和软掩码

将细分模型的输出用作掩码时,我们可以将其视为二进制(“硬”)掩码或“软”掩码。让我们说有两类像素,并且为每个像素分配了概率p的某个值 ,这表明像素属于这些类之一的可能性。例如,它可以用于用图片替换原始背景:

output= frame_color×p + background_picture_color×(1 – p)

遮罩可以保持原样。在这种情况下,我们有一个“软”蒙版,它将使检测到的对象的边缘稍微模糊,并使对象和背景之间的过渡不太明显。我们还可以通过降低较低的值来使蒙版锐化。例如,可以使用简单的平方运算:

p'= p 2

硬遮罩与Unity切口着色器类似,因为图像不会有部分透明的区域。一切都是完全不透明的或完全透明的。这就是为什么得到一个“硬”的面具,我们需要一定的门槛,比如二值化这 一个。

p' = p if p > a else 0

AR-emoji-app-image5.JPG

哪种方法更适合使用?试图寻找一个好的结果是一个问题。如果神经网络给出稳定的精确输出,则硬掩模可能看起来更自然。否则,柔软的口罩会掩盖一些瑕疵。

灯光和颜色

用于渲染对象的照明会对整体感知产生巨大影响。如果场景中的光线与原始相机框架相差太大,则看起来非常不自然。一种灵活的方法是添加具有不同方向的多个光源或设置高水平的环境光。在这种情况下,没有明显的黑暗区域,使场景保持中性。阴影表明对象不平坦。

AR-emoji-app-image6.JPG

改善感知或营造某种氛围的另一种方法是在后期处理过程中使用滤色器。

调试

在基于相机的应用程序中查找导致问题的原因的最简单方法是转储故障帧。它们可用于重现错误并检查管道的每个步骤。找到解决方案后,您可以确保它在相同的输入下和相同的条件下工作。

转储可能包含输入图像,以及一些初始预处理后的相同图像以及来自神经网络或中间计算结果的原始输出。

调整某些设置以比较前后的效果并查看是否有改善时,也可以使用转储帧。如果推理引擎不支持开发机器的平台(如本例所示),您还可以将神经网络的输出保存在设备上一定的帧。然后,您可以在开发和调试期间使用这些值来模拟推理,而无需在每次进行更改时都安装应用程序。

结论

首先,构建具有增强现实功能的移动应用程序,或将增强现实功能添加到现有应用程序似乎有些棘手。它以某种方式。但是使用正确的框架和工具,您可以节省大量时间。前面介绍的技巧可以帮助您获得更好的视觉质量。

当您需要专注于美观的资产,将整个管道组装在一起或只是调整一些参数以实现更高的真实性时,最好在图形等方面节省一些时间。这意味着使用摄像机和ML推理并使用现有解决方案。在这种情况下,Unity是一个不错的选择,特别是考虑到他们即将推出的梭子鱼技术,该技术仍处于开发阶段,但已经显示出良好的效果。

如果您的目标是在Arm设备上获得最佳性能,请查看 Arm NN –我们在项目中使用的技术。

了解有关Arm NN的更多信息:https://developer.arm.com/ip-products/processors/machine-learning/arm-nn?_ga=2.213011156.1383911884.1618669827-343890698.1618236450

推荐阅读
关注数
5841
内容数
525
定期发布Arm相关软件信息,微信公众号 ArmSWDevs,欢迎关注~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息