本記事のやること
- 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コード
- 顔検出 今回使用
- 画像分類
- 物体検出
- 文字認識
- 手書き認識
- ポーズ検出
- 自撮り抜出
- 言語 (Natural Language APIs)
- 言語識別
- 翻訳
- 応答の提案
- 住所・電話番号検出
- カスタムモデル (TensorFlow Lite)
- 画像分類 今回使用
- テキスト分類
- 質問の回答
Firebase のアプリ登録
ML Kit を使えるようにします。ローカル環境だと無料で使えますがアプリ登録は必要です。クラウド経由だと有料になります。
(1) Firebase にプロジェクトを作成
(2) アプリを追加して構成ファイルをダウンロード
以下のファイルを取得するのがゴールです。
google-services.json (androidの場合)
GoogleService-Info.plist (iosの場合)
(3) Flutterプロジェクトに構成ファイルを追加
Flutter プロジェクト
android
gradle
app
build
src
build.gradle
google-service.json
ios
Flutter
Runner
Assets.xcaseets
Base.lpoj
AppDelegate.swift
GoogleService-Info.plist
Info.plist
Tensorflow Lite でモデルの作成
学習モデルを作成します。PCでPython環境を用意してください。
(1) 画像の収集
train フォルダーにサブフォルダーを作り、顔の画像をいっぱい入れます。
(2) モデルの作成
以下のコードをpythonで実行します。成功すると model.tflite と label.txt が生成されます。
# 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 に追加します。
flutter:
assets:
- assets/model.tflite ←Add
- assets/labels.txt ←Add
注意
プロジェクト直下の assets フォルダでないと読み込んでくれませんでした。
顔検出+顔分類
二段階で行うのがポイントです。
- MLKitのFaceを使って顔の領域を取得します。
- 顔の数だけ領域を切り取って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じゃなくファイルなので、切り抜き画像を一旦ファイル保存しています。
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ヶ月くらい頑張ろうと思います。