ARUCOマーカーをiOSで使う

# シンプルに検出する方法

### 1、OpenCVをXcodeプロジェクトに入れる

色々方法ありますが、これがシンプルかと。
ARUCOを含んだopenCV-iOSフレームワークを以下からダウンロード。

https://drive.google.com/file/d/1SpEmjRedHKq4XxWXAXiLv8alnKq1cuJ7/view?usp=sharing

zipを解凍してopencv2.frameworkというファイル名にしてXcodeプロジェクトのファイルのところにドラッグドロップしてコピー。

### 2、Objective-Cファイルとヘッダーとブリッジング・ヘッダーを作る。
OpenCV.mm（Objective-Cファイル。拡張子はmからmmに変更）
OpenCV.h（ヘッダー）
{プロジェクト名}-Bridgeng-Header.h（Objective-Cファイルを作ると自動生成のポップアップが出る）
という３ファイルを作り、OpenCVの関数呼び出しコードを記述。

```obj-c:OpenCV.mm
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreVideo/CoreVideo.h>

#import "ArucoCV.h"
using namespace cv;
using namespace std;

@implementation ArucoCV

+(NSMutableArray *) detectAruco:(CVPixelBufferRef)pixelBuffer  {
    std::vector<int> ids;
    std::vector<std::vector<cv::Point2f>> corners;
    cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50);

    UIImage *uiImage = [self convertCVPixelBufferToUIImage:pixelBuffer];
    
    // UIImageをcv::Matに変換
    cv::Mat cvMat = [self convertUIImageToCVMat:uiImage];
    
    // グレースケールに変換
    cv::Mat grayMat = [self convertToGrayScale:cvMat];

    cv::aruco::detectMarkers(grayMat,dictionary,corners,ids);
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

    NSMutableArray *arrayMatrix = [NSMutableArray new];
    for (size_t i = 0; i < corners.size(); i++) {
        std::vector<cv::Point2f> markerCorners = corners[i];
        NSMutableArray *cornerPoints = [NSMutableArray new];
        
        for (size_t j = 0; j < markerCorners.size(); j++) {
            cv::Point2f corner = markerCorners[j];
            NSValue *pointValue = [NSValue valueWithCGPoint:CGPointMake(corner.x, corner.y)];
            [cornerPoints addObject:pointValue];
        }
        
        [arrayMatrix addObject:cornerPoints];
    }
    return arrayMatrix;
}
// CVPixelBufferからUIImageに変換する関数
+ (UIImage *)convertCVPixelBufferToUIImage:(CVPixelBufferRef)pixelBuffer {
    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
    CIContext *context = [CIContext context];
    CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent];
    UIImage *uiImage = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);
    return uiImage;
}

// UIImageからcv::Matに変換する関数
+ (cv::Mat)convertUIImageToCVMat:(UIImage *)image {
    cv::Mat cvMat;
    UIImageToMat(image, cvMat);
    return cvMat;
}

// グレースケールに変換する関数
+ (cv::Mat)convertToGrayScale:(cv::Mat)inputMat {
    cv::Mat grayMat;
    cv::cvtColor(inputMat, grayMat, cv::COLOR_BGR2GRAY);
    return grayMat;
}

@end
```

```obj-c++:OpenCV.h

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

NS_ASSUME_NONNULL_BEGIN

@interface ArucoCV : NSObject 

+(NSMutableArray *) detectAruco:(CVPixelBufferRef)pixelBuffer;

@end
```

```obj-c++:{プロジェクト名}-Bridgeng-Header.h
#import "ArucoCV.h"
```

### Swiftから呼び出す

```swift
let markers = ArucoCV.detectAruco(pixelBuffer)
var swiftMarkers: [[CGPoint]] = []
if let nsConcreteValueArray = markers as? [[NSValue]] { // 複数ARUCOマーカーがある場合、その数だけ[NSValue]が格納されている。
    for nsPointArrayInner in nsConcreteValueArray {
        var markerTopLeft: CGPoint = .zero // 一つのARUCOマーカーの
        nsPointArrayInner[0].getValue(&markerTopLeft)
        var markerTopRight: CGPoint = .zero
        nsPointArrayInner[1].getValue(&markerTopRight)
        var markerBottomRight: CGPoint = .zero
        nsPointArrayInner[2].getValue(&markerBottomRight)
        var markerBottomLeft: CGPoint = .zero
        nsPointArrayInner[3].getValue(&markerBottomLeft)

```

🐣
****
フリーランスエンジニアです。
AIについて色々記事を書いていますのでよかったらプロフィールを見てみてください。

もし以下のようなご要望をお持ちでしたらお気軽にご相談ください。
AIサービスを開発したい、ビジネスにAIを組み込んで効率化したい、AIを使ったスマホアプリを開発したい、
ARを使ったアプリケーションを作りたい、スマホアプリを作りたいけどどこに相談したらいいかわからない…

いずれも中間コストを省いたリーズナブルな価格でお請けできます。

お仕事のご相談はこちらまで
rockyshikoku@gmail.com

機械学習やAR技術を使ったアプリケーションを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)