LoginSignup
4
5

More than 3 years have passed since last update.

SwiftでARマーカー (ArUco) を検出する

Last updated at Posted at 2020-04-14

はじめに

Swift で OpenCV の ArUco モジュールを使ってARマーカーを検出するコードを書きました。
ArUcoモジュールによるARマーカー検出は、マーカーの向きや傾きによる影響が少なく、非常に認識精度が高いです。検出したマーカーのから得られる情報にしたがって進行方向を決定する動体の実装などに使えそうです。

SwiftにOpenCVを導入する

こちらの記事を参考にしてください。
Swiftで OpenCV の ArUco モジュールを使う

プログラム

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

@interface opencvWrapper : NSObject {
    UIImage *_image;
    NSDictionary *_markerDict;
}
- (UIImage *)image; // 検出されたマーカーを描画した画像
- (NSDictionary *)markerDict; // 検出されたマーカーの (id, rect) の辞書

//(返り値の型 *)関数名:(引数の型 *)引数名;;
- (void)detectARMarker:(UIImage *)input_img;
@end

Objective-C++のヘッダーファイルです。
Swiftから検出されたARマーカーが描画された画像と、ARマーカーのIDおよび座標が取得しやすいように、インスタンス変数として_image、_markerDictを定義しています。

OpencvWrapper.mm
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import <opencv2/aruco.hpp>
#import "opencvWrapper.h"
using namespace cv;
using namespace std;

@implementation opencvWrapper
- (UIImage *)image { return _image; };
- (NSDictionary *)markerDict { return _markerDict; }

- (void)detectARMarker:(UIImage *)input_img {
    Mat mat;
    UIImageToMat(input_img, mat);
    cvtColor(mat, mat, CV_RGBA2RGB);
    vector<int> ids;
    vector<vector<Point2f> > corners;

    // ArUcoモジュールに事前定義されたARマーカーの辞書を取得する
    Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);

    // detectMarkers でARマーカーを検出する
    // ids に ARマーカーのID、corners に ARマーカーの座標を格納する
    aruco::detectMarkers(mat, dictionary, corners, ids);

    // mat (元の画像) に 検出されたARマーカーを描画する
    if (ids.size() > 0)
        aruco::drawDetectedMarkers(mat, corners, ids); // draw bounding boxes.

    // Mat型の画像を Swiftで扱えるUIImage型に変換
    UIImage * output_img = MatToUIImage(mat);
    _image = output_img;

    // ids と corners を {id<NSNumber>: corner<NSArray[CGPoint]>} の形式の辞書に変換してmutableDictに格納する
    NSMutableDictionary *mutableDict =  [@{} mutableCopy];
    for (int i=0; i<ids.size(); i++) {
        auto id = ids[i];
        NSNumber* markerId = [NSNumber numberWithInt:id];
        NSMutableArray* corner = [[NSMutableArray alloc] initWithCapacity:corners[i].size()];
        for (auto point: corners[i]) {
            [corner addObject:[NSValue valueWithCGPoint:CGPointMake(point.x, point.y)]];
        }
        mutableDict[markerId] = corner;
    }
    _markerDict = mutableDict;
}
@end

Objective-C++の実装ファイルです。
詳しい説明はコード中のコメントとして記述しています。
detectMarkers関数によるARマーカーの検出結果は、検出されたARマーカーのIDとcorners(四隅の座標)の形で得られ、drawDetectedMarkers関数によって画像の中にARマーカーの四隅を直線で結んだ枠とidを描画しています。

ViewController.swift

import UIKit

class ViewController: UIViewController {

    let openCV = opencvWrapper()
    var imageView: UIImageView?
    var detectBtn: UIButton?

    override func viewDidLoad() {
        super.viewDidLoad()
        let image = UIImage(named: "armarkertest")
        self.imageView = UIImageView(image: image)
        self.imageView?.frame = CGRect(x: 75, y: 200, width: 250, height: 250)
        self.view.addSubview(self.imageView!)

        self.detectBtn = UIButton(type: .system)
        self.detectBtn?.frame = CGRect(x: 75, y: 550, width: 250, height: 90)
        self.detectBtn?.setTitle("Detect", for: .normal)
        self.detectBtn?.setTitleColor(UIColor.white, for: .normal)
        self.detectBtn?.titleLabel?.font = .systemFont(ofSize: 24)
        self.detectBtn?.backgroundColor = UIColor.blue
        self.detectBtn?.addTarget(self, action: #selector(detect(_:)), for: .touchUpInside)
        self.view.addSubview(self.detectBtn!)

    }

    @objc func detect(_ sender: UIButton) {
        openCV.detectARMarker(self.imageView?.image)
        let aruco_img = openCV.image()
        self.imageView?.image = aruco_img

        guard let markers = openCV.markerDict() else { return }
        if let markerDict = markers as? [Int : Array<CGPoint>] {
            for (key,data) in markerDict {
                print("id: \(key), corner: \(data)\n")
            }
        }
    }
}

ARマーカーが複数個映った画像を画面上部のUIImageViewに配置し、その下に「Detect」ボタンを配置しています。
「Detect」ボタンを押下すると、OpencvWrapper.mm の detectARMarker関数が実行され、UIImageViewの画像がARマーカー検出結果が反映されたものに変わります。
また、同時にARマーカーのIDと座標の情報が標準出力に表示されます。

実行結果は以下です。

「Detect」前
image.png

「Detect」後
少し小さいですが、画像の中のARマーカーが緑の枠で囲われ、マーカーIDが赤字で記されています。
image.png

標準出力

id: 62, corner: [(233.0, 273.0), (190.0, 273.0), (196.0, 241.0), (237.0, 241.0)]

id: 40, corner: [(359.0, 310.0), (404.0, 310.0), (410.0, 350.0), (362.0, 350.0)]

id: 124, corner: [(425.0, 163.0), (430.0, 186.0), (394.0, 186.0), (390.0, 162.0)]

id: 203, corner: [(195.0, 155.0), (230.0, 155.0), (227.0, 178.0), (190.0, 178.0)]

id: 98, corner: [(427.0, 255.0), (469.0, 256.0), (477.0, 289.0), (434.0, 288.0)]

id: 23, corner: [(298.0, 185.0), (334.0, 186.0), (335.0, 212.0), (297.0, 211.0)]

参考

4
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5