LoginSignup
7
5

More than 1 year has passed since last update.

Flutter MLKit TensorFlowLite で顔認識

Posted at

本記事のやること
- Google ML Kit を使ってみる。
- TensorFlow Lite でモデルを自作してみる。

結果

違ってますがこれが限界。ご結婚おめでとうございます。
余談ですが、過去にiOSで同じことを試したことがあります。
iPhoneで顔認識-Qiita

つかうもの

  • Flutter
    Google 社のフレームワークです。プログラム言語は Dart で、主に Android Studio で開発します。Android の他に iOS も作成できるのがポイントです。iOS の場合は Xcode のプロジェクトが作成されます。頑張れば Windows アプリもできるそうです。VisualStudioのプロジェクトファイルが作成されるようです。 Win11でアンドロイドアプリが動くようですがどうなるのでしょうかね。
  • ML Kit
    Google 社の機械学習ライブラリです。SDKです。Flutter/Dart で実行します。
  • TensorFlow Lite
    機械学習モデルを構築します。PC上で Python で実行します。できたモデルファイルをAndroidアプリに同梱します。

Google ML Kit でできること

具体的には以下のことができます。今回は顔検出とカスタムモデルを使います。他にもいくつか試してみました。github

  • 画像 (Vision APIs)
    • QRコード
    • 顔検出 :arrow_left:今回使用
    • 画像分類
    • 物体検出
    • 文字認識
    • 手書き認識
    • ポーズ検出
    • 自撮り抜出
  • 言語 (Natural Language APIs)
    • 言語識別
    • 翻訳
    • 応答の提案
    • 住所・電話番号検出
  • カスタムモデル (TensorFlow Lite)
    • 画像分類 :arrow_left:今回使用
    • テキスト分類
    • 質問の回答

Firebase のアプリ登録

:pencil: ML Kit を使えるようにします。ローカル環境だと無料で使えますがアプリ登録は必要です。クラウド経由だと有料になります。

(1) Firebase にプロジェクトを作成

(2) アプリを追加して構成ファイルをダウンロード

以下のファイルを取得するのがゴールです。
google-services.json (androidの場合)
GoogleService-Info.plist (iosの場合)

(3) Flutterプロジェクトに構成ファイルを追加

Flutter プロジェクト
:file_folder:android
:file_folder:gradle
:open_file_folder:app
  :file_folder:build
  :file_folder:src
  :notebook:build.gradle
  :notebook:google-service.json :arrow_left:
:file_folder:ios
:file_folder:Flutter
:file_folder:Runner
  :file_folder:Assets.xcaseets
  :file_folder:Base.lpoj
  :notebook:AppDelegate.swift
  :notebook:GoogleService-Info.plist :arrow_left:
  :notebook:Info.plist

Tensorflow Lite でモデルの作成

学習モデルを作成します。PCでPython環境を用意してください。

(1) 画像の収集

train フォルダーにサブフォルダーを作り、顔の画像をいっぱい入れます。

(2) モデルの作成

以下のコードをpythonで実行します。成功すると model.tflite と label.txt が生成されます。

main.py
# pip install tensorflow
# pip install tflite-model-maker

import os
import numpy as np
import tensorflow as tf

from tflite_model_maker import model_spec
from tflite_model_maker import image_classifier
from tflite_model_maker.config import ExportFormat
from tflite_model_maker.config import QuantizationConfig
from tflite_model_maker.image_classifier import DataLoader
import matplotlib.pyplot as plt

data = DataLoader.from_folder('train/')
train_data, test_data = data.split(0.9)
model = image_classifier.create(train_data)
loss, accuracy = model.evaluate(test_data)

model.export(export_dir='.')
model.export(export_dir='.', export_format=ExportFormat.LABEL)

(3) カスタムモデルをアプリに組み込む

Flutter プロジェクト直下の assets フォルダに model.tflite をコピーします。同梱するため pubspec.yaml に追加します。

pubspec.yaml
flutter:
  assets:
    - assets/model.tflite  ←Add
    - assets/labels.txt  ←Add

:pencil: 注意
プロジェクト直下の assets フォルダでないと読み込んでくれませんでした。

顔検出+顔分類

二段階で行うのがポイントです。
1. MLKitのFaceを使って顔の領域を取得します。
1. 顔の数だけ領域を切り取ってTensorflowで推論します。


ソースの一部を解説

ソースは全部 github にあります。

アプリ部分
Future<void> detect(File imagefile) async { 
  final inputImage = InputImage.fromFile(imagefile);

  // MLKitのFaceDetectorで顔の数だけ矩形を取得
  List<Rect> rects = [];
  faces = await _faceDetector!.processImage(inputImage);
  for (Face f in faces) {
    rects.add(f.boundingBox);
  }

  results.clear();
  final File cropfile = File('${(await getTemporaryDirectory()).path}/crop.jpg');
  final byteData = imagefile.readAsBytesSync();
  imglib.Image? srcimg = imglib.decodeImage(byteData);

  // 顔の切り抜き
  for (Rect r1 in rects) {
    r1 = r1.inflate(4.0);
    imglib.Image crop = imglib.copyCrop(srcimg!, r1.left.toInt(), r1.top.toInt(), r1.width.toInt(), r1.height.toInt());
    await cropfile.writeAsBytes(imglib.encodeJpg(crop));
    TfResult res = await _tflite!.detect(cropfile); // TfLiteで推論
    res.rect = r1;
    if(res.outputs.length>0)
      results.add(res);
  }
}

Google ML Kit の FaceDetector で顔の矩形を取得します。
顔の数だけを画像を切り取って Tensorflow に渡します。
苦労した点。TensorflowのSDKにある画像の切り抜きが、できそうでできませんでした
引数がImageじゃなくファイルなので、切り抜き画像を一旦ファイル保存しています。

Tensor部分
Future initModel() async {
  try {
    // 自作モデルの読み込み
    // outputTensors.length=1
    _interpreter = await Interpreter.fromAsset('model.tflite');
    final outputTensors = _interpreter!.getOutputTensors();
    outputTensors.asMap().forEach((i, tensor) {
      TensorBuffer output = TensorBuffer.createFixedSize(tensor.shape, tensor.type);
      _outputTensorBuffers[i] = output;
      _outputBuffers[i] = output.buffer;
      _outputTensorNames[i] = tensor.name;
    });

    // ラベルファイルの読み込み
    final labelData = await rootBundle.loadString('assets/labels.txt');
    final labelList = labelData.split('\n');
    _labels = labelList;

  } on Exception catch (e) {
    print('initModel '+e.toString());
  }
}

// 検出
// 引数のimagefileが顔だけを切り抜いた画像
Future<TfResult> detect(File imagefile) async {
  TfResult res = TfResult();
  try {
    TensorImage inputImage = TensorImage.fromFile(imagefile);
    inputImage = getProcessedImage(inputImage);
    res = await run(inputImage);
  } on Exception catch (e) {
    print('-- detect catch='+e.toString());
  }
  return res;
}

Future<TfResult> run(TensorImage inputImage) async {
  TfResult res = TfResult();
  try {
    final inputs = [inputImage.buffer];
    _interpreter!.runForMultipleInputs(inputs, _outputBuffers); // 推論

    // _outputTensorBuffers.length=1
    // getDoubleList() にはモデルのラベルの数だけ値が入っています
    for (int i = 0; i < _outputTensorBuffers.length; i++) {
      TensorBuffer buffer = _outputTensorBuffers[i]!;
      for (int j = 0; j < buffer.getDoubleList().length; j++) {
        TfOutput r = TfOutput();
        r.label = _labels.length>j ? _labels[j] : '-';
        r.score = buffer.getDoubleList()[j]; // スコア
        res.outputs.add(r);
      }
    }
  } on Exception catch (e) {
    print('Exception='+e.toString());
  }

  // スコア順にソート
  res.outputs.sort((b,a) => a.score.compareTo(b.score));
  return res;
}

ハマりポイント

  • 構成ファイル (google-services.json) を同梱しないとML Kitでエラーになります。
  • Tensorflow liteの推論画像で 224x224 以外のサイズはエラーになります。
  • Tensorflow SDKのImageProcessorで切り出し(crop)ができそうでできませんでした。
  • TensorflowのInterpreterの getOutputTensors で出力が1つしかありませんでした。Githubで他のソースを見ると2つや4つあるような書き方です。 私があまり理解せず使っているのが原因でしょう。
  • カメラアプリと描画キャンバス カメラが16:9でタブレットが16:10のとき上下に黒が入ります。黒を消すためにカメラを拡大してはみ出るようにしました。描画するキャンバスの座標を合わせるのに苦労しました。さらにスマホでは17:9など縦に長かったりするケースもあります。

まとめ

Flutter、MLKit、TensorFlow Lite を使ってアプリを作ってみました。モデルを自作するのがポイントです。顔認識や車の認識などは大手企業がやるでしょう。モデルを自作することでマイナー業界を狙えば、中小ITでもチャンスがあると思います。
ML Kitはいろんなことができます。やってみたいことが思いついたのでまた1ヶ月くらい頑張ろうと思います。

7
5
0

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
7
5