版權聲明:Davidwang原創文章,嚴禁用於任何商業途徑,授權後方可轉載。
前節所述使用“標準模型”匹配人臉以檢測人臉姿態是衆多人臉姿態檢測方法中的一種,實際上,人臉姿態估計還有很多,如柔性模型法、非線性迴歸法、嵌入法等等 。在計算機視覺中,頭部姿勢估計是指推斷頭部朝向,結合AR位置信息構建人臉矩陣參數的能力,有利因素是人體頭部運動範圍是有限的,可以藉此消除一些誤差。
(一)人臉姿態
人臉姿態估計主要是獲得臉部朝向的角度信息,爲快速的檢測到人臉姿態,通常預先定義一個6關鍵點的3D臉部模型(左眼角,右眼角,鼻尖,左嘴角,右嘴角,下頜),如下圖所示,然後使用計算機圖像算法檢測出視頻幀中人臉對應的6個臉部關鍵點,並將這6個關鍵點與標準模型中對應的關鍵點進行擬合,解算出旋轉向量,最後將旋轉向量轉換爲歐拉角。理論上流程如此,但在實際中,可能並不能同時檢測到人臉的6個關鍵點(由於遮擋、視覺方向、光照等原因),因此人臉姿態估計通常還會進行信息推測來提高魯棒性。
慶幸的是,在ARFoundation中,我們完全可以不用去了解這些底層細節,AR Face Manager已處理好了這一切,AR Face Manager組件如下圖所示。
AR Face Manager負責檢測人臉,但其使用底層Provider提供的算法進行檢測,不同的底層對人臉檢測的要求也不一樣,一般都是使用前置攝像頭,ARCore在使用前置攝像頭時會禁用後置攝像頭,因此所有平面檢測、運行跟蹤、環境理解、圖像跟蹤等等都會失效,爲提高性能,最好禁用這些功能。而新的ARKit則可以同時使用前置與後置攝像頭。
與其他 Trackable Manager一樣,AR Face Manager在檢測到人臉時會使用人臉的姿態(位置與方向)實例化一個預製體,即上圖中的Face Prefab屬性,這個屬性可以不設置,但如果設置後,在實例化預製體時,AR Face Manager會負責爲該預製體掛載一個ARFace組件,ARFace組件包含了檢測到的人臉的相關信息。Maximum Face Count屬性用於設置在同一場景中檢測的人臉數量,ARCore與ARKit都是支持多人臉檢測,設置較小的值有助於性能優化。
AR Face Manager提供了一個supported屬性用於檢測用戶設備是否支持人臉檢測,當前並不是所有的設備都支持人臉檢測功能。AR Face Manager還提供了facesChanged事件,我們可以通過註冊事件來獲取到新檢測、更新、移除的人臉信息,進行個性化的處理。AR Face Manager對每一個檢測到的人臉生成一個唯一的TrackableId,利用TrackableId可以跟蹤每一張獨立的人臉。
另外,在人臉檢測和後續過程中,ARFoundation會自動進行環境光檢測與估計,不需要開發人員手動編寫相關代碼。
在ARFoundation中,AR Face Manager完成了人臉姿態檢測(使用底層Provider算法),我們可以通過以下方法驗證其人臉姿態檢測效果。首先製作一個交點在原點的三軸正交座標系Prefab,如下圖所示。
新建一個場景,選中Hierarchy窗口的AR Session Origin對象,爲其掛載AR Face Manager組件,其Maximum Face Count設置爲1,並將剛纔製作的三軸座標系Prefab賦給Face Prefab屬性。編譯運行,效果如下圖所示。
從上圖可以看出,ARFoundation檢測出來的人臉姿態座標原點位於人臉鼻尖位置,Y軸向上,Z軸向裏,X軸向左。對比上面兩張圖可以看到,X軸朝向是反的,這是因爲我們使用的是前置攝像頭,這種反轉X軸朝向的處理在後續人臉上掛載虛擬物體時可以保持虛擬物體正確的三軸朝向。
(二)人臉網格
除了人臉姿態,ARFoundation還提供了每一個檢測到的人臉網格,該網格可以覆蓋檢測到的人臉形狀,這張網格也是由底層SDK提供,包括頂點、索引、法向、紋理座標等相關信息,如下圖所示。ARCore 人臉網格包含468個頂點,ARKit人臉網格則包含1220個頂點,對普通消費級應用來講已經足夠了。利用這些網格,開發者只需要操控網格上的座標和特定區域的錨點就可以輕鬆的在人臉上附加一些特效,如面具、 眼鏡、虛擬帽子,或者對人臉進行扭曲等等。
在ARFoundation中,人臉網格信息由ARFace組件提供,而人臉網格的可視化則由ARFaceMeshVisualizer組件實現,該組件會根據ARFace提供的網格數據更新顯示網格信息。顯示人臉網格與顯示模型非常類似,需要先製作一個預製件Prefab,區別是這個預製件中沒有模型,因爲網格信息與網格可視化都由專門的組件負責實現。
在Hierarchy窗口中新建一個空對象,命名爲FaceMesh,並在其上掛載ARFace與ARFaceMeshVisualizer組件,同時掛載渲染組件Mesh Renderer和Mesh Filter,如下圖所示。
我們的目標是使用京劇臉譜渲染網格,爲了將眼睛與嘴露出來,我們需要提前將眼睛與嘴的部分鏤空,並將圖像保存爲png格式,在渲染時需要將鏤空部分剔除掉,因此,編寫一個Shader如下。
Shader "DavidWang/FaceShader" {
Properties{
_MainTex("Texture", 2D) = "white" {}
}
SubShader{
Tags{ "Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
Cull Off
CGPROGRAM
#pragma surface surf Standard vertex:vert fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
float y : TEXCOORD0;
};
void vert(inout appdata_full v,out Input o) {
o.uv_MainTex = v.texcoord.xy;
o.y = v.vertex.y;
}
void surf(Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Alpha = c.w;
clip(o.Alpha - 0.1);
o.Albedo = c.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
在 Shader 代碼中,剔除透明區域使用的是 clip(x)函數,該函數功能是丟棄 x 小於 0 的像素。然後我們新建一個使用該Shader的材質,命名爲face,並賦上相應紋理。
將FaceMesh製作成Prefab,並刪除Hierarchy窗口中的FaceMesh對象,最後一步將FaceMesh預製體賦給Hierarchy窗口中AR Session Origin對象下AR Face Manager組件的Face Prefab懺悔,編譯運行,效果如下圖所示。
進一步思考,AR Face Manager組件的只有一個Face Prefab屬性,只能實例化一類人臉網格,如果要實現川劇中的變臉效果該如何處理?
前文提到,AR Face Manager有一個facesChanged 事件,因此我們可以註冊這個事件來實現我們需要的功能。思路如下:現實變臉就是要更換掉渲染的紋理,因此我們只需要在AR Face Manager檢測到人臉的added事件中,在其實例化人臉網格之前替換掉相應的紋理即可。爲此,我們需要製作幾張類似的紋理,並放置在Resources文件夾中以便運行時加載。新建一個C#腳本,命名爲ChangeFace,編寫如下代碼。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
[RequireComponent(typeof(ARFaceManager))]
public class ChangeFace : MonoBehaviour
{
private GameObject mFacePrefab;
ARFaceManager mARFaceManager;
private Material _material;
private int TextureIndex = 0;
private Texture2D[] mTextures = new Texture2D[3];
private void Awake()
{
mARFaceManager = GetComponent<ARFaceManager>();
}
void Start()
{
mFacePrefab = mARFaceManager.facePrefab;
_material = mFacePrefab.transform.GetComponent<MeshRenderer>().material;
mTextures[0] = (Texture2D)Resources.Load("face0");
mTextures[1] = (Texture2D)Resources.Load("face1");
mTextures[2] = (Texture2D)Resources.Load("face2");
}
private void OnEnable()
{
mARFaceManager.facesChanged += OnFacesChanged;
}
void OnDisable()
{
mARFaceManager.facesChanged -= OnFacesChanged;
}
void OnFacesChanged(ARFacesChangedEventArgs eventArgs)
{
foreach (var trackedFace in eventArgs.added)
{
OnFaceAdded(trackedFace);
}
/*
foreach (var trackedFace in eventArgs.updated)
{
OnFaceUpdated(trackedFace);
}
foreach (var trackedFace in eventArgs.removed)
{
OnFaceRemoved(trackedFace);
}
*/
}
private void OnFaceAdded(ARFace refFace)
{
TextureIndex = (++TextureIndex) % 3;
_material.mainTexture = mTextures[TextureIndex];
// Debug.Log("TextureIndex:" + TextureIndex);
}
}
上述代碼的主要邏輯就是在AR Face Manager實例化人臉網格之前替換紋理,將該腳本也掛載在Hierarchy窗口中的AR Session Origin對象上。編譯運行,當手在臉前拂過時,虛擬人臉網格紋理就會隨之替換,效果如圖所示。