React NativeからCore MLを使用して画像分類するまで

この記事は [ハンズラボ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/handslab) 19日目の記事です。

@tuki0918です。

最近、趣味・お仕事で少しReact Nativeを触る機会があり、以前から気になっていたCore MLに手を出してみました。

最終的には自分で作成した学習モデルを使用して予測するようにしたかったのですが、変なところで5-6時間ハマってしまったため既存のモデルを動かすところまで対応としました。

React Nativeについては [こちら](https://facebook.github.io/react-native/)
Core MLについては [こちら](https://developer.apple.com/documentation/coreml)


# 成果物について

分類対象の画像と分類結果を表示する簡単な画面を作成しました
ソースコードについては [こちら](https://github.com/tuki0918/react-native-coreml) に置いていますので、基本的に要点のみ記載していきます。

![iPhone_8_Plus_-_11_2.png](https://qiita-image-store.s3.amazonaws.com/0/2642/ab62b72c-79c6-015e-3ea9-8aa7c50420ec.png)



# 目次

+ プロジェクトを作成する
+ Native Moduleの準備する
+ React NativeからNative Moduleにアクセスする
+ CoreMLのサンプルのモデルを準備
+ CoreMLの実装
+ React NativeからCore MLを呼び出す




# プロジェクトを作成する

今回のプロジェクトはXcodeを利用するため、下記コマンドを使用して進めています。

```
$ react-native init AwesomeProject
```



# Native Moduleの準備する

公式に[Objective-C と Swift のネイティブモジュールの作成方法](https://facebook.github.io/react-native/docs/native-modules-ios.html)が記載されています。
今回はCoreMLの実装が目的のため、PromisesとSwiftのネイティブモジュールの作成を参考に
プロジェクトのディレクトリ階層・ファイルが下記になるようにファイルを作成しました。

![RNCoreML_swift_と_MobileNet.png](https://qiita-image-store.s3.amazonaws.com/0/2642/20e3e57e-7428-0486-eca0-1b24e5cded9d.png)


```swift:RNCoreML.swift
import Foundation

@objc(RNCoreML)
class RNCoreML: NSObject {
  
  @objc(findEvents:findEventsWithResolver:rejecter:)
  func findEvents(name: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void {
    do {
      resolve(name);
    } catch {
      reject(nil, nil, nil);
    }
  }
  
}
```

```objc:RNCoreMLBridge.m
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(RNCoreML, NSObject)

RCT_EXTERN_METHOD(findEvents:(NSString *)name findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)

@end
```

```objc:AwesomeProject-Bridging-Header.h
#import <React/RCTBridgeModule.h>
```

最終的にはここから少し引数など変更しましたが、上記でもネイティブ側との動作確認はできると思います。


# React NativeからNative Moduleにアクセスする

JS側からは呼び出したいファイルで下記のように記載することでNative Moduleを利用できました。

```javascript
import { NativeModules } from 'react-native';
const RNCoreML = NativeModules.RNCoreML;

// promise なので async/awaitでもいいね
RNCoreML.findEvents('ABC').then(name => {
    console.log(name); // ABC
});
```



# CoreMLのサンプルのモデルを準備する

アップル公式の[こちら](https://developer.apple.com/machine-learning/)のサイトより
GoogLeNet, Inception v3, VGG16などよく目にする学習モデルが用意されていますが
モバイル向けのMobileNetをダウンロードし、Xcodeプロジェクトに追加しました。

![RNCoreML_swift_と_MobileNet2.png](https://qiita-image-store.s3.amazonaws.com/0/2642/3b929d7f-528f-cb16-c0d1-02df982f24bb.png)

ハマったポイントとして、mlmodelプロジェクトに追加すると自動でモデルのクラスを作成してくれますが、
実際にSwiftから呼び出す場合はヘッダーファイルにimport文を書く必要がありました。

```objc:AwesomeProject-Bridging-Header.h
#import <React/RCTBridgeModule.h>
#import "MobileNet.h"
```


# CoreMLの実装

基本的には他の方の実装を参考にしました。
しばらくの間、ちょこちょこ修正すると思うので興味のある方は下記コードを確認下さい。

https://github.com/tuki0918/react-native-coreml/blob/master/AwesomeProject/ios/RNCoreML.swift

筆者は Objective-C と Swift がほぼ解らないため、主に [こちら](https://dev.classmethod.jp/smartphone/ios-11-vision-framework-coreml/) の記事を参考にさせて頂きました




# React NativeからCore MLを呼び出す

元々あるApp.jsを少し修正する形で修正しました。
stateのimageURLに分類したい画像を入力することで、最初に紹介した成果物のような見た目になり、Core ML上で予測した値が反映されます。

```javascript:App.js
import React, { Component } from 'react';
import {
    Image,
    Platform,
    StyleSheet,
    Text,
    View,
    NativeModules
} from 'react-native';

const RNCoreML = NativeModules.RNCoreML;

export default class App extends Component<{}> {

    state = {
        imageURL: '分類したい画像のURL',
        identifier: '-',
        confidence: 0
    };

    componentDidMount() {
        this.predict();
    }

    /**
     * 画像の分類予測
     */
    predict = () => {
        const { imageURL } = this.state;
        RNCoreML.predict(imageURL).then(data => {
            this.setState({
                identifier: data.identifier,
                confidence: data.confidence
            });
        });
    };

    render() {

        const { imageURL, identifier, confidence } = this.state;

        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>
                    Welcome to React Native!
                </Text>
                <Image style={{width: 150, height: 150}} source={{uri: imageURL}} />
                <Text style={styles.instructions}>
                    identifier: {identifier}
                </Text>
                <Text style={styles.instructions}>
                    confidence: {confidence}
                </Text>
            </View>
        );
    }
}

... 略

```


# 最後に

最近はアウトプットしてなかったこともあり、なかなか書く内容が決まりませんでした。
Qiita自体も3年ぶりの投稿だったようです、2018年はアウトプットを少しずつ増やしていきたいですね。

今回のソースコードは [こちら](https://github.com/tuki0918/react-native-coreml) です。
※ カメラを活用したものや自分で作成した学習モデルで試せていないため、
　しばらくの間は時間を見つけて内容を修正・機能を追加などするかもしれません。


ハンズラボ [ハンズラボ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/handslab) 明日20日目は @kurapy-n です！よろしくお願いします。