Google ML Kit Visionでできること

# はじめに

MLKitの画像分類でできることを紹介します。
この記事を読んでくれた人が、日々の日常で何か新しいアイデアを発見できればいいなと思っています。

# Google ML Kit とは

https://developers.google.com/ml-kit

Google ML Kit は Google が提供しています。Flutterのライブラリ名は「google_ml_kit」です。１年前は firebase_ml_vision でした。モリモリ進化しているので期待が持てます。ちなみに Apple にも CoreML Vision という似たようなものがあります。


使うためには Firebase のアプリ登録が必要です。アプリ登録といっても公開するわけではないので com.example.xxx でOKです。MLKitをAndroid本体に組み込んで使う分には無料です。MLKitをクラウドで実行すると有料になるので気をつけてください。

# ソース

https://github.com/koji4104/FlutterVision

Flutterで作成しました。google_ml_kit 0.12.0 (2022/8) を使用しています。
:warning: 注意点
iOSでは動きません。TensorFlowLite が対応していないみたいです。
ビルドするには Firebase でアプリを許可して google-services.json というファイルを取得してください。
↓Firebaseアプリ登録の記事
https://qiita.com/koji4104/items/196109cae96be52430ee

# 実験結果

やってみた結果です。

## Face Detector（顔検出）
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/f49875fd-44ba-5c50-b0d7-ab66b743786e.jpeg' width=40%><img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/8ee9d385-31be-c55b-e59c-2347607b0664.jpeg' width=40%>

```dart:dart
List<Face> faces = await _faceDetector.processImage(inputImage);
for (Face f in faces) {
  f.BoundingBox         矩形
  f.smilingProbability  笑顔
  f.headEulerAngle      顔の向き
  f.landmarks           特徴（目鼻口などの点）
  f.contours            輪郭（目鼻口などのパス）
}
```

:smile: 顔を検出します。目鼻口などの点や輪郭を検出できます。パーツだけでなく笑顔や顔の向きも検出できます。顔検出は精度が高いです。スマホの顔認証のノウハウから来ているのでしょうか。私はこの「顔の向き」使って、何人こちらを向いているかという視聴質を作ろうと模索中です。
写真は河村友歌さんです。笑顔99点です。さすがはフリー素材の女王と言われてるだけあります。私が試すと笑顔80点でした。そういや心から笑ったことなんてもう何年前でしょうか。

## Text Recognizer（テキスト認識）

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/419e51d2-b8b5-0758-f642-35fd20b762e0.jpeg' width=40%> <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/1350840e-0cc2-07e4-47e4-ec49872acee3.jpeg' width=40%>

```dart:dart
RecognizedText text = await _textRecognizer.processImage(inputImage);
for (TextBlock b in text.blocks) {
  b.boundingBox 矩形
  b.text        文字
}
```

:smile: テキストを文字にしてくれます。ドキュメントに日本語もできるようなことを書いているのですが上手くいきませんでした。私は業務用機器のメーカー勤務なのですが、本体には型番やシリアル番号が書かれたシールが貼られています。このシールをスマホで読み取れたら便利だなと考えています。

## Image Labeler（画像ラベル付け）
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/99c76c30-9c45-b005-b48e-7143ef27ca27.jpeg' width=40%> <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/9569c4aa-4d9f-80f1-9287-967e83780936.jpeg' width=40%>

```dart:dart
List<ImageLabel> labels = await _imageLabeler.processImage(inputImage);
for (ImageLabel label in labels) {
  label.confidence 制度
  label.label      ラベル、名札
}
```

:smile: 画像をみて「強いて言えば何」というような回答をします。このアプリでは写真ぜんぶをみて画像分類を行います。精度を上げたい場合は何かひと手間必要です。フライドチキンを分類する実験をしたときは、すべて白い皿の上に乗せました。人の顔を認識するときは、一度人の顔を切り取ってから顔専用モデルで画像分類をしました。

## Barcode Detector（バーコード）
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/35177b69-5d20-1144-dfc0-dfe30ddb47eb.jpeg' width=40%>

```dart:dart
List<Barcode> barcodes = await _barcodeScanner.processImage(inputImage);
for (Barcode b in barcodes) {
  b.boundingBox  矩形
  b.displayValue 値
}
```

:smile: QRコードを読むだけなら専用ライブラリがいいです。
人の部位を検出できれば、もう一歩何かできそうな気がします。
例えば「手に持ったスマホ」という状態を検出できれば、少し離れていても読み取ることができるでしょう。
例えば「胸にある名札」という状態を検出できれば、QRコードをかざす必要もなくなるでしょう。
例えば「おでこにあるQRコード」という状態を検出できれば、顔認証の情報量も上がるでしょう。江戸時代に犯罪者はおでこに犬と書かれたらしいです。

## Pose Detector（ポーズ）
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/37cbc1b2-ecb4-69c0-6833-340aeeb971cd.jpeg' width=20%> <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/c0e77092-f5ac-afa6-7f6d-044e9718ab7b.jpeg' width=20%> <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/6aa7d7a8-4467-d855-199b-e2ac7fea7bfa.jpeg' width=20%>

```dart:dart
List<Pose> poses = await _poseDetector.processImage(inputImage);
poses.forEach((pose) {
  Map<PoseLandmarkType, PoseLandmark> lms = pose.landmarks;
}
```

:smile: 体の部位を検出します。右のおしり・右のひざ・右のかかとなど52項目あります。精度はあまりよくありません。今すぐ実践投入は難しいと思います
しかしのこポーズ検出はかなりの可能性を秘めていると思います。2018年にストリートファイターの技を検出する記事を書きました。このポーズ検出を使えばもっと精度が上がりそうです。
https://qiita.com/koji4104/items/bbcd5ba257f0b2b98961

## Object Detector（物体）

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/f1c50bfc-ab62-5555-9996-eb3c04ba3a2b.jpeg' width=40%> 

```dart:dart
List<DetectedObject> objects = await _objectDetector.processImage(inputImage)
for (DetectedObject b in objects) {
  b.boundingBox
  b.trackingId
  List<Label> ls = b.labels;
}
```
:smile: 物体を検出してIDが付きます。trackingとあるので追従するのでしょうか。いまいち使い方が分かりませんでした。

## Selfie Segmenter（自撮り）
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/eea1842f-3e27-cce3-54b2-e1655507df65.jpeg' width=40%> <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/66cde3b0-fd5b-b5e2-8e69-a1d08499dc4c.jpeg' width=40%> 

```dart:dart
SegmentationMask mask = await _selfieSegmenter.processImage(inputImage);
final confidences = mask.confidences;
for (int y = 0; y < mask.height; y++) {
  for (int x = 0; x < mask.width; x++) {
    confidences[(y * mask.width) + x] 
  }
}
```

:smile: 顔や上半身を抜き出してくれます。
confidences[(y * mask.width) + x] に0.0～1.0が入っています。width,heightは256,256でした。こういう情報あまりないですよね。maskとあるように画像のマスク処理で使うのでしょうか。

## TensorFlowLite

:smile: TensorFlowLite は以前の記事になります。自作した学習モデルが使えるのは魅力です。
https://qiita.com/koji4104/items/196109cae96be52430ee

# Vision（画像分類）について
ML Kit には Vision（画像分類）とLanguage（言語処理）の二系統あり今回の記事は Vision です。画像分類は既にある選択肢の中から「強いて言えば何」というような回答をします。MLは間違い探しが得意となります。


画像分類を行うためには「学習済みモデル」というものが必要になります。学習済みモデルは一般にも公開されています。その一般モデルには1000個の「ラベル」が入っています。ラベルにはマウスやキーボード、車や家などが入っています。画像を与えると全てのモデルを足して100点になるように結果を出ます。Aが80点と出る時もあれば、A30点、B25点、C20点のように微妙な時もあります。

# 苦労した点
スケールを合わせるのに苦労しました。
写真は1280x720です。タブレットの場合は上下に黒が入るので、それを避けるために拡大しています。一度1280x720のキャンパスに結果を描画してそのキャンパスをスケールで拡大するという方法にしました。

# まとめ
Google ML Kit Vision の実験結果をまとめました。できることを知っておくと日常にあるチャンスを発見できるかも知れません。

スマホアプリ・カメラアプリにこだわっています。今までになかった分野だからです。
最近、銀行の登録の本人確認で専用アプリで顔を撮影することがありました。専用アプリでは顔を少し動かすなど本人が操作する必要があります。Google Developersの場合は免許証の写真を送ります。これでは盗んだものも可能ですよね。そのとき確認アプリはどうやって作ってるのかなーと思いながらやってました。周りではどんどんAI搭載アプリが出てきているので、自分ももっと頑張らねばと思います。