PS2レベルの3Dモデルから生成した教師データのみで歩行者検知に挑む

# はじめに
機械学習に取り組むにあたり、大量の教師データの用意は一つの大きな壁で、
現場でも「教師データが少なくて困っている」というのはよく聞く話だと思います。

データセットが少ない、もしくは全くない状況での学習に対するアプローチの一つとして、
3Dモデルからの学習データの生成があり、今日では商用ゲームが自動運転の訓練等に活用されています。
[Don't Worry, Driverless Cars Are Learning From Grand Theft Auto](https://www.bloomberg.com/news/articles/2017-04-17/don-t-worry-driverless-cars-are-learning-from-grand-theft-auto?leadSource=uverify%20wall)

こうした例では実写と見紛うレベルのモデルが使われていますが、
一般的に、ここまで高品質なモデルを手に入れるハードルはかなり高いものだと思われます。

本記事では、誰でも無料・簡単に作成できるレベルの3Dモデルから
生成したデータセットのみを学習に用い、実際のデータセットに対する検知がどの程度うまくいくかを検証します。

# 検証対象
今回は、検証対象として[PennFudanDataset](https://www.cis.upenn.edu/~jshi/ped_html/)を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/bcc6ead6-761b-0295-2aa8-ed091a7eccea.png)

# 学習データの生成
## 人物モデルの作成
人物モデルの作成には[MakeHuman](http://www.makehumancommunity.org/)を使います。
MakeHumanはBlenderに精通していなくても簡単に人物の3Dモデルを作ることができる、大変ありがたいツールです。

今回はデフォルトの設定を使って、下の彼を含む6人（男性3人、女性3人）を作りました。
blenderで扱えるよう、fbx形式でエクスポートしておきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/7866024e-59ff-e9e3-1c24-9957a645ceae.png" width="500">

余談ですが、PlayStation公式が公開しているPS1〜4のグラフィック比較動画を見ると、
MakeHumanでサクッと作れる人物は大体PS2ぐらいの見た目に位置するのではと思い、本記事のタイトルとなりました。
[PS4 vs PS3 vs PS2 vs PS1 Graphics Comparison: The Road To PlayStation 4](https://www.youtube.com/watch?v=XKQTfAgF67k)

こちらの[歴代MGSのポリゴン数カウント](https://www.reddit.com/r/metalgearsolid/comments/gutx4t/polygon_count_of_snake_from_1987_to_2015/)が正しいとすると、ポリゴン数ではPS3、4を超えてしまっていそうです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/dbeaac64-1799-34ff-302d-29607e73777b.png" width="300">

## 作成したモデルから学習データの生成
学習データの生成には[BlenderProc](https://github.com/DLR-RM/BlenderProc)を使います。
BlenderProcはblenderのシーンから簡単に機械学習用のデータが生成できるツールで、背景や物体、カメラ、照明のランダム化により多様なデータを生み出すことができます。また、COCO formatでのアノテーション出力も可能です。

BlenderProcは公式が豊富なデモを用意してくれていることから、exampleを参照すればそれっぽいデータセットを簡単に量産することができます。今回は[coco_annotations](https://github.com/DLR-RM/BlenderProc/blob/main/examples/advanced/coco_annotations/README.md)をベースに進めます。

### sceneの用意

blenderで新規sceneを立ち上げ、[人物モデルの作成](##-人物モデルの作成)で用意した3Dモデルをインポートします。
インポートした直後は人物モデルが20m近くあることになっているので、その後扱いやすいようにscaleを0.1程度に調整します。

### sceneから画像の生成

以下のコマンドでsceneから画像が生成できます。
```
blenderproc run \
   examples/advanced/coco_annotations/main.py \
   examples/resources/camera_positions \
   <上で用意したsceneのパス> \
   examples/advanced/coco_annotations/output
```

おそらくそのまま`main.py`を実行しても対象物が映らない、ということになると思うので、
理想の画角が得られるよう`examples/resources/camera_positions`などを調整します。

`main.py`に下記を追加し、`enable_transparency`オプションをONにすると、
対象物以外が透過された状態で画像が生成されます。（次項の背景の合成に必要になります）
```
bproc.renderer.set_output_format(enable_transparency=True)
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/5f04655a-0209-5912-bcfc-3cf1cf16dbff.png" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/aa9f7ce9-5c20-3f4b-c307-b2ea66fba319.png" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/f249dd29-fc1f-fe35-680c-694a4461db3e.png" width="200"> 

### 背景の合成

[random_backgrounds](https://github.com/DLR-RM/BlenderProc/blob/main/examples/advanced/random_backgrounds/README.md)を参考に、ランダムな背景を用意して透過画像に合成します。
今回使った20枚の背景は[Wikimedia Commons](https://commons.wikimedia.org/wiki/Main_Page)から拝借しました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/54d9a885-4734-c9a0-010a-cf7867157ee5.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/809ccc80-22de-f8c9-8ca3-922bcfe843f2.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/e4481412-fae2-6c79-5ee2-b639474e7f76.jpeg" width="200"> 


### 成果物

最終的なデータセットは以下のようになります。
今回は6人×100枚ずつで計600枚の画像を生成しました。
（boundary boxはBlenderProcのvisコマンドで表示しています）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/6dd257ff-fa8e-aae2-8155-cc904b18795a.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/a25209a8-f9ad-c41d-700e-3382714e57e2.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/2b9a8588-0d47-781b-192b-0ac6c58f3379.jpeg" width="200">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/563927c3-eaf4-78c2-a77a-4599f98ed25e.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/ca2bdf1d-4791-b310-e401-6cfdcb08a943.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/b6e4e8a7-8624-3b79-6ea9-01785fa90799.jpeg" width="200">

boundary boxから少し頭がはみ出していますが、今回はざっくりした結果が出せれば良いのでこのまま進めます。
（BlenderProcでアノテーション生成時に人物全体が入るboundary boxを作るため、3Dモデルをblender内にimport後、肌と靴のObjectを結合するという荒技を使ったため）

# 学習
歩行者の検知には手元に環境があったYOLOv5sを使います。
生成した600枚のうち80%をtrain、20%をvalidationに、
PennFudanDataset全170枚をtestに使い、30エポックの学習を回しました。
augmentationは入れていないです。

# 結果
今回の試行では精度の計算や他データセットとの比較はせず、結果の表示に留まりますが、
特定のシーンではしっかりと人物が検出できることが分かりました。

## うまくいった例

※confidence70%以上で画面上の概ねの人物の全身が検知できている結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/40282b81-f3f1-2298-6e4f-abe67cdd4ec6.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/a2d22482-770a-a13d-0aaa-0d0d0e1174c3.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/60a6f318-7428-e5ba-3d29-b805b610a26b.jpeg" width="200">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/2a6fe9b7-76a4-d6b0-f54c-7aa277c9873a.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/712735c5-1e4f-ca7c-8a34-be6f0d02d57c.jpeg" width="200">

## うまくいかなかった例

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/ece1b0e6-8ac3-777c-745b-8ead01c4b05c.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/b84ffd40-8162-f6d2-7814-d617e2a61389.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/b1394059-44e2-8dec-637c-86b27b5b8be2.jpeg" width="200">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/4a54ca26-6378-3e36-5a00-bb601b5f4e85.jpeg" width="200"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2286990/ab87b221-da17-7c59-49dc-b0334303ac66.jpeg" width="200">

# おわりに
PS2レベルの3Dモデルから生成した教師データのみの学習でも、歩行者検知には十分有用であることがわかりました。
髪型、服、ポーズといった人物のバリエーションとしては、今回6種類のみを教師データとした検証となりましたが、異なる様相の人々も検出できました。
今回検知できなかった人物については、3Dモデルのバリエーションを増やすことで検知精度を上げることが可能だと思われます。

モデルが用意できればPS1レベルの3Dモデルで検証してみても面白そうです。