[Swift]GoogleのAR CoreのFaceTrackingのサンプルのコード読んでみた(前編)
AR Core(Faceサンプル)のファイル構成
・FacesViewController
・FaceMeshGeometryConverter
FacesViewController
viewDidLoad
if !setupScene() {
return
}
if !setupCamera() {
return
}
if !setupMotion() {
return
}
シーン、カメラ、モーションが起動しなければアプリはここで落ちます
do {
faceSession = try GARAugmentedFaceSession(fieldOfView: videoFieldOfView)
} catch {
alertWindowTitle = "A fatal error occurred."
alertMessage = "Failed to create session. Error description: \(error)"
popupAlertWindowOnError(alertWindowTitle: alertWindowTitle, alertMessage: alertMessage)
}
Google謹製のARsessionをfetchしようと試みますが失敗するとエラーを吐きます。
videoFieldOfViewはFloat型の変数です。現時点では無視でおk
viewDidAppear
・判定用BoolであるviewDidAppearReachedがtrueになります
・エラーが発生したらpopupします
◆setupScene()
guard let faceImage = UIImage(named: "Face.scnassets/face_texture.png"),
let scene = SCNScene(named: "Face.scnassets/fox_face.scn"),
let modelRoot = scene.rootNode.childNode(withName: "asset", recursively: false)
else {
alertWindowTitle = "A fatal error occurred."
alertMessage = "Failed to load face scene!"
popupAlertWindowOnError(alertWindowTitle: alertWindowTitle, alertMessage: alertMessage)
return false
}
・顔面に貼り付けるためのNodeをここで呼び出しており、見つからないとエラーを吐きます
foreheadLeftNode = modelRoot.childNode(withName: "FOREHEAD_LEFT", recursively: true)
foreheadRightNode = modelRoot.childNode(withName: "FOREHEAD_RIGHT", recursively: true)
noseTipNode = modelRoot.childNode(withName: "NOSE_TIP", recursively: true)
faceNode.addChildNode(faceTextureNode)
faceNode.addChildNode(faceOccluderNode)
scene.rootNode.addChildNode(faceNode)
・Nodeの定義と描画
let cameraNode = SCNNode()
cameraNode.camera = sceneCamera
scene.rootNode.addChildNode(cameraNode)
sceneView.scene = scene
sceneView.frame = view.bounds
sceneView.delegate = self
sceneView.rendersContinuously = true
sceneView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
sceneView.backgroundColor = .clear
// Flip 'x' to mirror content to mimic 'selfie' mode
sceneView.layer.transform = CATransform3DMakeScale(-1, 1, 1)
view.addSubview(sceneView)
・CameraNodeを追加し、sceneViewのプロパティを行います。ポイントになりそうなのはsceneViewのバックグラウンドが透明なところとか。発想的にはセル画のアニメみたいに現実世界の映像とCGの映像のレイヤーを切り離していそうです。
faceTextureMaterial.diffuse.contents = faceImage
// SCNMaterial does not premultiply alpha even with blendMode set to alpha, so do it manually.
faceTextureMaterial.shaderModifiers =
[SCNShaderModifierEntryPoint.fragment: "_output.color.rgb *= _output.color.a;"]
faceOccluderMaterial.colorBufferWriteMask = []
return true
・マテリアルとシェーダーの設定です。モデリング勢なら馴染み深い概念。
・問題なければtrueを返します。falseだと起動できません
◆setUpCamera()
private func setupCamera() -> Bool {
guard
let device =
AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
else {
alertWindowTitle = "A fatal error occurred."
alertMessage = "Failed to get device from AVCaptureDevice."
popupAlertWindowOnError(alertWindowTitle: alertWindowTitle, alertMessage: alertMessage)
return false
}
カメラを呼び出す準備をします。出来なければエラーを返します。
guard
let input = try? AVCaptureDeviceInput(device: device)
else {
alertWindowTitle = "A fatal error occurred."
alertMessage = "Failed to get device input from AVCaptureDeviceInput."
popupAlertWindowOnError(alertWindowTitle: alertWindowTitle, alertMessage: alertMessage)
return false
}
・カメラのインプットを呼び出す準備をします。出来なければエラーを返します。
let output = AVCaptureVideoDataOutput()
output.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
output.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: .userInteractive))
let session = AVCaptureSession()
session.sessionPreset = .high
session.addInput(input)
session.addOutput(output)
captureSession = session
captureDevice = device
videoFieldOfView = captureDevice?.activeFormat.videoFieldOfView ?? 0
cameraImageLayer.contentsGravity = .center
cameraImageLayer.frame = self.view.bounds
view.layer.insertSublayer(cameraImageLayer, at: 0)
・アウトプットやセッションのコンフィグをすましていきます。 videoFieldOfViewにデバイスのview数を代入しておきます。
getVideoPermission(permissionHandler: { granted in
guard granted else {
NSLog("Permission not granted to use camera.")
self.alertWindowTitle = "Alert"
self.alertMessage = "Permission not granted to use camera."
self.popupAlertWindowOnError(
alertWindowTitle: self.alertWindowTitle, alertMessage: self.alertMessage)
return
}
self.captureSession?.startRunning()
})
return true
}
・ビデオを使う設定をユーザーに許可されていなければここでエラーを返します。そうでなければデバイスを起動します。
◆setUpMotion()
private func setupMotion() -> Bool {
guard motionManager.isDeviceMotionAvailable else {
alertWindowTitle = "Alert"
alertMessage = "Device does not have motion sensors."
popupAlertWindowOnError(alertWindowTitle: alertWindowTitle, alertMessage: alertMessage)
return false
}
motionManager.deviceMotionUpdateInterval = 0.01
motionManager.startDeviceMotionUpdates()
return true
}
・アプリのダウンロードされた端末にモバイルセンサーが付いていなければエラーを投げます。クッソ面倒な事前準備してこのエラー吐き出されたらキレそうですね
・なお、対応端末は公式サイトから確認できます。ちゃんと見てませんがARKitを難なく使いこなせていれば基本ARCoreを動かせるはずです。確かSE2くらいからAR Core使えると思います
・deviceMotionUpdateIntervalではフレーム数を指定できます
getVideoPermission()
private func getVideoPermission(permissionHandler: @escaping (Bool) -> Void) {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
permissionHandler(true)
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video, completionHandler: permissionHandler)
default:
permissionHandler(false)
}
}
・カメラの使用許可の状況によって対応を分岐させます
今回はここまで
この記事が気に入ったらサポートをしてみませんか?