iOSプログラミング with Swift 2


2016.06.17: created by

SwiftでOpenCV を利用して顔認識する(もっとも単純な例)

  1. OpenCV のiOS用バイナリ を用意します。今回は http://opencv.org/ で公開されている OpenCV for iOS から opencv2.framework.zip をダウンロードします。 学内からはこちらからが速くダウンロードできます。 展開して opencv2.framework フォルダを生成します。
  2. Xcode を起動して "Create a new Xcode project" で "Single View Application" として新しいプロジェクトを開きます。 ここではプロジェクト名を SwiftOpenCV としています。



  3. プロジェクトの最初に表示される画面は、ウィンドウの左側に表示されている "Show the project navigator" アイコンをクリックしても表示されます。 "General" -> "Linked Frameworks and Libraries" ->"+"ボタン を押して Add Other で OpenCV2.framework を追加します。















  4. (注意) 追加したframework が認識されない場合は、プロジェクトの "Build Settings" -> "Search Paths" -> "Framework Search Paths" の "Debug" と "Release" の両方に opencv2.framework が存在するパスを付け加えましょう。(例では /Users/nitta/doc/iApp/2016 )




  5. ウィンドウ左の Project Navigator のプロジェクト名の上で、マウスを左クリックします。 メニューの中から "New File..." を選択します。



  6. iOS -> Cocoa Touch Class -> Next を選ぶとクラス名の入力になります。ここではクラス名は ObjCWrapper、Subclass ofに NSObject, LauguageにObjective-C を選択しました。







    このあと「SwiftとObjective-Cをブリッジ(橋渡し)するHeaderを作るか」と確認を求められるので "Create Bridging Header" を選択します。 ブリッジングヘッダーファイルの名前は「プロジェクト名-Bridging-Header.h」となります。




    ObjCWrapper.h, ObjCWrapper.m, SwiftOpenCV-Bridging-Header.h が新たに生成されました。

  7. ObjCWrapper.m のファイル名をObjCWrapper.mm に変更します。 この変更によって ObjCWrapper.mm の中で C++ が使えるようになります。



  8. "プロジェクト名-Bridging-Header.h" (この例では "SwiftOpenCV-Bridging-Header.h") に Objective-C のヘッダファイルを imporotする記述を追加します。この記述によって、このプロジェクト内の Swift プログラムから Objective-C のクラスが参照できるようになります。
  9. SwiftOpenCV-Bridging-Header.hに追加するコード(赤字部分)
    #import "ObjCWrapper.h"
    
  10. ObjCWrapperクラスにプロパティやメソッドの定義を追加します。
  11. ObjCWrapper.h はブリッジヘッダにincludeされて Swift のプログラムから参照されるので、 ObjCWrapper.h に opencv2/opencv.hpp をincludeしてはいけないことに注意しましょう。 Foundation/Foundation.h だけが importされていますので UIKit/UIKit.h もimport します。
    ObjCWrapper.hに追加するコード(赤字部分)
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    @interface ObjCWrapper : NSObject
    
    - (bool) isActive;
    - (bool) setXML: (NSString *) name;
    - (int) detect: (UIImage *) image founds: (NSMutableArray *) arr;
    
    @end
    
    ObjCWrapper.mmに追加するコード(赤字部分)
    #import "ObjCWrapper.h"
    #import <opencv2/opencv.hpp>
    #import <opencv2/highgui/ios.h>
    
    using namespace std;
    
    @implementation ObjCWrapper
    
    cv::CascadeClassifier cascade;
    bool active;
    
    - (bool) isActive { return active; }
    
    - (bool) setXML: (NSString *) name {
        NSBundle *bundle = [NSBundle mainBundle];
        NSString *path = [bundle pathForResource: name ofType:@"xml"];
        string cascadeName = (char *) [path UTF8String];
        if (!cascade.load(cascadeName)) {
            return active = false;
        }
        return active = true;
    }
    
    - (int) detect: (UIImage *) image founds: (NSMutableArray *) arr {
        CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
        CGFloat cols = image.size.width;
        CGFloat rows = image.size.height;
        cv::Mat mat(rows,cols,CV_8UC4);
        CGContextRef contextRef = CGBitmapContextCreate(mat.data, cols, rows, 8, mat.step[0], colorSpace, kCGImageAlphaNoneSkipLast);
        CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
        CGContextRelease(contextRef);
        
        vector<cv::Rect> founds;
        cascade.detectMultiScale(mat, founds, 1.1, 2, CV_HAAR_SCALE_IMAGE,cv::Size(30,30));
        
        for (int i=0; i<founds.size(); i++) {
            cv::Rect rect = founds[i];
            [arr addObject: [NSNumber numberWithInteger: rect.x]];
            [arr addObject: [NSNumber numberWithInteger: rect.y]];
            [arr addObject: [NSNumber numberWithInteger: rect.width]];
            [arr addObject: [NSNumber numberWithInteger: rect.height]];
        }
        
        return (int) [arr count];
    }
    
    @end
    
  12. Main.storyboard のViewController 上に Button と Image Viewを配置します。



  13. Button は Action (Touch Up Inside) で "tapButton" 関数と connectし、 Image View は Outlet で "myImageView" 変数とconnect します。



  14. ViewController.swift を変更します。
  15. ViewController.swiftに追加するコード(赤字部分)
    import UIKit
    
    class ViewController: UIViewController {
        
        var detector: ObjCWrapper!
    
        @IBOutlet weak var myImageView: UIImageView!
        
        @IBAction func tapButton(sender: AnyObject) {
            let image: UIImage? = UIImage(named: "lena.jpg")
            myImageView.image = image;
            if (detector.isActive()) {
                let arr = NSMutableArray()
                detector.detect(image,founds: arr)
                print(arr.count)
                
                UIGraphicsBeginImageContext(image!.size);
                image!.drawInRect(CGRectMake(0,0,image!.size.width,image!.size.height))
                let context: CGContextRef = UIGraphicsGetCurrentContext()!
                CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0)
                CGContextSetLineWidth(context, 5.0);
                for i in 0..<(arr.count/4) {
                    let x:Int = arr[i * 4 + 0] as! NSNumber as Int
                    let y:Int = arr[i * 4 + 1] as! NSNumber as Int
                    let w:Int = arr[i * 4 + 2] as! NSNumber as Int
                    let h:Int = arr[i * 4 + 3] as! NSNumber as Int
                    print("\(i): \(x) \(y) \(w) \(h)")
                    CGContextAddRect(context, CGRectMake(CGFloat(x),CGFloat(y),CGFloat(w),CGFloat(h)));
                }
                CGContextStrokePath(context)
                let img = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                
                myImageView.image = img;
             }
         }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            detector = ObjCWrapper();
            detector.setXML("haarcascade_frontalface_alt")
       }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    
    }
    
  16. プロジェクトに、OpenCVの認識用 xml ファイル haarcascade_frontalface_alt.xml を追加します。
  17. ウィンドウ左のProject Navigator でプロジェクトを選択して、マウスの右クリックで "Add Files to プロジェクト名"を選択します。 ファイル選択の画面ではまず Option をクリックして "Copy items if needed" にチェックをいれてから、Addします。













  18. プロジェクトに、画像ファイル lena.jpg を追加します。
  19. ウィンドウ左のProject Navigator でプロジェクトを選択して、マウスの右クリックで "Add Files to プロジェクト名"を選択します。 ファイル選択の画面ではまず Option をクリックして "Copy items if needed" にチェックをいれてから、Addします。













  20. 実行すると Button が表示され、Image Viewは真っ白です。 ここでButtonを押すと、lena.jpg を読み込み、顔認識した領域が線で囲まれて表示されます。
    -->
  21. サンプルのプロジェクトはこちら。(Xcode 7.3.1版)。 (注意) ダウンロードしたプロジェクトを各自の環境で動かすには、 プロジェクトの Bulding Settings の Search Framework Paths に opencv2.framework の親フォルダへのパスを設定し直す必要があると思われます。
  22. OpenCV-2.4と同時に配布されている認識用のXMLファイルの一覧はこちら。 また、画像ファイル例はこちら


http://karel.tsuda.ac.jp