はじめに
iOSエンジニアをやっていますわたなべと申します。 現在私は、フォト事業部の方で自動撮影アプリというアプリの開発に携わっています。 自動撮影アプリというのは、保育園で保育士さんがBluetoothカメラとiPodを使って園児の様子を自動で撮影するアプリになります。いろいろと課題があるので、下記のようなことを考えてみました。
やりたい事
iPodには、カメラが付いているので、そのカメラを使って撮影できないか? という訳で、iPodの背面カメラを使用し、カメラ内に顔が検出できたら、数秒間、動画データから全てのフレームを画像として保存する。ということをやりたいと思います。
カメラを使用する
今まで、カメラを使用したアプリを作った事がなかったので、ここから軽く説明していこうと思います。 カメラを使用するにはAVFoundationをインポートします。 そして、入力と出力を管理するAVCaptureSessionを生成します。 まず、入力はAVCaptureDeviceを用いて、使用するカメラを設定します。(今回は背面カメラを使用して、フレームレートを1/30秒に設定しています。) デバイスの設定が終わったら、これを入力デバイスとしてAVCaptureSessionに追加します。 続いて、出力です。出力は、カメラの入力を動画データとして扱いたいので、AVCaptureVideoDataOutputを使用します。 AVCaptureVideoDataOutputでは、ビデオ出力などを設定し、setSampleBufferDelegate(〜)を設定すると AVCaptureVideoDataOutputSampleBufferDelegateプロトコルで、メソッドcaptureOutput(〜)がフレーム毎に呼ばれますので、ここで顔検出を行います。(1フレームづつ画像に変換して顔検出を行います。) こちらも設定が終わったらAVCaptureSessionに追加します。
そして、キャプチャした映像をプレビュー表示するために、AVCaptureVideoPreviewLayerを設定して、
キャプチャースタートです!
最後に、Info.plistにカメラを使用する事を忘れずに…
顔検出方法
顔の検出方法は、iOS11から搭載されているVisionフレームワークを使用します。
Vision.フレームワークでは、いろいろな検出ができるので、 まず、どのような検出を行うかを要求して、(Request) 何に対して、検出処理をするか指定して実行、(RequestHandler) そして、検出結果を処理します。(Observation)
そこで、Visionで顔検出を行う際に重要になってくるクラスがあります。 今回は、顔を検出したいので、下記のクラスを使用しています。
役割 | クラス | 説明 |
---|---|---|
Request | VNDetectFaceRectanglesRequest | 顔検出を要求します。 |
RequestHandler | VNImageRequestHandler | 上記のRequestに対する検出処理を実行するクラスです。 |
Observation | VNFaceObservation | Requestに対する検出結果を処理をするクラスです。 |
下記が実際のコードになります。(顔検出部分を抜粋)
1: let faceRectanglesRequest = VNDetectFaceRectanglesRequest(completionHandler: { (request, error) in 2: for observation in request.results as! [VNFaceObservation] { 3: /// observationには検出した顔の矩形が入ってくる 4: /// 検出できた顔の部分に矩形などを表示する処理を行う 5: } 6: }) 7: 8: if let cgImage = image.cgImage { 9: let handler = VNImageRequestHandler(cgImage: cgImage, options: [:]) 10: try? handler.perform([faceRectanglesRequest]) 11: }
- 1行目でどのようなRequestを要求するか指定しRequestを作成します。今回は、顔検出なので、VNDetectFaceRectanglesRequestを使います。
- ちょっと飛んで9行目から説明します。検出したい画像を指定しVNImageRequestHandlerを作成します。 ここでの解析したい画像は後ほど説明します。
- 10行目でVNDetectFaceRectanglesRequestで生成した要求に対して顔検出を開始します。
- 検出した結果が、2行目のrequest.resultsに入ってきます。
上記の処理を「カメラを使用する」でも説明したcaptureOutput(〜)内で処理を行っています。 captureOutput(〜)の最初で1フレームづつ画像に変換し8,9行目で使用しているimageがそれになり、検出を実行しています。
最後に
当初の目的の顔検出ができた時、そのあと数秒間のフレームを画像に書き出すという処理を追加しているので、目的は達成できたかとは思うのですが、まだまだ、改良の余地ありですね。
今回は、駆け足で説明してしまったので細かい所、顔を検出した所に枠を出すとか、検出した結果の顔の座標は反転しているとか、いろいろと注意しなければいけない点が多々ありますが、今後の課題としていろいろと試していければと思っています。