茶漬けの技術メモ

Golang, Rubyで趣味開発します。テックニュース書いたり。ガジェット触ったり。

【Swift】OpenCVを使ってみる 4 〜テンプレートマッチングしてみる〜

この記事は

【Swift】OpenCVを使ってみる 1 〜導入編〜 - 茶漬けの技術メモ

【Swift】OpenCVを使ってみる 2 〜バージョンを表示〜 - 茶漬けの技術メモ

【Swift】OpenCVを使ってみる 3 〜カラー画像をグレースケールに変換〜 - 茶漬けの技術メモ

これらの記事の続きとなっております。

それでは今回は、OpenCVを使って、画像の中からテンプレートマッチングによって画像の一部を見つけ出し、描画するところまでしようと思います。

ヘルパークラスの作成

テンプレートマッチングの前に、UIImage型とcv::Mat型にそれぞれ変換するメソッドを定義したヘルパークラスを作成します。


OpenCVHelper.mm

#import "OpenCVHelper.h"
#import <opencv2/opencv.hpp>
#import <opencv2/highgui/ios.h>

@implementation OpenCVHelper

+ (cv::Mat)cvMatFromUIImage:(UIImage *)image
{
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
    CGFloat cols = image.size.width;
    CGFloat rows = image.size.height;
    
    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
    
    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to  data
                                                    cols,                       // Width of bitmap
                                                    rows,                       // Height of bitmap
                                                    8,                          // Bits per component
                                                    cvMat.step[0],              // Bytes per row
                                                    colorSpace,                 // Colorspace
                                                    kCGImageAlphaNoneSkipLast |
                                                    kCGBitmapByteOrderDefault); // Bitmap info flags
    
    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
    CGContextRelease(contextRef);
    CGColorSpaceRelease(colorSpace);
    
    return cvMat;
}

+ (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
    CGColorSpaceRef colorSpace;
    
    if (cvMat.elemSize() == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }
    
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
    
    // Creating CGImage from cv::Mat
    CGImageRef imageRef = CGImageCreate(cvMat.cols,                                 //width
                                        cvMat.rows,                                 //height
                                        8,                                          //bits per component
                                        8 * cvMat.elemSize(),                       //bits per pixel
                                        cvMat.step[0],                              //bytesPerRow
                                        colorSpace,                                 //colorspace
                                        kCGImageAlphaNone|kCGBitmapByteOrderDefault,// bitmap info
                                        provider,                                   //CGDataProviderRef
                                        NULL,                                       //decode
                                        false,                                      //should interpolate
                                        kCGRenderingIntentDefault                   //intent
                                        );
    
    // Getting UIImage from CGImage
    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);
    
    return finalImage;
}
@end


OpenCVHelper.h

#import <UIKit/UIKit.h>
#import <opencv2/opencv.hpp>

@interface OpenCVHelper : NSObject

+ (cv::Mat)cvMatFromUIImage:(UIImage *)image;
+ (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat;
@end


テンプレートマッチングを行い描画

OpenCVHelperをしっかりインポートし、実装していきます。


OpenCVWrapper.mm

#import "OpenCVWrapper.h"
#import "OpenCVHelper.h"
#import <opencv2/opencv.hpp>
#import <opencv2/highgui/ios.h>

@implementation OpenCVWrapper

+(NSString *) openCVVersionString
{
    return [NSString stringWithFormat: @"openCV Version %s", CV_VERSION];
}

+(UIImage * ) makeGrayFromImage:(UIImage *)image
{
    // transform UIImagge to cv::Mat
    cv::Mat imageMat;
    UIImageToMat(image, imageMat);
    
    // if the image already grayscale, return it
    if(imageMat.channels() == 1)return image;
    
    // transform the cv::Mat color image to gray
    cv::Mat grayMat;
    cv::cvtColor (imageMat, grayMat, CV_BGR2GRAY);
    
    return MatToUIImage(grayMat);
}

+ (UIImage *)match :(UIImage *)srcImage templateImage:(UIImage *)templateImage {
    
    cv::Mat srcMat = [OpenCVHelper cvMatFromUIImage:srcImage];
    cv::Mat tmpMat = [OpenCVHelper cvMatFromUIImage:templateImage];
    
    // 入力画像をコピー
    cv::Mat dst = srcMat.clone();
    
    // マッチング
    cv::matchTemplate(srcMat, tmpMat, dst, cv::TM_CCOEFF);
    
    double min_val, max_val;
    cv::Point min_loc, max_loc;
    cv::minMaxLoc(dst, &min_val, &max_val, &min_loc, &max_loc);
    
    // 結果の描画
    cv::rectangle(srcMat, max_loc, cv::Point(max_loc.x + tmpMat.cols, max_loc.y + tmpMat.rows), CV_RGB(0, 255, 0), 2);
    
    return [OpenCVHelper UIImageFromCVMat:srcMat];
}


OpenCVWrapper.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface OpenCVWrapper : NSObject

// funciton to get opencv version
+(NSString * ) openCVVersionString;

// function to convert image to grayscale
+(UIImage * ) makeGrayFromImage:(UIImage * ) image;

// function to detect
+ (UIImage *)match :(UIImage *)srcImage templateImage:(UIImage *)templateImage;

@end


ViewController.swift

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var openCVVersionLabel: UILabel!
    @IBOutlet weak var birdImageView: UIImageView!
    let original_image = UIImage(named: "bird.jpg")
    let bird_head = UIImage(named: "bird_head.jpg")
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        openCVVersionLabel.text = OpenCVWrapper.openCVVersionString()
    }

    @IBAction func detect(_ sender: UIButton) {
        birdImageView.image = OpenCVWrapper.match(original_image, templateImage: bird_head)
    }
}


ここまで実装できたら、アプリを実行しボタンをタップします。

すると、

これが、、

f:id:biwako_no_otyazuke:20161127124522p:plain:w400


こうなります

f:id:biwako_no_otyazuke:20161127124551p:plain:w400



画像から、鳥の頭の部分をテンプレートマッチングにより、見つけだすことができました!

次回は特徴点の抽出を行いたいと思います!!

続きはこちら

【Swift】OpenCVを使ってみる 5 〜特徴点を検出してみる〜 - 茶漬けの技術メモ


Thanks

OpenCV for iOS でウォーリーを探すカメラアプリをつくる - Qiita

OpenCV for iOSの使い方 - Qiita