omuriceman's blog

iOS / AWS / Firebase / JavaScript / Deep Learning を中心とした技術を面白く紹介します!

iOSで手軽に物体検出とトラッキングをするにはML Kit for Firebaseの新機能が便利!

f:id:omuriceman:20190531142328p:plain

ハッカソン参加に向けて最近あまり触っていなかった画像の検出周りを調査していたところ、FirebaseがiOS/Androidで簡単にオブジェクト(物体)検出とトラッキングができる面白い機能をリリースしていました。

firebase.google.com

今回はこの機能について紹介いたします。

ML Kit for Firebaseとは

Firebaseで機械学習系の機能を実装できるライブラリです。

firebase.google.com

ざっくりこんなことができます。

  • テキスト認識(OCR
  • 顔検出
  • バーコード スキャン
  • 画像のラベル付け
  • ロゴ認識
  • ランドマーク認識
  • 不適切なコンテンツの検出
  • 類似画像の検索
  • カスタムモデルの推論

「デバイス上だけで動作する機能(On-device)」と「クラウドと連携して動作する機能(Cloud)」があるのでご注意ください。

日本語のページでは「画像のラベル付け(ラベル検出)」はクラウドのみチェックされていますがデバイスAPIもあり、日本語ページと英語のページで差異があります。ちゃんと調べたい方は英語のページの閲覧を推奨します。

そして先日の「Google I/O」で新たに物体(オブジェクト)検出とトラッキング機能(Object detection & tracking)が追加されました。

f:id:omuriceman:20190531192917g:plain

オブジェクト検出とトラッキングとは

画像やライブ映像から物体を検出します。Firebaseの公式サイトの機能説明には「Tracking the most prominent object across images」とあります。翻訳してみると「画像の中で1番目立つものを追跡する」ということになります。

この物体検出とトラッキング機能ですがFirebase以外にも色々あり、代表的なものではiOS12標準の機能でオブジェクト検出ができます。

developer.apple.com

サンプルコードも存在するので上記のURLからダウンロードして実行をしてみてください。どうやら認識できるのが朝食で出てくる物(バナナ・コーヒー・クロワッサンなど)だけのようです。もっと汎用的な物体検出をするには自分でモデルを構築する必要があります。

そのためにデータを用意したりアノテーションしたり。軽く試すには少し面倒ですよね。

Firebaseの物体検出とトラッキングは上記のような面倒さが一切なく、汎用的な画像検出とトラッキングが可能になっております。

サンプルアプリをダウンロード

Firebaseの物体検出のサンプルアプリを起動してみましょう。こちらからcloneできます。

github.com

2019/5/29現在、ダウンロードしたファイルの中に2つのプロジェクトが存在しています。

  • ShowcaseApp
  • TranslateDemo

今回は物体検出のみの紹介となりますので、ShowcaseAppを使用します。TranslateDemoはOCR(文字認識)と翻訳ができるアプリです。

アプリを実行する前にFirebaseの管理画面上で新規アプリを作成し「GoogleService-Info.plist」ファイルを取り込む必要があります。 そちらについての説明は省きますが、わからない方は下記のリンクを参考にしてみてください。

firebase.google.com

機能の説明

コードの主要な部分の解説をしていきます。AVFoundationとFirebaseを忘れずにimportしてください。サンプルコードがObjective-Cのため、解説もそちらをベースにしております。

// Calculate the image orientation
FIRVisionDetectorImageOrientation orientation;

// Using front-facing camera
AVCaptureDevicePosition devicePosition = AVCaptureDevicePositionFront;

UIDeviceOrientation deviceOrientation = UIDevice.currentDevice.orientation;
switch (deviceOrientation) {
    case UIDeviceOrientationPortrait:
        if (devicePosition == AVCaptureDevicePositionFront) {
            orientation = FIRVisionDetectorImageOrientationLeftTop;
        } else {
            orientation = FIRVisionDetectorImageOrientationRightTop;
        }
        break;
    case UIDeviceOrientationLandscapeLeft:
        if (devicePosition == AVCaptureDevicePositionFront) {
            orientation = FIRVisionDetectorImageOrientationBottomLeft;
        } else {
            orientation = FIRVisionDetectorImageOrientationTopLeft;
        }
        break;
    case UIDeviceOrientationPortraitUpsideDown:
        if (devicePosition == AVCaptureDevicePositionFront) {
            orientation = FIRVisionDetectorImageOrientationRightBottom;
        } else {
            orientation = FIRVisionDetectorImageOrientationLeftBottom;
        }
        break;
    case UIDeviceOrientationLandscapeRight:
        if (devicePosition == AVCaptureDevicePositionFront) {
            orientation = FIRVisionDetectorImageOrientationTopRight;
        } else {
            orientation = FIRVisionDetectorImageOrientationBottomRight;
        }
        break;
    default:
        orientation = FIRVisionDetectorImageOrientationTopLeft;
        break;
}

FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
metadata.orientation = orientation;
//カメラとデバイスの向きを設定しています。

FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:buffer];
image.metadata = metadata;
//動画のバッファーデータからFIRVisionImageインスタンスを生成し、上記のFIRVisionImageMetadataをセットしています。

FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
options.detectorMode = FIRVisionObjectDetectorModeStream;
options.shouldEnableMultipleObjects = NO;
options.shouldEnableClassification = YES;  // Optional
//物体検出をするときのオプションを設定しています。内容については後述します。

FIRVisionObjectDetector *objectDetector = [[FIRVision vision] objectDetectorWithOptions:options];
//物体検出を実行します。
[objectDetector processImage:image
                  completion:^(NSArray<FIRVisionObject *> * _Nullable objects,
                               NSError * _Nullable error) {
                    if (error == nil) {
                      return;
                    }
                    if (objects == nil | objects.count == 0) {
                      //物体検出できなかった場合
                      return;
                    }

                    // 物体が検出された場合detectedObjectsに情報が入ってきます。

                    // ...
                  }];

物体検出時のオプションについて補足します。


  • detectorMode

FIRVisionObjectDetectorModeStream→精度は低いですが早く動作します。追跡用のIDも振られます。

FIRVisionObjectDetectorModeSingleImage→精度は高いですが遅く動作します。追跡用のIDは振られません。

リアルタイムに検出とトラッキングをする場合は前者のStreamがいいでしょう。

  • shouldEnableMultipleObjects

有効にすると最大5つまで検出します。無効の場合は1つのみです。

  • shouldEnableClassification

ざっくりしたカテゴリ分類をするかどうかを設定します。


サンプルコードをそのまま起動するとこんな感じになります。

f:id:omuriceman:20190531143822g:plain
これは・・・私が購入したiPad!!

うまく物体検出ができた場合、取得できる情報は以下の通りです。

  • frame:検出できた物体の範囲
  • trackingID:オブジェクト識別用のID。追跡に使用
  • classificationCategory:ざっくりした画像の分類。(本当にざっくりですのであまり使えません)
  • label:カテゴリの英語の説明(という解説ですが、現状「object」としか出てきません)
  • entityID:Google Knowledge Graph検索用のID
  • confidence:分類の信頼度

frameは画面の描画に使用したり、全体の画像からframeの矩形で切り抜いて別の処理に繋いだりします。次項にて検出した画像をラベル付けする仕組みを作ってみます。

検出した物体の画像からラベル付けをおこなう

実装できた物体検出ですがあくまで矩形を検出するだけなので、なんの画像かまではわかりません。これを別のラベル付け(画像分類)に連携してみます。

firebase.google.com

こちらの機能を使用します。先述の通り、ラベル付け機能には端末だけで完結する機能とクラウドを利用する機能があります。

f:id:omuriceman:20190530194418p:plain
On-deviceとCloudの比較

上記の通り、端末だけの場合無料で利用できます。ただし識別できる数は400+程度とのことです。

組み込み方についてですが上記のサンプルコードをベースに修正を加える場合、

  //検出したサイズで画像を抜き取る
  UIImage *croppedImage = [self croppedImageFromSampleBuffer:self.lastDetectedSampleBuffer.data
                                                    inRect:self.lastDetectedObject.frame];

  FIRVisionImage *image = [[FIRVisionImage alloc]initWithImage:croppedImage];
  
  FIRVisionImageLabeler *labeler = [[FIRVision vision] onDeviceImageLabeler];
  
  [labeler processImage:image
             completion:^(NSArray<FIRVisionImageLabel *> *_Nullable labels,
                          NSError *_Nullable error) {
                 if (error != nil) { return; }
                 
                 // 画像から推測されたラベルが配列で入ってくる
                 for (FIRVisionImageLabel *label in labels) {
                     NSLog(@"%@",label.text);
                 }
             }];

こんな感じでラベル付けが可能になります。取得したら画面に表示したりしてみると面白いですね。

f:id:omuriceman:20190530212049g:plain

こちらは私が作成したアプリの画面です。

ちゃんと識別できていないものもありますが、フィギュアなんかはうまく認識できていると思いませんか? 無料で物体検出から分類までできるなんてとても便利ですね。

まとめ

  • 物体検出を手軽に試せるFirebaseのML Kitを試した
  • 物体検出した後にラベル付けをする機能を追加した
  • 物体検出からラベル付けという流れで精度の高い画像分類ができる
  • 画像をかざして検索などに使えるかもしれない

今後もアプリを使った機械学習コンテンツをアップしていきます。