[Swift] CIFaceFeatureで顔を検出してマスクをつけてみた

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

iOS7から追加されたCIFaceFeatureですが、画像の中の人の顔を見つけ出し、顔に関する情報を取得する事が出来ます。

今回はタイトルの通り、CIFaceFeatureで写真の中の人の顔を検出して上からマスク画像をかぶせてみました。


↑こんな感じで(。・ω・。)

実装

画面構成

プロジェクトはSingleViewApplicationで作成します。 そして、storyboardは下図のように配置しました。

Main_storyboard_—_Edited

ViewController.swift に UIImageViewのアウトレットを設定します。

Insert_Connection_と_Main_storyboard_—_Edited

また、ボタンのアクションも登録します。

Insert_Connection_と_Main_storyboard_—_Edited_2

素材の準備

今回はmask@2x.pngという名前の透過PNGを用意しました。

mask@2x

Images.xcassetsを開いて画像をドラックすればOKです。

Images_xcassets

ソースコード

import UIKit

class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
    /** 写真表示用ImageView */
    @IBOutlet weak var photoImageView: UIImageView!
    /** 画像取得用 */
    var picker:UIImagePickerController?
    /** 画像認識 */
    var detector:CIDetector?
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        detector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])
        picker = UIImagePickerController()
        picker!.delegate = self
        picker!.allowsEditing = false
        messageTextView.resignFirstResponder()
    }
    /**
    * アルバムボタンをタップ
    */
    @IBAction func onTapAlbumButton(sender: AnyObject)
    {
        //ライブラリが使用できるかどうか判定
        if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary))
        {
            self.presentViewController(picker!, animated: true, completion: nil)
        }
    }
    /**
    * 写真選択
    */
    func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!){
        self.dismissViewControllerAnimated(true, completion: nil)
        //画像を変換
        var ciImage:CIImage = CIImage(image: image)
        // グラフィックスコンテキスト生成
        UIGraphicsBeginImageContextWithOptions(image.size, true, 0);
        // 読み込んだ写真を書き出す
        image.drawInRect(CGRectMake(0, 0, image.size.width, image.size.height))
        // 取得するパラメーターを指定する
        let options = [CIDetectorSmile : true, CIDetectorEyeBlink : true]
        // 画像内に顔があるか調べる
        let features = detector?.featuresInImage(ciImage, options: options)
        for feature in features as [CIFaceFeature] {
            // マスク画像
            let uiImage = UIImage(named:"mask")
            var faceRect = feature.bounds
            // 位置の補正
            faceRect.origin.y = image.size.height - faceRect.origin.y - faceRect.size.height
            // 顔の場所に書き出す
            uiImage?.drawInRect(faceRect)
        }
        // グラフィックスコンテキストの画像を取得
        var outputImage = UIGraphicsGetImageFromCurrentImageContext()
        // グラフィックスコンテキストの編集を終了
        UIGraphicsEndImageContext();
        // 画像を出力
        photoImageView.image = outputImage
    }
    
    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
    }
}

以下のループ内で顔があるか判定し、顔のサイズ/位置を取得してマスク画像を出力してます。 (CoreImageの座標系は左下が(0,0)になるため、位置の補正を行っています)

	let features = detector?.featuresInImage(ciImage, options: options)
        for feature in features as [CIFaceFeature] {
            // マスク画像
            let uiImage = UIImage(named:"mask")
            var faceRect = feature.bounds
            // 位置の補正
            faceRect.origin.y = image.size.height - faceRect.origin.y - faceRect.size.height
            // 顔の場所に書き出す
            uiImage?.drawInRect(faceRect)
        }

今回は位置情報(bounds)しか使ってませんが、CIFaceFeatureでは以下の情報が取れる様です。笑顔とか取得できるのが興味深いです。

プロパティ 説明
bounds CGRect 顔の大きさ/位置情報
faceAngle float 顔の傾き
leftEyePosition CGPoint 左目の位置
rightEyePosition CGPoint 右目の位置
mouthPosition CGPoint 口の位置
hasSmile BOOL 笑顔かどうか
leftEyeClosed BOOL 左目が閉じているかどうか
rightEyeClosed BOOL 右目が閉じているかどうか

CIFaceFeature Class Reference

実行結果について

人が写っている写真で試してみました。

outputimage

顔を判定してマスク画像がついたのですが、 結果を見るまで頭全体が取れると勘違いしてました。。。顔面エリア(?)が取れる感じですね。
袋を被っている感じにしたかった。。。顔面なら、お面っぽい画像にすればよかった・・・(´・ω・`)

最後に

CIFaceFeatureですが、最初は位置情報くらいしか取得できないと思っていました。想像以上に色々なプロパティがあって楽しかったです。
また精度について個人的な所感を。 試してみたところ、顔が認識されやすい人とされにくい人が居る印象を受けました。また横顔は認識されず、斜め顏は認識される時とされない時がありました。

参考