前回に続いてARKit関連の記事になります。
今回は Face Tracking を使ってみたいと思います。
※ Face Tracking は iPhone X 系でのみ動作します
Xcodeを立ち上げ、新規プロジェクトから Augmented Reality App を選択します。
storyboard 上の ViewController に TableView を追加します。
ここでは始めからある SceneView を小さくしていますが、これはどちらでもいいです。
(前面には表示しませんが、Face Tracking するには画面上に存在する必要があります)
ViewController は下記のように記述します。
import UIKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate, UITableViewDataSource {
private let cellIdentifier = "cell"
private let defaultConfiguration: ARFaceTrackingConfiguration = {
let configuration = ARFaceTrackingConfiguration()
return configuration
}()
@IBOutlet var sceneView: ARSCNView!
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
sceneView.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
sceneView.session.run(defaultConfiguration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
private func scrollUpInMainThread() {
DispatchQueue.main.async {
self.tableView.contentOffset = CGPoint(x: 0, y: self.tableView.contentOffset.y + 10)
}
}
private func scrollDownInMainThread() {
DispatchQueue.main.async {
self.tableView.contentOffset = CGPoint(x: 0, y: self.tableView.contentOffset.y - 10)
}
}
// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
cell.textLabel?.text = "セル " + indexPath.row.description
return cell
}
// MARK: - ARSCNViewDelegate
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor else {
return
}
if let lookUpLeft = faceAnchor.blendShapes[.eyeLookUpLeft] as? Float {
if lookUpLeft > 0.3 {
self.scrollDownInMainThread()
}
}
if let lookUpRight = faceAnchor.blendShapes[.eyeLookUpRight] as? Float {
if lookUpRight > 0.3 {
self.scrollDownInMainThread()
}
}
if let lookDownLeft = faceAnchor.blendShapes[.eyeLookDownLeft] as? Float {
if lookDownLeft > 0.3 {
self.scrollUpInMainThread()
}
}
if let lookDownRight = faceAnchor.blendShapes[.eyeLookDownRight] as? Float {
if lookDownRight > 0.3 {
self.scrollUpInMainThread()
}
}
}
}
Face Tracking に関連する部分のみ簡単に解説します。
sceneView.session.run(defaultConfiguration) にて Face Tracking モードで SceneView のセッションをスタートさせます。
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) は AR空間上のノードが更新されると呼び出されます。
今回の場合は顔が認識されると呼び出されることになります。
if let lookUpLeft = faceAnchor.blendShapes[.eyeLookUpLeft] as? Float { の部分で左目が上を見ている度合いを取得します。
この値は 0 ~ 1 の間であり、このコードでは 0.3 を超えると「上を向いている」と判断しています。
他についても同様です。
ビルドして実機で動かすとこんな感じになりました。
※この動作中、一切画面を触っていません
ちなみに実際に使ってみると目の負担が大きく、微調整もできないので、このままだと実用性はほぼ皆無ですね。。。
ただ、視線の検知はとても面白いと思うので、なにかイイモノが作れないか模索中です。
※この記事の内容は https://3jino-oyatsu.com/blog/120/ からお引越ししたものです