見出し画像

Unityアプリ(iOS)でCoreMLを使う

「Unityアプリ(iOS)」で「CoreML」を使って画像分類を行う方法をまとめました。iOSプラグインの作成方法については、「UnityのiOSプラグインの作成」を参照。

1. 画像分類を行うクラスの作成

Xcodeを使って、画像分類クラス「ImageClassifier.swift」を作成します。Unityに組み込む前に、動作確認も行います。

◎ モデル
モデルはAppleの提供する「MobileNetV2Int8LUT.mlmodel」を使います。

◎ 画像
テスト用の画像も用意してプロジェクトに追加します。

・cat.jpg (256 x 256)

画像1


◎ ImageClassifier.swift
画像分類を行うコード「ImageClassifier.swift」を作成します。predict() にbase64形式の画像とコールバックを渡すと、推論結果をコールバックで返します。

import UIKit
import Vision

// 画像分類器
@objc public class ImageClassifier: NSObject {
    @objc public static let shared = ImageClassifier() // シングルトン
    var model = try! VNCoreMLModel(for: MobileNetV2Int8LUT().model) // モデル

    // 予測
    @objc public func predict(_ base64: String!, onCallback: ((NSString)->Void)!) {
        // base64デコード
        let data:Data! = Data(base64Encoded: base64, options:
            Data.Base64DecodingOptions.ignoreUnknownCharacters)

        //リクエストの生成
        let request = VNCoreMLRequest(model: self.model) {
            request, error in
            // エラー処理
            if error != nil {
                onCallback(error!.localizedDescription as NSString)
                return
            }
           
            // 検出結果の取得
            let observations = request.results as! [VNClassificationObservation]
            var text: String = "\n"
            for i in 0..<min(3, observations.count) { //上位3件
                let probabillity = Int(observations[i].confidence*100) //信頼度
                let label = observations[i].identifier //ID
                text += "\(label) : \(probabillity)%\n"
            }
            
            // コールバック
            onCallback(text as NSString)
        }
       
        // リクエストの実行
        let handler = VNImageRequestHandler(data:data, options:[:])
        guard (try? handler.perform([request])) != nil else {return}
    }
}

◎ ViewController.swift
テスト用のコード「ViewController.swift」も準備します。

import UIKit

// 画像分類のテスト
class ViewController: UIViewController {

   // ビュー表示時に呼ばれる
   override func viewDidAppear(_ animated: Bool) {
       // イメージの読み込み
       let image:UIImage! = UIImage(named:"cat")
       
       // base64エンコード
       let jpgData:Data! = image.jpegData(compressionQuality: 1.0);
       let base64:String! = jpgData.base64EncodedString(options:
           Data.Base64EncodingOptions.lineLength64Characters);

       // 推論
       ImageClassifier.shared.predict(base64) {result in
           print(result as String);
       }
   }
}

◎ 実行

Egyptian cat : 12%
Angora, Angora rabbit : 10%
Siamese cat, Siamese : 7%

2. Unityプロジェクトの準備

Unityプロジェクトを準備します。

◎ 画像
画像「cat.jpg」を Assets/StreamingAssets に配置します。

◎ UI
UnityのUIを準備します。

(1) 「RawImage」を追加し、「ImageView」と名前を指定。
(2) 「Text」を追加し、「Label」と名前を指定。
(3) 空の「GameObject」を追加し、「ImageClassifier」と名前を指定し、スクリプト「ImageClassifier.cs」を追加。

画像2

「ImageClassifier.cs」のコードは次のとおり。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;

public class ImageClassifier : MonoBehaviour
{
    [DllImport("__Internal")]
    private static extern void Predict(string base64);

    // 参照
    public RawImage imageView;
    public Text label;

    // イメージ
    private byte[] imageData;
    private string result = "";

    // スタート時に呼ばれる
    public void Start()
    {
        // 画像の読み込み
        this.imageData = File.ReadAllBytes(Application.streamingAssetsPath+"/cat.jpg");

        // 画像の表示
        Texture2D texture = new Texture2D(256, 256);
        texture.LoadImage(this.imageData);
        this.imageView.texture = texture;

        // base64エンコード
        string base64 = System.Convert.ToBase64String(this.imageData);

        // 推論
        Predict(base64);
    }

    // 結果の取得
    public void OnResult(string result)
    {
        label.text = result;
    }
}

◎ iOSプラグイン
iOSプラグイン「ImageClassifier.mm」をAssets/Pluginsに配置します。

 #import <UnityFramework/UnityFramework-Swift.h>

 #ifdef __cplusplus
 extern "C" {
 #endif
 void Predict(char* base64) {
     NSString *base64Str = [NSString stringWithCString:base64 encoding:NSUTF8StringEncoding];
     [[ImageClassifier shared] predict:base64Str onCallback:^(NSString *str){
         UnitySendMessage("ImageClassifier", "OnResult", (char *)[str UTF8String]);
     }];
 }
 #ifdef __cplusplus
 }
 #endif

◎ Swiftコード
先程作成した「ImageClassifier.swift」をAssets/Plugins/iOSに配置します。

◎ ビルド
ビルドして、Xcodeプロジェクトを生成します。

3. モデルの追加

Xcodeプロジェクト生成後に、モデルの追加作業を行います。
(自動化したいけどできてない。)

(1) Xcodeプロジェクトに、「MobileNetV2Int8LUT.mlmodel」をドラッグ&ドロップ。ターゲットは「UnityFramework」を選択。

画像3

(2) 「UnityFramwork」の「General」でiOSのバージョンを「12」に上げる。
「ImageClassification.swift」で、iOS12以降の機能を使ってるので。

画像4

(3) Projectの「Build Settings」の「CoreML Model Compiler - Code Generation」の「CoreML Model Class Generation Language」で「Swift」を選択。
Objective-CとSwiftが混じってるプロジェクトのせいか、選択しないとエラーになります。

画像5

4. 実行

成功すると、以下のような画面が表示されます。

画像6


この記事が気に入ったらサポートをしてみませんか?