去年 Apple 推出的新功能中,其中一個最創新的功能就是原深感測鏡頭 (True Depth Camera)。原深感測鏡頭對軟硬體工程師來說,支援了 FaceID 這個安全的面部識別系統;而對於開發者,原深感測鏡頭就開創了許多可能性,尤其是在基於臉部互動 (face-base interactions) 這方面。
在開始這篇 ARKit 教學之前,讓我快速說明一下相機的不同部分。原深感測鏡頭和大部分的 iPhone/ iPad 的前置相機一樣,有內嵌麥克風、具七百萬像素的相機、環境光源偵測器 (ambient light sensor)、距離感應器 (proximity sensor)、與揚聲器。而原深感測鏡頭最獨特的地方,就是獨有的測繪點投射器 (dot projector)、泛光照射器 (flood illuminator)、與紅外線鏡頭 (infrared camera)。
測繪點投射器會投射超過 3 萬個隱形測繪點到使用者的臉上,以建構成一個面部測繪圖(文章後部會提到);而紅外線相機就會讀取測繪點,擷取成一張紅外線影像,並傳送資料到 Apple A12 Bionic 處理器作比對;最後,泛光照射器就會利用紅外線,方便在黑暗環境辨識容貌。
這些不同的感測元件整合在一起,就可以創造如 Animojis 和 Memojis 這種神奇體驗。有了原深感測鏡頭,我們就可以利用使用者臉部和頭部的 3D 模型,為 App 建立更多特殊效果。
教學範例專案
我相信對於開發者而言,最重要的就是學會使用原深感測鏡頭,應用臉部追蹤功能,為使用者建構令人驚艷的臉部辨識體驗。在本篇教學中,我將會說明如何利用 ARKit 框架中的 ARFaceTrackingConfiguration
,去透過 3 萬個測繪點辨識不同的臉部動作。
最終成果會是這樣的:
![true-depth-camera-demo](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-1-1024x461.png)
讓我們開始吧!
你需要在 iPhone X、XS、XR、或 iPad Pro(第三代)上執行這個專案,因為只有這些機型才支援原深感測鏡頭。文章中,我們會使用 Swift 5 和 Xcode 10.2。
建立 ARKit 範例來追蹤臉部動作
首先,打開 Xcode 建立一個新 Xcode 專案。在 Templates 下,請確認選擇的是 iOS 的 Augmented Reality App。
接著,請為專案命名。我把專案命名為 True Depth。請確認你設定了 Language 為 Swift,Content Technology 為 SceneKit。
![arkit-face-tracking-1](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-3-1024x607.png)
前往 Main.storyboard
, 這裡應該有一個 Single View,而當中的 ARSCNView
應該已經連接到程式碼上。
![arkit-face-tracking-2](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-4-1024x607.png)
我們真正需要做的其實非常簡單!我們只需要加入一個 UIView
,再在 View 內加入一個 UILabel
。這個 Label 將會告知使用者,他們正在展示甚麼臉部表情。
將 UIView
拖拉至 ARSCNView
內,然後來設定條件。將 Width 設定為 240pt,Height 為 120pt,然後再設定 Left 與 Bottom 為 20pt。
![arkit-face-tracking-3](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-5-1024x607.png)
為了美觀的設計,讓我們將 View 的 alpha 設為 0.8。現在,將 UILabel
拉到你剛完成的 view 內,再將所有的邊設為 8pt。
![arkit-face-tracking-4](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-6-1024x576.png)
最後,把 Label 對齊設置到中間。設置好後,你的 storyboard 應該會像這樣:
![arkit-face-tracking-5](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-7-1024x607.png)
現在,讓我們來設定 IBOutlets
與 ViewController.swift
連接。轉換到 Assistant editor 模式,按住 Control 鍵點選 UIView
和 UILabel
,然後將它們拉到 ViewController.swift
來建立 IBOutlets
。
![arkit-face-tracking-6](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-8-1024x607.png)
你應該可以建構兩個 outlet:faceLabel
和 labelView
。
![arkit-face-tracking-7](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-9-1024x607.png)
建立臉部網格 (Face Mesh)
因為我們選擇了 Augmented Reality App 為程式碼範本,範本內有些不需要的程式碼,所以讓我們先把程式碼整理一下吧。將 viewDidLoad
方法改成這樣:
override func viewDidLoad() {
super.viewDidLoad()
// 1
labelView.layer.cornerRadius = 10
sceneView.delegate = self
sceneView.showsStatistics = true
// 2
guard ARFaceTrackingConfiguration.isSupported else {
fatalError("Face tracking is not supported on this device")
}
}
依照原來的範本,程式碼將會讀取一個 3D scene;但我們並不需要這個 scene,所以我們刪除了它。然後,我們可以在 project navigator 內刪除 art.scnassets
資料夾。最後,我們增加兩段程式碼到 viewDidLoad
方法內:
- 首先,我們會把
labelView
的邊角設置為圓角。這其實只是一個設計偏好。 - 接著,我們需要確認機型是否支援
ARFaceTrackingConfiguration
。這是我們用來建立臉部網格的 AR 追蹤功能,如果我們沒有確認的話,程式就會崩潰;而如果使用的機型不支援設定的話,就將會呈現錯誤訊息。
接著,我們將在 viewWillAppear
方法內改變一行程式碼:將常數 configuration
改為 ARFaceTrackingConfiguration()
。如此一來,你的程式碼應該會像這樣:
![face-mesh-1](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-10-1024x607.png)
然後,我們需要加入 ARSCNViewDelegate
方法。將下列程式碼加到 // MARK: - ARSCNViewDelegate
的下方:
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let faceMesh = ARSCNFaceGeometry(device: sceneView.device!)
let node = SCNNode(geometry: faceMesh)
node.geometry?.firstMaterial?.fillMode = .lines
return node
}
這個程式碼將在 ARSCNView
呈現時執行。首先,我們建立 sceneView
的臉部圖形資訊,並將它設給一個常數 faceMesh
。然後,我們將此圖形資訊指派給 SCNNode
,並設置材料 (material) 為 node
。對於 3D 物件而言,這個材料通常是 3D 物件的顏色或紋理 (texture)。
為了建構臉部網格,你可以使用兩種材料:填滿材料 (fill material) 或線材料 (lines material)。我比較傾向使用線材料,所以在程式碼中設定了 fillMode = .lines
,但你可以自由選用。現在你的程式碼應該像是這樣:
![face-mesh-2](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-11-1024x607.png)
如果你執行這個 App,你應該會看到這樣的畫面:
![face-mesh-demo](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-12-1024x743.png)
更新臉部網格
你可能會注意到,臉部網格並沒有隨著你的表情變化(如眨眼、微笑、或打哈欠等)而更新。這是因為我們還需要在 renderer(_nodeFor)
方法下,加入一個 renderer(_didUpdate:)
。
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
if let faceAnchor = anchor as? ARFaceAnchor, let faceGeometry = node.geometry as? ARSCNFaceGeometry {
faceGeometry.update(from: faceAnchor.geometry)
}
}
每當 sceneView
更新,這段程式碼就會執行。首先,我們定義一個 faceAnchor
為 sceneView
內臉部被偵測到的錨點 (anchor)。這個錨點是當執行臉部追蹤 AR session 時,被偵測臉部的姿態 、拓撲 (topology)、表情的資料。我們也定義一個叫 faceGeometry
的常數,這是為被偵測臉部的拓撲資料。利用這兩個常數,我們就可以每次都更新 faceGeometry
。
再次執行程式碼,現在每當你改變臉部表情時,臉部網格就會以 60 幀率 (fps) 更新。
![face-mesh-update](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-14-1024x694.png)
分析臉部表情變化
首先,在檔案最頂部建立一個變數:
var analysis = ""
接著,在檔案最下方建立下列函式:
func expression(anchor: ARFaceAnchor) {
// 1
let smileLeft = anchor.blendShapes[.mouthSmileLeft]
let smileRight = anchor.blendShapes[.mouthSmileRight]
let cheekPuff = anchor.blendShapes[.cheekPuff]
let tongue = anchor.blendShapes[.tongueOut]
self.analysis = ""
// 2
if ((smileLeft?.decimalValue ?? 0.0) + (smileRight?.decimalValue ?? 0.0)) > 0.9 {
self.analysis += "You are smiling. "
}
if cheekPuff?.decimalValue ?? 0.0 > 0.1 {
self.analysis += "Your cheeks are puffed. "
}
if tongue?.decimalValue ?? 0.0 > 0.1 {
self.analysis += "Don't stick your tongue out! "
}
}
上面函式以一個 ARFaceAnchor
為一個函數。
blendShapes
是一個命名系數的字典,根據特定臉部表情變化,表示檢測到的臉部表情。Apple 提供超過 50 種以上的系數,來偵測不同的臉部表情變化。以我們的需求而言,我們僅會使用 4 種:mouthSmileLeft
、mouthSmileRight
、cheekPuff
、和tongueOut
。- 我們使用系數來確認臉部執行這些表情的概率。為了檢測微笑,我們加入嘴巴的右側和左側的概率。我發現,設定微笑的概率為 0.9、臉頰和舌頭的概率為 0.1 的效果最好。
我們採用可能的值,並將文本添加到 analysis
字串中。
我們已經建立好方法了,現在來更新 renderer(_didUpdate:)
方法吧!
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
if let faceAnchor = anchor as? ARFaceAnchor, let faceGeometry = node.geometry as? ARSCNFaceGeometry {
faceGeometry.update(from: faceAnchor.geometry)
expression(anchor: faceAnchor)
DispatchQueue.main.async {
self.faceLabel.text = self.analysis
}
}
}
現在每當 sceneView
更新,就會執行 expression
方法。因為這個函數設定 analysis
字串,所以我們終於可以設定 faceLabel
的文本到 analysis
字串上。
我們完成所有的程式碼囉!執行程式碼,你應該會得到文章開頭的成果。
![true-depth-camera-demo](https://www.appcoda.com/wp-content/uploads/2019/07/facial-recognition-17-1024x743.png)
總結
利用 ARKit 開發基於臉部的使用者體驗這個功能,背後還有很多可能性。遊戲和 App 可以將原深感測鏡頭用於各種用途。我最愛的其中一個 App 就是 Hawkeye Access,這是一個讓使用者可以用眼睛來控制的瀏覽器。
如果你想了解更多關於原深感測鏡頭的相關資訊,你可以看看 Apple 的官方影片 Face Tracking with ARKit。你也可以在 GitHub 上下載本次教學的專案。