Medium - Face Detection and Recognition With CoreML and ARKit

원문 - Omar M’Haimdat

Trend 파악을 Medium 기고문 요약 포스팅 - ARKit의 기능을 이용한 얼굴 감지와 CoreML 모델을 이용한 얼굴 인식 구현

500x400

Create a Single View Application

Create a single view app

싱글 뷰 앱으로 프로젝트를 만들어서 시작해 봅시다.

이제 프로젝트가 생기셨겠죠? 그런데 저는 스토리보드를 사용하는 것을 좋아하지 않습니다. 앱은 프로그래밍에 따라서 완성될 것입니다. 토글하기 위한 스위치나 버튼 없이 순수한 코드만 있다는 말이죠🤗

여러분은 main.storyboard를 삭제하고 AppDelegate.swift를 다음과 같이 설정해 주세요.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.makeKeyAndVisible()
        let controller = ViewController()
        window?.rootViewController = controller

        return true
    }

deployment info에 storyboard “Main”이라고 적힌걸 지우셨는지 확인해 주세요.

Create Your Scene and Add It to the Subview

이 앱에는 앱의 시작점이 되는 뷰컨트롤러 하나만 가지게 될 것입니다.

이 단계에서 우리는 카메라의 라이브 영상을 자동적으로 화면 배경이 되도록 하기 위해서 ARKit를 import하고 ARSCNView 인스턴스가 필요합니다. ARSCNView 인스턴스는 자동적으로 SceneKit camera를 움직여서 기기의 실제 움직임과 일치시킵니다. 따라서 우리는 화면을 추가하기 위해 물체의 움직임을 추적하지 않아도 되는 것이죠.

여기서는 카메라 세션이 전체 화면을 차지하게끔 화면 영역을 지정해 줍니다.

//ARSCNView 초기화
let sceneView = ARSCNView(frame: UIScreen.main.bounds)

ViewDidLoad 메소드에서는 델리게이트나 프레임 드랍을 감시하기 위한 프레임 통계 등을 설정합니다.

self.view.addSubview(sceneView) // ARSCNView를 서브뷰로 추가합니다.
sceneView.delegate = self // 해당 뷰컨트롤러를 델리게이트로 설정합니다.
sceneView.showsStatistics = true // 통계를 표시하도록 합니다.

Start an ARFaceTrackingConfiguration session

ARFaceTrackingConfiguration을 사용해서 세션을 시작할 것입니다. 이 설정은 iPhone X, Xs, Xr에서 사용 가능한 front-facing TrueDepth 카메라에 접근할 수 있도록 해줍니다.

ViewDidLoad 메소드는 아래와 같습니다.

override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(sceneView)
        sceneView.delegate = self
        sceneView.showsStatistics = true
        guard ARFaceTrackingConfiguration.isSupported else { return }
        let configuration = ARFaceTrackingConfiguration()
        configuration.isLightEstimationEnabled = true
        sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
    }

Train a Face recognition medel

CoreML에 적합한 .mlmodel 파일을 만드는 방법은 여러가지가 있습니다. 다음의 방법들이 일반적인 예 입니다.

  1. Turicreate: 간단하게 커스텀 머신러닝 모델을 개발하기 위한 파이썬 라이브러리 입니다. 더욱 중요한 것은 .mlmodel 파일을 Xcode로 파싱할 수 있다는 것이죠.
  2. MLImageClassifierBuilder(): Xcode에서 사용할 수 있는 build-in 솔루션 이며 드래그 앤 드롭 인터페이스로 관련된 간단한 모델을 학습 시킬 수 있습니다.

MLImageClassifierBuilder

저는 충분한 데이터 셋이 없었기 때문에 위의 두 솔루션을 가지고 테스트를 진행했고 최종적으로 67장의 저의 사진과 261장의 다른사람의 얼굴 데이터를 가지고 MLImageClassifierBuilder를 사용하기로 결정했습니다.

플레이 그라운드를 켜서 아래의 코드를 작성해보세요.

import CreateMLUI

let builder = MLImageClassifierBuilder()
builder.showInLiveView()

저는 최대 iterations를 20으로 설정하시고 각 이미지마다 크롭된 4장의 인스턴스를 추가해주는 크롭 인수를 사용하시길 추천드립니다.

Capture Camera Frames and Inject Them Into the model

우리의 뷰컨트롤러를 scene delegate를 사용해 확장해야 합니다. 2개의 델리게이트 메소드가 필요한데 하나는 얼굴을 감지하는 것이고 다른 하나는 얼굴이 감지 되었을 때 화면을 업데이트 하는 것입니다.

Face detection:

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {

        guard let device = sceneView.device else {
            return nil
        }

        let faceGeometry = ARSCNFaceGeometry(device: device)

        let node = SCNNode(geometry: faceGeometry)

        node.geometry?.firstMaterial?.fillMode = .lines

        return node
    }

불행하게도 제가 눈이나 입을 열어도 화면이 업데이트 되지 않더라구요, 이럴 때는 코드를 이용해서 업데이트를 해줘야 합니다.

Update the scene:

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {

        guard let faceAnchor = anchor as? ARFaceAnchor,
            let faceGeometry = node.geometry as? ARSCNFaceGeometry else {
                return
        }

        faceGeometry.update(from: faceAnchor.geometry)
}

우리는 얼굴 전체를 도형으로 매핑했고 그 노드를 업데이트 합니다.

Get the camera frames:

ARSCNView가 AVCaptrueSession으로 부터 상속되기 때문에 우리는 cvPixelBuffer를 사용해서 우리의 모델에 전달할 수 있습니다.

아래는 sceneView에서 픽셀 버퍼를 가져오는 쉬운 방법 입니다.

guard let pixelBuffer = self.sceneView.session.currentFrame?.capturedImage else { return }

Inject camera frames into the model:

이제 우리는 얼굴을 감지하고 매 카메라 프레임을 가지고 있습니다. 이제 모델에게 컨텐트를 피드할 준비가 되었군요.

guard let model = try? VNCoreMLModel(for: FaceRecognition3().model) else {
            fatalError("Unable to load model")
        }

        let coreMlRequest = VNCoreMLRequest(model: model) {[weak self] request, error in
            guard let results = request.results as? [VNClassificationObservation],
                let topResult = results.first
                else {
                    fatalError("Unexpected results")
            }

            DispatchQueue.main.async {[weak self] in
                print(topResult.identifier)
            }
        }

        guard let pixelBuffer = self.sceneView.session.currentFrame?.capturedImage else { return }


        let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
        DispatchQueue.global().async {
            do {
                try handler.perform([coreMlRequest])
            } catch {
                print(error)
            }
        }

Show the Name Above the Recognized face

조금 실망스러울 수도 있는 마지막 파트는 인식된 얼굴 위에 3D 텍스트를 띄우는 것입니다. 생각해보시면 알겠지만 우리가 한 설정은 ARWorldTrackingConfiguration 처럼 많은 메소드와 클래스를 사용할 수 있는 강력한 것이 아닙니다. 우리는 전면 카메라를 이용해서 아주 일부만 사용할 수 있죠.

그럼에도 불구하고 우리는 3D 텍스트를 화면에 띄울겁니다, 아마 얼굴 움직임을 따라서 적절하게 움직이진 않겠지만요.

let text = SCNText(string: "", extrusionDepth: 2)
let font = UIFont(name: "Avenir-Heavy", size: 18)
text.font = font
let material = SCNMaterial()
material.diffuse.contents = UIColor.black
text.materials = [material]
text.firstMaterial?.isDoubleSided = true

let textNode = SCNNode(geometry: faceGeometry)
textNode.position = SCNVector3(-0.1, -0.01, -0.5)
textNode.scale = SCNVector3(0.002, 0.002, 0.002)
textNode.geometry = text

이제 우리는 SCNText object가 생겼습니다. 일치하는 얼굴을 가지고 업데이트를 해서 rootNode:에 추가합시다.

let coreMlRequest = VNCoreMLRequest(model: model) {[weak self] request, error in
            guard let results = request.results as? [VNClassificationObservation],
                let topResult = results.first
                else {
                    fatalError("Unexpected results")
            }

            DispatchQueue.main.async {[weak self] in
                print(topResult.identifier)
                if topResult.identifier != "Unknown" {
                    text.string = topResult.identifier
                    self!.sceneView.scene.rootNode.addChildNode(textNode)
                    self!.sceneView.autoenablesDefaultLighting = true
                }
            }
        }

Final Result:

아래의 이미지는 얼굴 감지와 인식에 대한 최종 결과물 입니다.

500x400

Github Project - omarmhaimdat/WhoAreYou

Summary

  • iPhone X, Xs, Xr의 전면 카메라의 얼굴 추적기능을 이용하여 얼굴을 도형으로 맵핑하고 모델에게 해당 정보를 피드할 수 있음
  • 파이썬 라이브러리를 사용해서 CoreML을 통해 모델에게 학습을 시킬 수 있음
  • 얼굴 추적 기능을 이용해 얼굴 정보를 맵핑하고 머신러닝을 통해 얼굴 인식을 학습시키는 간단한 앱

© 2019. All rights reserved.

Powered by Hydejack v8.1.1