Help us understand the problem. What is going on with this article?

スマホカメラで手のモーションを記録してUnityでピアノ演奏したかった

:santa:この記事は、 North Detail Advent Calendar 2019 の22日目の記事です:christmas_tree:

概要

やりたいこと

  1. ピアノ演奏中の手のモーションをスマホカメラで記録する
  2. Unityでアニメーションしてピアノ演奏する

技術的なこと

  1. google/mediapipeでハンドトラッキング用のiOSアプリをビルド
  2. iPhoneカメラで手のモーションを記録
  3. Blenderでアニメーション化 & ピアノオブジェクト作成
  4. Unityに取り込んで再生、指と鍵盤の当たり判定で音を鳴らす

成果物

※雑音が流れるのでご注意を

 
......はい(出落ち)

作業環境

・MacBook Pro (Retina, 15-inch, Mid 2014) : Mojave 10.14.6
・iPhoneXs : iOS 13.2.2

・Xcode : 11.3
・Blender : 2.79
・Unity : 2019.2.12

今回はiPhoneのカメラを利用しますが、AndroidやPCのカメラでも同じことができるはず
後述しますが、PCカメラ利用の場合はLinuxでGPUが利用できる環境推奨

Google / MediaPipeでモーション記録用のアプリをビルド

MediaPipeってなぁに?

MediaPipe is a framework for building multimodal (eg. video, audio, any time series data) applied ML pipelines. With MediaPipe, a perception pipeline can be built as a graph of modular components, including, for instance, inference models (e.g., TensorFlow, TFLite) and media processing functions.

MediaPipeは、マルチモーダル(ビデオ、オーディオ、時系列データなど)を適用したMLパイプラインを構築するためのフレームワークです。 MediaPipeを使用すると、たとえばパイプラインモデル(TensorFlow、TFLiteなど)やメディア処理機能など​​、モジュラーコンポーネントのグラフとして認識パイプラインを構築できます。

......なるほど:thinking:(?)

どうやら機械学習パイプライン(画像処理→モデル推論→描画 など)の構築や、
それをグラフとして視覚化できるフレームワークらしい

サンプルではTensorFlowやTensorFlow Liteなどを利用して、
顔認識や物体の識別、手のモーション取得(ハンドトラッキング)などができる

Linux/Win/Macで実行したり、iOS/Android用にアプリをビルドしたりできる

MediaPipeをインストール

0. PCで実行したい人向け

モバイルアプリのビルドではなく、PCで直接実行したい人はLinux環境(OS Xは含まない)推奨です

2019/12/11現在、MediaPipeではデスクトップ用サンプルはLinuxのみGPUサポートが対応しています
(Win/MacでもCPU実行モードはあるのですが私の環境では即座にフリーズしました......)

また、VMやdocker上のLinux環境ではホストGPUが使えないため、
MacならデュアルブートでOSインストールなどが必要です
(これを知らずにVirtualBoxのUbuntu上で作業していて土日まるまる潰しました......超頑張ればできないこともないとかなんとか?)

1. 事前準備

インストールガイドページInstalling on macOSを参考に進めていきます

  • Homebrewをインストール
  • XcodeCommand Line Toolsをインストール

  • Pythonバージョンの確認
    私の環境ではPython 2.7ではビルドが通らなかったため、下記サイトを参考にPython 3.7.5をインストールしました
    参考:pyenvを使ってMacにPythonの環境を構築する

  • "six"ライブラリをインストール

$ pip install --user future six
2. MediaPipeリポジトリをクローン
$ git clone https://github.com/google/mediapipe.git
$ cd mediapipe
3. Bazelをインストール

二通りの方法がありますが、今回はOption 1の方法でやります
※2019/12/11現在、MacではBazel 1.1.0より上のバージョンは対応していないため注意

# Bazel 1.1.0より上のバージョンがインストールされている場合はアンインストール
$ brew uninstall bazel

# Install Bazel 1.1.0
$ brew install https://raw.githubusercontent.com/bazelbuild/homebrew-tap/f8a0fa981bcb1784a0d0823e14867b844e94fb3d/Formula/bazel.rb
$ brew link bazel
4. OpenCVとFFmpegをインストール

こちらも二通りあるのでOption 1の方法でインストール

$ brew install opencv@3
5. Hello World desktop exampleを実行
$ export GLOG_logtostderr=1
$ bazel run --define MEDIAPIPE_DISABLE_GPU=1 \
    mediapipe/examples/desktop/hello_world:hello_world

# しばし待ったのち以下が表示されたらOK
# Hello World!
# Hello World!
# Hello World!
# Hello World!
# Hello World!
# Hello World!
# Hello World!
# Hello World!
# Hello World!
# Hello World!

ここで私の環境ではPython2.7で以下のエラーが発生しました
同じ現象が起こった人は1. 事前準備を参考にPythonのバージョンを上げてみてください

ERROR: An error occurred during the fetch of repository 'local_config_git':
   Traceback (most recent call last):

モバイルアプリのビルド

今回は両手のモーションを取得するモバイルアプリをビルドしたいので、以下のサンプルを利用します
Multi-Hand Tracking (GPU)

※PCで直接実行したい場合は以下を利用してください
Multi-Hand Tracking on Desktop

Androidの場合

Androidの方が簡単にビルドできます
トラッキングには2Dと3Dモードの二通りあるので、3Dモードでビルドします

$ bazel build -c opt --config=android_arm64 --define 3D=true mediapipe/examples/android/src/java/com/google/mediapipe/apps/multihandtrackinggpu

かなり時間がかかるので待ちましょう
下記ディレクトリにapkファイルができるので実機にインストールすれば完了です!

mediapipe/bazel-bin/mediapipe/examples/android/src/java/com/google/mediapipe/apps/multihandtrackinggpu/multihandtrackinggpu.apk

iOSの場合

iOSの場合はこちらの設定がもうひと手間必要です
始めの方にあるXcodeやBazelのインストールは既に完了しているので不要です

(しかもこちらの方法でBazelをインストールすると最新バージョンを取得して動かなくなるという罠。間違えてインストールしてしまった場合は前述の方法でv1.1.0に置き換えてください)

1. Provisioning Profileの用意

AppleDeveloperProgram加入者はデベロッパサイトでProvisioning Profileを作成してダウンロードしてください

そうでない方は以下のサイトなどを参考に作成してください
参考:[Xcode][iOS] 有料ライセンスなしでの実機インストール 全工程解説!

Bundle Identifierを固有のものにしないとエラーが起きるので注意
どこかの誰かが使っているIDだとFailed to create provisioning profile.と怒られます
作成されたファイルは以下のpathにあります

~/Library/MobileDevice/Provisioning Profiles/

用意したファイルはprovisioning_profile.mobileprovisionにリネームして、
mediapipe/mediapipe/に配置します

2. BUILDファイルの修正

mediapipe/mediapipe/examples/ios/multihandtrackinggpu/BUILD45行目のbundle_idを、
Provisioning Profileで設定したBundle Identifierに変更

mediapipe/examples/ios/multihandtrackinggpu/BUILD:45
bundle_id = "com.google.mediapipe.MultiHandTrackingGpu",
         ↓
bundle_id = "hoge.fuga.piyo",
3. アプリのビルド

3Dモードでビルドします

$ bazel build -c opt --config=ios_arm64 --define 3D=true mediapipe/examples/ios/multihandtrackinggpu:MultiHandTrackingGpuApp

長いのでしばらく待ちます
ビルドが完了すると下記ディレクトリにipaファイルができます

bazel-bin/mediapipe/examples/ios/multihandtrackinggpu/
4. 実機にインストール

XcodeのWindow > Devices and Simulatorsから、
USB接続した実機デバイスを選択して、
画面下部の+ボタンからipaファイルをインストールすれば完了です!

手の動きをトラッキング

ビルドしたアプリを動かしてみる

hand_tracking.gif

おお!両手の動きがリアルタイムで反映されていますね

このときのiPhoneのプロセス状態を、USB接続したMacのコンソールアプリで確認してみましょう
スクリーンショット 2019-12-12 23.31.10.png
MultiHandTrackingGpuAppというプロセス名で何やらログが表示されています
hand[0]hand[1]の二つあり、それぞれ21個のLandmarkを持っています

先程のgifの片手のポイントの数が21なので、その座標のようですね
それぞれの手のログは約0.05秒毎に表示されているようです

ちなみに、ログ出力のコードは下記ファイルに記述されていました
mediapipe/mediapipe/examples/ios/multihandtrackinggpu/ViewController.mm : L177あたり

フロントカメラではなく背面カメラを使用したい場合は、同ファイルの以下を変更してビルドし直すと可能です

mediapipe/mediapipe/examples/ios/multihandtrackinggpu/ViewController.mm
// 100行目をYESからNOに
_renderer.mirrored = NO;

// 109行目を~Frontから~Backに
_cameraSource.cameraPosition = AVCaptureDevicePositionBack;

Unityで表示してみる

取得した座標をUnityの3D空間に浮かべてみます
スクリーンショット 2019-12-12 21.45.01.png
なぁにこれぇ?

ログをよくみるとz座標だけ数値が異様に大きいですね
倍率を変更してみます
スクリーンショット 2019-12-12 22.03.27.png
おっ!なんかそれっぽくなった!
向きと大きさを調整して線で繋いでみると・・・
スクリーンショット 2019-12-12 23.28.34.png
完全に手だコレ! \\ ٩( 'ω' )و//

あとはピアノを演奏しているところを撮影して座標を取得するだけです

Blenderでアニメーション化

Blenderとは?
  • 3Dモデル作成、アニメーション作成、レンダリングなどができる
  • 高機能・高品質、だけど高難度
  • オープンソースで無料で使える

最近 v2.8がリリースされ、UIや操作方法が変わり直感的に操作できるようになりました
参考サイトはv2.7xの方が多く、古いプロジェクトをインポートすると壊れてしまう場合もあるため今回はv2.79を使用します

「Blender アニメーション 作成」で検索すると、
手動でボーンを動かしてキーフレーム毎にポーズを設定する方法が主流のようです
参考:かんたんBlender講座 アニメーションの基本

この方法は単純なアニメーションだとよいのですが、
ミリ秒単位で複雑な動きをさせる場合は骨が折れます(ボーンだけに)

そのため今回はスクリプトで作成します

Pythonでスクリプト作成

BlenderではPythonスクリプトが実行できます
MediaPipeで取得した座標をCSVファイルにまとめて、スクリプトからアニメーション化します

1. 座標CSVファイルの作成

こんな感じでCSVファイルを作成しました
左から以下の値となってます

  • 手(0=左手、1=右手)
  • Landmark
  • 開始からの秒数
  • x座標
  • y座標
  • z座標

スクリーンショット 2019-12-22 3.21.21.png

2. Landmarkオブジェクトの作成

以下のSphereオブジェクトを追加
これをLandmarkポイントとして動かします

  • Left.000 ~ Left.020
  • Right.000 ~ Right.020
3. スクリプトの作成

Pythonスクリプトはこんな感じ

投稿時間を過ぎてしまったので説明は省略...:bow_tone1:
(倍率とかfpsとか適当。。。)

コメント追記しました

import bpy
import os

leftLandmarks = []
rightLandmarks = []
fps = 24

scale = (0.015,0.015,0.015)

# Landmarkオブジェクトを割当
for i in range(21):
    leftLandmarks.append(bpy.data.objects["Left.0" + str(i).zfill(2)])
    leftLandmarks[i].scale = scale

for i in range(21):
    rightLandmarks.append(bpy.data.objects["Right.0" + str(i).zfill(2)])
    rightLandmarks[i].scale = scale

# csvのあるディレクトリ
dirpath = "/Path/to/csv"
filename = "animation.csv"

filepath = os.path.join(dirpath, filename)

# csvを一行ずつ処理
with open(filepath, mode='rt', encoding='utf-8') as f:
    for line in f:
        params = line.split(",")

        # 左手 or 右手
        landmarks = leftLandmarks if params[0] == "0" else rightLandmarks

        # 座標を指定
        landmarks[int(params[1])].location = (float(params[3]),float(params[4]),float(params[5])/512)
        # 指定した座標、フレームにキーフレームを設定
        landmarks[int(params[1])].keyframe_insert(data_path="location", frame=int(float(params[2])*fps))
4. スクリプトの実行

Editor Type「Text Editor」にスクリプトを貼り付けてRun Script!

5. Animationの再生

Editor Type「Timeline」から再生!

pianoplay.gif

勝ったな:smirk:(フラグ)

6. オブジェクトのエクスポート

File > Export > FBXなどでエクスポートすれば完了!
.blendファイルもUnityでそのまま読み込めます

Blenderでピアノ作成

白鍵と黒鍵をCubeで並べていきます
アニメーションと合わせるので、実物の鍵盤と同じサイズ比を意識します

スクリーンショット 2019-12-18 22.36.19.png

Unityに取り込んで再生

まずは用意した素材を取り込みます

  • アニメーションを適用した3Dモデル
  • ピアノオブジェクト

ピアノの設定

1. 音源Assetのインポート

ここにきて大誤算
ピアノの単音のフリー音源ってあまり無いんですね。。。

自前の電子ピアノから音源を作成しました:weary:

コーラス音源であれば、以下のUnity Assetを無料で利用できます
(同じ販売者から「グランドピアノ音源」も出品されているのですが$50もする・・・)

2. 鍵盤の動きを設定

鍵盤は以下の条件で動かします

  • 動きはy座標回転のみ(xyz位置とxz回転を制限)
  • 指との衝突で回転
  • 天井と床(鍵盤のみと衝突する)を作り、元の位置より上下しないようにする

鍵盤にはRigidbodyとBoxColliderで当たり判定を付与します

スクリーンショット 2019-12-22 5.00.18.png

3. 打鍵すると音が鳴るよう設定

以下の条件で音が鳴るようにしました

  • ハンドオブジェクトと当たり判定がある(OnCollisionStay関数)
  • かつ鍵盤の角度が一定以下

アニメーションの設定

  • アニメーションコントローラの作成

  • 3Dモデルに適用

サイズ比率の調整

手と鍵盤とのサイズ比を調整します
サイズ比や位置の調整用に別途アニメーションを作成しておくとよいかもしれません

今回調整用のアニメーションも作成していたのですが、
手がドリルしていて使えませんでした。。。

再生

雑音が流れました

まとめ

MediaPipe面白い技術ですね!

想像していたよりはきれいにアニメーションしてくれました
ちゃんと調整すればメロディーを奏でそうなムーブではある

簡単な曲 → 複雑な曲というシナリオだったのですが思い通りにいかず

本当は3Dモデルにアニメーションさせたかったのですが、
時間も技術も足りませんでした。。。

アプリのログからアニメーションを作成するという方法もスマートじゃないですね
Linuxで実行できたらもう少しやりようがあったかもしれません
MediaPipe開発者がUnityのサポートについて反応しているので今後に期待ですね

また先日、VRヘッドセットのOculus Questにハンドトラッキングが実装されました
Unity対応のSDKも今月中にリリースとのことなので、Oculus Questをお持ちの方は是非試してみてください
参考:「Oculus Quest」にハンドトラッキング機能実装。コントローラなしでメニューなどの操作が可能に

ちなみに演奏していた曲は「We Wish You a Merry Christmas」でした
よいクリスマスを✨:santa:

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした