📜  在Android中使用ARCore增强面孔

📅  最后修改于: 2021-05-08 19:42:21             🧑  作者: Mango

增强的面部允许应用程序自然地区分开个人面部的各个区域,并利用这些区域以与个人面部的轮廓和区域适当匹配的方式来覆盖资源(例如,表面和模型)。 ARCore是在Android上构建增强现实应用程序的阶段。 Augmented Face是ARCore的子系统,可让您的应用程序执行以下操作:

  • 自然地,识别任何人识别出的面部的各个区域,并利用这些区域以与个人面部的轮廓和区域适当匹配的方式覆盖资源(例如,表面和模型)。
  • 利用ARCore提供的468点面网格在自定义的面上应用自定义纹理。

它是怎么运行的?

增强的脸部不需要特殊或特殊的硬件,例如深度传感器。相反,它使用手机的摄像头和机器学习来提供三个数据片段:

  1. 生成面部网格: 468点密集的3D面部网格,可让您平移精确地跟随面部瞬间的详细纹理。
  2. 识别姿势:根据生成的Face Mesh锚定人脸上的点,这对在太阳穴和鼻子上或附近放置效果很有用。
  3. 基于面部网格生成和识别的区域的叠加和位置纹理以及3D模型。

ARCore如何在没有任何深度硬件的情况下从2D图像提供3D面网格?

它使用基于TensorFlow Lite平台构建的机器学习模型来实现此目的,并且优化了交叉管道以在设备上实时运行。它使用一种称为转移学习的技术,其中我们针对两个目标训练神经网络,一个目标是预测3D顶点,另一个是预测2D轮廓。为了预测3D顶点,我们使用合成的3D数据集对其进行训练,并使用该神经网络作为下一阶段训练的起点。

在此下一阶段中,它将使用带注释的数据集和带注释的真实世界数据集来训练2D轮廓预测模型。生成的网络不仅可以从合成数据集中预测3D顶点,而且还可以从2D图像中很好地执行。为了确保该解决方案适合所有人,ARCore开发人员使用地理上不同的数据集来训练网络,以使其适用于所有类型的面孔,更宽的面孔,更高的面孔以及所有类型的肤色。

为了在移动设备上启用这些复杂算法,我们在ARCore中内置了多种自适应算法。这些算法动态地感测处理先前的图像并相应地调整管道的各种参数所花费的时间。它使用多种ML模型,其中一种针对更高的质量进行了优化,而另一种针对进行资源优化时则针对更高的性能进行了优化。它还会调整流水线参数(例如推断率),以便跳过一些图像,而用插值数据代替。通过所有这些技术,您可以获得给用户带来全帧速率的体验。因此,在处理ARCore内部的所有这些技术时,它可以以完整的相机帧速率提供面部网格和区域姿势。

识别增强的面部网格

为了在识别出的面部上适当地覆盖纹理和3D模型,ARCore提供了检测到的区域和增强的面部网格。该网格是对面部的虚拟描绘,包括顶点,面部区域和用户头部的焦点。在相机识别出用户面部时,ARCore执行以下步骤来生成增强的面部网格以及中心和区域姿势:

  • 它可以区分中心姿势和面部网格。
    • 位于鼻子后面的中心姿势是用户头部的实际中心点(换句话说,在头骨内部)。
    • 面部网格包含许多构成面部的顶点,并相对于中心姿势进行了特征化。

  • AugmentedFace类利用面部网格和中心姿势来区分出现在客户面部的面部区域。这些区域是:
    • 右眉(RIGHT_FOREHEAD)
    • 左殿(LEFT_FOREHEAD)
    • 鼻尖(NOSE_TIP)

Facement网格,中心姿势和面部区域姿势被AugmentedFace API用作定位点和区域,以将资源放置在您的应用程序中。

468点面纹理网格

参考术语

  • 可跟踪:可跟踪是一个接口,ARCore和可以与之连接的对象都可以跟随该接口。
  • 锚点:它描述了现实世界中的固定位置和方向。为了保持在物理空间中的固定位置,随着ARCore对空间的理解程度的提高,此位置的数字描述将更新。锚是可散列的,例如可以用作HashMaps中的键。
  • 姿势:在需要陈述场景的位置时,需要放置对象,并需要根据场景的坐标指定位置。姿势是您陈述这一观点的手段。
  • 会话:处理AR框架状态并处理会话生命周期。此类提供了到达ARCore API的主要通道。此类允许用户进行会话,配置,启动或停止会话,最重要的是,接收允许访问摄像机图像和设备姿势的帧。
  • 纹理:纹理对于增强脸部特别有用。这使您可以进行灯光覆盖,使其与已识别面部的语言区域保持一致,以增加您的体验。
  • ArFragment: ARCore利用提供许多功能的ArFragment,例如,飞机寻找,权限处理和摄像头设置。您可以在活动中合法地使用该片段,但是在任何需要自定义功能的地方(例如,Augmented Faces),都应该扩展ArFragment并设置适当的设置。该片段是隐藏所有复合材料(如OpenGL,渲染模型等)并提供高级API来加载和渲染3D模型的层。
  • ModelRenderable: ModelRenderable 通过将其附加到节点来渲染3D模型。
  • Sceneform SDK: Sceneform SDK是另一个适用于Android的库,可在您的应用程序中快速创建和混合AR体验。它加入了ARCore和令人惊叹的基于物理的3D渲染器。

示例项目

我们将创建人脸过滤器之类的Snapchat,Instagram和TikTok。下面给出了一个示例GIF,以了解我们将在本文中做些什么。注意,我们将使用Java语言实现该项目。

Android示例GIF中使用ARCore增强的面孔

步骤1:创建一个新项目

要在Android Studio中创建新项目,请参阅如何在Android Studio中创建/启动新项目。请注意,选择Java作为编程语言。

第2步:在此示例中添加资产文件

在sampledata / models文件夹中添加任何3D模型。为此,我们可以在项目文件目录中创建一个新文件夹,也可以直接从Android Studio中创建一个新文件夹。允许的3D模型扩展名是.fbx,.OBJ,.glTF。互联网上有许多免费模型。您可以访问这里或更多。您可以从此处下载本示例中使用的资产。请参考本文以在android studio中创建原始文件夹。然后,只需将fox_face.sfb文件复制并粘贴到原始文件夹中即可。同样,将fox_face_mesh_texture.png文件复制并粘贴到drawable文件夹中。

步骤3:将依赖项添加到build.gradle(:app)文件

将以下依赖项添加到build.gradle(:app) 文件。

将以下代码片段添加到build.grdale文件中。仅需要一次,即可将.fbx资产转换为.sfb并将其保存在原始文件夹中。或者,您也可以按照步骤2中的步骤自行添加它们。

步骤4:将依赖项添加到build.gradle(:project)文件

将以下依赖项添加到build.gradle(:project)文件。

步骤5:使用AndroidManifest.xml文件

将以下行添加到AndroidManifest.xml文件。

以下是AndroidManifest.xml文件的完整代码。

XML


  
    
  
    
    
  
    
    
      
    
  
    
        
        
            
                
  
                
            
        
    
  


XML


  
    
  


Java
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import com.google.ar.core.Config;
import com.google.ar.core.Session;
import com.google.ar.sceneform.ux.ArFragment;
import java.util.EnumSet;
import java.util.Set;
  
public class CustomArFragment extends ArFragment {
    @Override
    protected Config getSessionConfiguration(Session session) {
        Config config = new Config(session);
  
        // Configure 3D Face Mesh
        config.setAugmentedFaceMode(Config.AugmentedFaceMode.MESH3D);
        this.getArSceneView().setupSession(session);
        return config;
    }
  
    @Override
    protected Set getSessionFeatures() {
        // Configure Front Camera
        return EnumSet.of(Session.Feature.FRONT_CAMERA);
    }
  
    // Override to turn off planeDiscoveryController.
    // Plane traceable are not supported with the front camera.
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        FrameLayout frameLayout = (FrameLayout) super.onCreateView(inflater, container, savedInstanceState);
        getPlaneDiscoveryController().hide();
        getPlaneDiscoveryController().setInstructionView(null);
        return frameLayout;
    }
}


Java
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.google.ar.core.AugmentedFace;
import com.google.ar.core.Frame;
import com.google.ar.core.TrackingState;
import com.google.ar.sceneform.rendering.ModelRenderable;
import com.google.ar.sceneform.rendering.Renderable;
import com.google.ar.sceneform.rendering.Texture;
import com.google.ar.sceneform.ux.AugmentedFaceNode;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
  
public class MainActivity extends AppCompatActivity {
    private ModelRenderable modelRenderable;
    private Texture texture;
    private boolean isAdded = false;
    private final HashMap faceNodeMap = new HashMap<>();
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
  
        CustomArFragment customArFragment = (CustomArFragment) getSupportFragmentManager().findFragmentById(R.id.arFragment);
  
        // Use ModelRenderable.Builder to load the *.sfb
        // models at runtime.
        // Load the face regions renderable.
        // To ensure that the asset doesn't cast or receive
        // shadows in the scene, ensure that setShadowCaster
        // and setShadowReceiver are both set to false.
        ModelRenderable.builder()
                .setSource(this, R.raw.fox_face)
                .build()
                .thenAccept(rendarable -> {
                    this.modelRenderable = rendarable;
                    this.modelRenderable.setShadowCaster(false);
                    this.modelRenderable.setShadowReceiver(false);
  
                })
                .exceptionally(throwable -> {
                    Toast.makeText(this, "error loading model", Toast.LENGTH_SHORT).show();
                    return null;
                });
  
        // Load the face mesh texture.(2D texture on face)
        // Save the texture(.png file) in drawable folder.
        Texture.builder()
                .setSource(this, R.drawable.fox_face_mesh_texture)
                .build()
                .thenAccept(textureModel -> this.texture = textureModel)
                .exceptionally(throwable -> {
                    Toast.makeText(this, "cannot load texture", Toast.LENGTH_SHORT).show();
                    return null;
                });
  
        assert customArFragment != null;
  
        // This is important to make sure that the camera
        // stream renders first so that the face mesh
        // occlusion works correctly.
        customArFragment.getArSceneView().setCameraStreamRenderPriority(Renderable.RENDER_PRIORITY_FIRST);
        customArFragment.getArSceneView().getScene().addOnUpdateListener(frameTime -> {
            if (modelRenderable == null || texture == null) {
                return;
            }
            Frame frame = customArFragment.getArSceneView().getArFrame();
            assert frame != null;
  
            // Render the effect for the face Rendering the effect involves these steps:
            // 1.Create the Sceneform face node.
            // 2.Add the face node to the Sceneform scene.
            // 3.Set the face region Renderable. Extracting the face mesh and
            // rendering the face effect is added to a listener on
            // the scene that gets called on every processed camera frame.
            Collection augmentedFaces = frame.getUpdatedTrackables(AugmentedFace.class);
  
            // Make new AugmentedFaceNodes for any new faces.
            for (AugmentedFace augmentedFace : augmentedFaces) {
                if (isAdded) return;
  
                AugmentedFaceNode augmentedFaceMode = new AugmentedFaceNode(augmentedFace);
                augmentedFaceMode.setParent(customArFragment.getArSceneView().getScene());
                augmentedFaceMode.setFaceRegionsRenderable(modelRenderable);
                augmentedFaceMode.setFaceMeshTexture(texture);
                faceNodeMap.put(augmentedFace, augmentedFaceMode);
                isAdded = true;
  
                // Remove any AugmentedFaceNodes associated with 
                // an AugmentedFace that stopped tracking.
                Iterator> iterator = faceNodeMap.entrySet().iterator();
                Map.Entry entry = iterator.next();
                AugmentedFace face = entry.getKey();
                while (face.getTrackingState() == TrackingState.STOPPED) {
                    AugmentedFaceNode node = entry.getValue();
                    node.setParent(null);
                    iterator.remove();
                }
            }
        });
    }
}


步骤6:修改activity_main.xml文件

我们已经添加activity_main.xml中的文件片段以下是activity_main.xml文件的代码

XML格式



  
    
  

步骤7:创建一个新的Java类

创建一个新类,并将该文件命名为可扩展ArFragment的CustomArFragment下面是CustomArFragment的代码。 Java文件。

Java

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import com.google.ar.core.Config;
import com.google.ar.core.Session;
import com.google.ar.sceneform.ux.ArFragment;
import java.util.EnumSet;
import java.util.Set;
  
public class CustomArFragment extends ArFragment {
    @Override
    protected Config getSessionConfiguration(Session session) {
        Config config = new Config(session);
  
        // Configure 3D Face Mesh
        config.setAugmentedFaceMode(Config.AugmentedFaceMode.MESH3D);
        this.getArSceneView().setupSession(session);
        return config;
    }
  
    @Override
    protected Set getSessionFeatures() {
        // Configure Front Camera
        return EnumSet.of(Session.Feature.FRONT_CAMERA);
    }
  
    // Override to turn off planeDiscoveryController.
    // Plane traceable are not supported with the front camera.
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        FrameLayout frameLayout = (FrameLayout) super.onCreateView(inflater, container, savedInstanceState);
        getPlaneDiscoveryController().hide();
        getPlaneDiscoveryController().setInstructionView(null);
        return frameLayout;
    }
}

步骤8:修改 主要活动。 Java文件

以下是代码 主要活动。 Java文件。在代码内部添加了注释,以更详细地了解代码。

Java

import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.google.ar.core.AugmentedFace;
import com.google.ar.core.Frame;
import com.google.ar.core.TrackingState;
import com.google.ar.sceneform.rendering.ModelRenderable;
import com.google.ar.sceneform.rendering.Renderable;
import com.google.ar.sceneform.rendering.Texture;
import com.google.ar.sceneform.ux.AugmentedFaceNode;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
  
public class MainActivity extends AppCompatActivity {
    private ModelRenderable modelRenderable;
    private Texture texture;
    private boolean isAdded = false;
    private final HashMap faceNodeMap = new HashMap<>();
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
  
        CustomArFragment customArFragment = (CustomArFragment) getSupportFragmentManager().findFragmentById(R.id.arFragment);
  
        // Use ModelRenderable.Builder to load the *.sfb
        // models at runtime.
        // Load the face regions renderable.
        // To ensure that the asset doesn't cast or receive
        // shadows in the scene, ensure that setShadowCaster
        // and setShadowReceiver are both set to false.
        ModelRenderable.builder()
                .setSource(this, R.raw.fox_face)
                .build()
                .thenAccept(rendarable -> {
                    this.modelRenderable = rendarable;
                    this.modelRenderable.setShadowCaster(false);
                    this.modelRenderable.setShadowReceiver(false);
  
                })
                .exceptionally(throwable -> {
                    Toast.makeText(this, "error loading model", Toast.LENGTH_SHORT).show();
                    return null;
                });
  
        // Load the face mesh texture.(2D texture on face)
        // Save the texture(.png file) in drawable folder.
        Texture.builder()
                .setSource(this, R.drawable.fox_face_mesh_texture)
                .build()
                .thenAccept(textureModel -> this.texture = textureModel)
                .exceptionally(throwable -> {
                    Toast.makeText(this, "cannot load texture", Toast.LENGTH_SHORT).show();
                    return null;
                });
  
        assert customArFragment != null;
  
        // This is important to make sure that the camera
        // stream renders first so that the face mesh
        // occlusion works correctly.
        customArFragment.getArSceneView().setCameraStreamRenderPriority(Renderable.RENDER_PRIORITY_FIRST);
        customArFragment.getArSceneView().getScene().addOnUpdateListener(frameTime -> {
            if (modelRenderable == null || texture == null) {
                return;
            }
            Frame frame = customArFragment.getArSceneView().getArFrame();
            assert frame != null;
  
            // Render the effect for the face Rendering the effect involves these steps:
            // 1.Create the Sceneform face node.
            // 2.Add the face node to the Sceneform scene.
            // 3.Set the face region Renderable. Extracting the face mesh and
            // rendering the face effect is added to a listener on
            // the scene that gets called on every processed camera frame.
            Collection augmentedFaces = frame.getUpdatedTrackables(AugmentedFace.class);
  
            // Make new AugmentedFaceNodes for any new faces.
            for (AugmentedFace augmentedFace : augmentedFaces) {
                if (isAdded) return;
  
                AugmentedFaceNode augmentedFaceMode = new AugmentedFaceNode(augmentedFace);
                augmentedFaceMode.setParent(customArFragment.getArSceneView().getScene());
                augmentedFaceMode.setFaceRegionsRenderable(modelRenderable);
                augmentedFaceMode.setFaceMeshTexture(texture);
                faceNodeMap.put(augmentedFace, augmentedFaceMode);
                isAdded = true;
  
                // Remove any AugmentedFaceNodes associated with 
                // an AugmentedFace that stopped tracking.
                Iterator> iterator = faceNodeMap.entrySet().iterator();
                Map.Entry entry = iterator.next();
                AugmentedFace face = entry.getKey();
                while (face.getTrackingState() == TrackingState.STOPPED) {
                    AugmentedFaceNode node = entry.getValue();
                    node.setParent(null);
                    iterator.remove();
                }
            }
        });
    }
}

输出:在物理设备上运行

Github项目链接: https : //github.com/raghavtilak/AugmentedFaces

Ar Core的局限性

  1. 增强脸部仅适用于前置摄像头。
  2. 并非所有设备都支持ARCore。仍然有一小部分不支持AR Core的设备。您可以在https://developers.google.com/ar/discover/supported-devices中查看ARCore支持的设备列表。
  3. 对于AR可选 App minSdkVersion应为14,AR要求的App minSdkVersion应为24。
  4. 如果您的应用属于“ AR必需”应用类别,则使用该应用的设备应已安装AR Core。
想要一个节奏更快,更具竞争性的环境来学习Android的基础知识吗?
单击此处,前往由我们的专家精心策划的指南,以使您立即做好行业准备!