【MediaPipe Model Maker でお手軽転移学習】ジェスチャ認識タイプライターを作る

ML関連の技術キャッチアップ & リハビリ的にハイテクタイプライターを作ってみました。


アルファベットの形を模したハンドジェスチャを認識して、レシートを印字するシステムです。
最新技術とレトロの組み合わせ、袴×ブーツ的な良さがありますよね。

MediaPipe HandsのモデルをModel Makerを利用して転移学習、オリジナルのジェスチャ認識モデル組み込み、サーマルプリンターと接続・印刷までの実装解説をしていきます。

用語解説

MediaPipe Model Makerとは?

MediaPipeモデルメーカーは、既存の機械学習(ML)モデルを、あなたのデータやアプリケーションで動作するようにカスタマイズするためのツールです。(中略)Model Makerは、新しいデータで既存のモデルを再トレーニングする、転移学習と呼ばれるMLトレーニング技術を使用します。
ref. MediaPipe Model Maker  |  Google for Developers

.task形式のMLモデルをMediaPipe製のツールを通じて転移学習する仕組みのようです。
2023.07現在 (v0.10.1) も絶賛開発中の機能ですが、一足先に試してみます。

他ライブラリでの転移学習

どちらも、既存モデルの特徴マップを再利用→最終出力層を変更→学習時に最終出力層の重みのみ更新するように実装を変更、の流れでコードを書き換えます。
TensorFlowやPyTorchは、細かなチューニングが行いやすい点が売りでしょう。


一方、MediaPipe Model Maker (以下MMM) はより手軽で、ライブラリの関数呼び出しのみで転移学習がほとんど完結します。
TensorFlowやPyTorchの独自データ形式や仕様と戦わなくてすみますし、Python + MLエンジニアリングの基礎知識のみで実装可能です。モデルの細かな調整というより、PoC段階でとても活躍してくれそうな機能ですね。ただし、2023.07時点で early release なので注意が必要です。


ちなみに macOS向けの転移学習を利用したモデル作成ライブラリには、 Create ML があります。
Create MLの概要 - 機械学習 - Apple Developer

さっそく作っていく


※2023.07.09現在の公式情報をもとに開発しています。必ず公式サイトから最新情報を確認してください。

公式のサンプルコード

こちらを元に作っていきます。
Hand gesture recognition model customization guide  |  MediaPipe  |  Google for Developers

データセットの作成

1. データの中身を観察する

公式サンプルコード付属の転移学習用の追加データセット "rps_data_sample" の中身を見てみます。

$ for d in `find ./ -type d`; do echo $d,`ls "$d" | wc -l`; done
# ./, 4
# .//paper, 125
# .//rock, 125
# .//scissors, 125
# .//none, 125

4クラス (3種 + none) で各クラス125枚程度で良いようですね。各クラスのデータ画像はこんな感じです。


手の部分だけクロップした画像で、アスペクト比は様々、w,h共に600px以下のものでしょうか(全ては見てないですが)。
このじゃんけんジェスチャのデータセットをお手本にして、オリジナルのデータセットを作っていきます。

2. ジェスチャの録画

アルファベットのハンドジェスチャを4種類認識させようと思います。
お好みのソフトで各ジェスチャのムービーを録画します。
QuickTime Playerで1分半くらいのムービ*4 ("A","P","L","E") を撮影しました。

3. ffmpegで連番画像として切り出し
$ ffmpeg -i input.mp4 -vcodec mjpeg image_%03d.jpg
  • A

  • P

  • L

  • E


このような画像が連番で切り出されます。
幅と高さもリサイズしておきます。

4. 切り出した画像を必要な枚数に絞る

それぞれ125枚とします。noneは元データセットからジェスチャと被らなさそうな画像を任意で200枚選択しました。

学習

ほぼサンプルコード通りで大丈夫です。
500枚程度のデータセット + 10epochなら、colabの無料プランでも20分弱で学習が完了します。
最終的なスクリプトはこちら
github.com

3/3 [==============================] - 1s 28ms/step - loss: 0.1199 - categorical_accuracy: 0.9077
Test loss:0.11989104002714157, Test accuracy:0.9076923131942749

適当なデータセットを入れたにしては、結構良い精度が出ていますね。

タイプライターと接続する

プリンターの導入

まず、プリンターを購入します。サーマルプリンター (レジスターのレシート印字するあの機械です) を、"タイプライター" として見立てる寸法です。
型番: EPSON TM-T90
www.epson.jp
メルカリで6000円でした。修理対応期限20年......。サ終が当たり前の世界に住んでいると、長期間責任を持って修理サービスを提供する業界に謎の感動を覚えますね......。

接続

EPSON製のプリンターには "ESC/POS" というプリンターを制御する独自のコマンドが存在します。独自コマンドという響き......、素敵ですね。
コマンドリファレンスはこれです。
reference.epson-biz.com
このESC/POSコマンドをプリンタに直接ぶち込んで制御していくのですが、(個人的に)できる限り触りたくありません。

有志(神)によるPythonライブラリがあったので、こちらを使わせていただきます。
github.com

 p = Usb(idVendor=0x04b8, idProduct=0x0202) # 接続
 text = "test test test test"
 p.text(text) # 印刷
 p.cut() # レシートカット

めちゃ簡単ですね。

モデルのコールバック関数の中で、ジェスチャ認識したら印刷コマンドを送信するように実装します。

# ジェスチャ認識コールバック
def __callback(result: GestureRecognizerResult, output_image: mp.Image, timestamp_ms: int):
    ### ... (中略) ... ###      
    # 240frameに1回printを試行
    if timestamp_ms%60*4 == 0:
        if any(current_gestures): 
            # 直近認識したジェスチャの最頻値を取得
            mode = statistics.mode(current_gestures) 
            printer.output_and_cut(mode)
            current_gestures.clear()

実際の実装コードはこちらに置いています。
github.com

遊んでみる

......楽しい!!!

改修案

Handsのkeypoints推定は元のモデルで学習済みなので、適当に学習画像を入れてもそこそこいけますね。
ただ、データセットに偏りがあるためか、実際はもう少し精度が良くない印象でした。枚数を増やして、データ拡張(特にクロップ、明るさ調整、±20°程度の回転など)をすると体感的には改善できるかもしれません。

おわり

位置付け的には、3年前に作ったものの後継モデルです。
tama-ud.hatenablog.com
3月のものづくりイベントに持っていく予定で開発していたのですが、普通に間に合わず夏になってしまいました。一度は自作アプリとハードウェアとの組み合わせで何か作りたい、という目的は達成できたのでよしとします。
体感ですが、ソフトウェアに閉じない体験設計は、サービスでもものづくりでも結構大事な気がしています。
今後も徒然に何かしら作っていくぞ。以上!