【MediaPipe】超簡単！自分の靴にバーチャルな靴を重ねるだけのアプリを作る

# 実行結果
![5i6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360820/71796fee-3831-bb37-a45a-9786dd1c79f5.gif)


# 対象読者
1. MediaPipeの名前は知っていて何かを試してみたい人。
2. バーチャルな靴を重ねるアプリを試したい人。

# 概要
この記事では、上の実行結果のように靴を表示するところまでをゴールとします。
MediaPipeはいわばフレームワークで、MediaPipeを使った実際のソリューションがいくつかあります。
今回は、Objectronというソリューションをベースに使ってサンプルの靴を表示するところまでを説明します。
サンプルを少しだけ変えるだけなので、超簡単にできます！

# 前提条件

- **Android機器(APIレベル28<=)を持っている方**
- **MediaPipeが導入されていること(後述)**

今回は**Android**を使って開発します。現在(2021/3/4)では、AndroidとPythonのみで対応しています。ですので、iOS機器のみ持っている方は、PCでAndroidエミュレーターを使ってください（**カメラを使えるようにする必要があります**）。

また、MediaPipeが導入されていることが条件です。
MediaPipeをまだ導入されてない方は、[Installation](https://google.github.io/mediapipe/getting_started/install.html)のページをみて各プラットフォームに合った方法で導入してください。

好評だったら導入方法の記事も書くかもしれません。

# 開発環境
macOS Big Sur 11.2.2 Apple M1
Google Pixel 4a(Android 11.0)


# MediaPipeとは
クロスプラットフォームで動作可能なMLソリューションを構築できます。
内部はC++で開発されており、高速です。
また、オープンソースです。
詳しい概要は[MediaPipe](https://google.github.io/mediapipe/)をご確認ください。

<img width="30%" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360820/cf8eac33-cde5-2786-4932-7dc8eb59fbeb.png">


上の図のようにグラフと呼ばれるものに、物体検出や画像処理などを追加していろいろなアプリを作成できます。すでにMediaPipeにはソリューションが用意されており、物体検出や顔検出、髪のセグメンテーション、物体検出、3D物体検出などがあります。
作成できるアプリのプラットフォームは、iOS, Android, C++, JS, Python, Coralで動かすことができます。
ただし、ソリューションによっては非対応のものがあります。例えば、物体検出のソリューションはiOS, Android, C++でのみ対応しています。

Web上でMediaPipeを試したい場合は、[MediaPipeグラフビジュアライザー](https://viz.mediapipe.dev/demo/edge_detection)で試せます。

3D検出して靴を表示することができるソリューション「Objectron」を今回は使用します。

# Objectronとは
上記で書いた3D物体検出がObjectronに当たります。靴、椅子、カップ、カメラを3D検出可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360820/64f6d39c-b184-23b6-0541-75db0edc29ee.png)
> 引用元:https://google.github.io/mediapipe/solutions/objectron

このソリューションの靴バージョンを使ってバーチャルな靴を現実にある靴に重ねて表示してみます。



## グラフを理解する
まず初めにObjectronのグラフを見てみましょう。

グラフのコードは`MediaPipe/MediaPipe/graphs/object_detection_3d/object_occlusion_tracking.pbtxt`にあります。

グラフはJSONに似たコードなのでそのままだと理解しにくいです。
[MediaPipeグラフビジュアライザー](https://viz.mediapipe.dev/demo/edge_detection)を使ってグラフを可視化するとこうなります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360820/4ef841be-95ec-eb8c-bfea-ec3675437c7c.png)


input_videoからフローが始まり、output_videoで終わります。
黄色の四角で囲まれたものが入力パケット・入力ストリームです。アプリ側で入力します。
白い四角は、Calculatorと呼ばれるもので、実際に処理がされる部分です。例えば、ImageCroppingでは入力されたカメラ画像をinput_width, input_heightのサイズに変えます。

実際に3Dボックスや靴を描画するところはGlAnimationOverlayで行います。
次は、内部コードをみてみましょう。

# コードを編集する
実際に靴を描画するコード(Calculator)があるところを編集していきます。
3Dボックスや靴を実際に描画するCalculatorである、GlAnimationOverlayをみてみましょう。

`mediapipe/graphs/object_detection_3d/calculators/gl_animation_overlay_calculator.cc`にあります。

まず、定義されるCalculatorはCalculatorBaseを継承しており、4つの関数をオーバーライドする必要があります。

**GetContract()** :入力ストリーム・入力パケット・出力ストリームなどをここで定義します。
**Open()** :グラフが開始されたら呼び出されます。
**Process()** :ストリームが入力されるごとに呼び出されます。
**Close()** :ストリームが閉じられるなどしたときに呼び出されます。

目的の処理はProcessの**[ここ行番号706-714](https://github.com/google/mediapipe/blob/a92cff7a60031f5c3097b06e74416732d85b5011/mediapipe/graphs/object_detection_3d/calculators/gl_animation_overlay_calculator.cc#L706-L714)**にあります。

```cpp:gl_animation_overlay_calculator.cc
if (has_occlusion_mask_) {
      glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
      const TriangleMesh &mask_frame = mask_meshes_.front();
      MP_RETURN_IF_ERROR(GlBind(mask_frame, mask_texture_));
      // Draw objects using our latest model matrix stream packet.
      for (const ModelMatrix &model_matrix : current_mask_model_matrices_) {
        MP_RETURN_IF_ERROR(GlRender(mask_frame, model_matrix.get()));
      }
    }
```

実は、サンプルとして用意されている靴は、内部コードで描画OFFになっています。
ですので、上記のコードを少しだけ変えるだけでサンプルの靴を表示することができます。

```cpp:gl_animation_overlay_calculator.cc
//if内の処理をするためにtrueにする
has_occlusion_mask = true;
if (has_occlusion_mask_) {
      //全ての色を描画するようにする
      glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

      const TriangleMesh &mask_frame = mask_meshes_.front();
      MP_RETURN_IF_ERROR(GlBind(mask_frame, mask_texture_));
      // Draw objects using our latest model matrix stream packet.
      for (const ModelMatrix &model_matrix : current_mask_model_matrices_) {
        MP_RETURN_IF_ERROR(GlRender(mask_frame, model_matrix.get()));
      }
    }
```

たった2行です！
早速ビルドしましょう。

カレントディレクトリをWORKSPACEファイルがあるmediapipeにした状態で、

```
mediapipe % bazel build -c opt --config android_arm64 mediapipe/examples/android/src/java/com/google/mediapipe/apps/objectdetection3d:objectdetection3d --verbose_failures
```

以上コマンドを実行しましょう。
ビルドに成功すると、
`bazel-bin/mediapipe/examples/android/src/java/com/google/mediapipe/apps/objectdetection3d/objectdetection3d.apk`にapkファイルが出力されます。これをadbコマンドなどでAndroid実機にインストールしましょう。靴を写すと、靴が表示されるはずです。

# 実行結果
無事表示することができました！
![5i6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360820/71796fee-3831-bb37-a45a-9786dd1c79f5.gif)


# 最後に
MediaPipeはこれからいろいろできることが増えていくと思うので今後が楽しみです。
困ったらgit issueとかMediaPipeのSlackに訊くのが良いと思います。かなり活発で、質問にすぐ返してくれたりします！

よかったらTwitter __[@nekokojpn](https://twitter.com/nekokojpn)__ フォローお願いします！