3Dモデルから2D画像を自動生成する方法について

[「Applibot Advent Calendar 2020」](https://qiita.com/advent-calendar/2020/applibot)4日目の記事になります。
前日は @taratarai さんの
[パッシブスキル「話しかけられ力」の作り方](https://qiita.com/taratarai/items/9292d479735c35c57853)という記事でした!



# はじめに
大学で深層学習について研究していたので、その学習データ集めの実装方法についてご紹介します。
記事の作成は初めてで、拙い説明やコードになると思いますが、**温かい目**で読んでいただけると幸いです。

# 概要
今回は３Dモデルから２D画像を自動生成する方法をご紹介します。
この実装により、**１つの３Dモデルから複数の２D画像を一気に生成し、集めること**を目的としています。
３Dモデルを中心に、そのモデルの周りをぐるっと移動しながら３Dモデルの画像を撮影します。
これをblenderのpython APIを使って自動で行います。
実装するツールのダウンロードからご紹介するので、Macを持っている方は今からでも始めることができると思います！

# 動作環境
macOS Catalina バージョン10.15.7 
Python 2.7.16

# 流れ
1.Blenderのインストール
2.Blenderをターミナルから起動する
3.モデルのダウンロード
4.画像自動生成の実装
5.実装結果
6.問題点
7.まとめ
8.おわりに

# 1. Blenderのインストール
[こちら](https://www.blender.org/download/)にアクセスして以下のページに移動します。
![スクリーンショット 2020-11-28 19.05.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/81b879bb-9e1b-d4f1-ed57-50081462dc87.png)
[Download Blender 2.91.0]をクリックしダウンロードしてください。
![スクリーンショット 2020-11-28 19.08.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/6897036e-8616-2f49-81a2-af5d8e93745c.png)
上の画像よりBlenderアイコンをドラッグし、ファイルをコピーしてください。
![スクリーンショット 2020-11-28 19.22.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/6a91d089-a374-c2d1-7119-9fc2f6461502.png)
アプリケーションをクリックして上の画像のように起動できる状態にしたらインストール完了です。

# 2. Blenderをターミナルから起動する
コードで自動生成する上でターミナルで起動する必要があります。
そのために、ターミナル（bash）で起動できる環境の設定を行います。

以下のコマンドでBlenderが起動すると思います。
※違う場合はFinder内のアプリアイコン右クリック＞[パッケージ内容を表示]で開かれるファイルの場所を確認できます。

```
$ /Applications/Blender.app/Contents/MacOS/Blender
```
これを以下で起動するように設定します。

```
$ blender
```

ターミナルでの起動はこちらの[記事](https://blenderapproach.wordpress.com/2014/08/09/%E3%82%BF%E3%83%BC%E3%83%9F%E3%83%8A%E3%83%AB%E3%81%8B%E3%82%89blender%E3%82%92%E8%B5%B7%E5%8B%95/
)に書かれている流れで進めました。

以下のコマンドでvimで~/.bashrcを開きます。

```
$ vim ~/.bashrc
```
~/.bashrcに以下のコードを追加

``` ~./bashrc
alias blender='/Applications/Blender.app/Contents/MacOS/Blender'
```
以下を入力で更新

```
$ source .bashrc
```
これで以下のコマンドで起動できる状態になりました。

```
$ blender
```

# 3. モデルのダウンロード
3Dモデルについては事前にこちらの[サイト](https://free3d.com/ja/3d-model/bugatti-chiron-2017-model-31847.html)からモデルをダウンロードしました。
![スクリーンショット 2020-12-01 13.18.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/8c0f5d33-1fa1-58ea-c231-162bafd80f11.png)

.blendファイルを開くとこんな感じで表示されます。
![スクリーンショット 2020-12-01 13.13.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/d21395cb-0d59-1a29-c54c-12bfcb1ab41d.png)
今回はobjファイルを使用します。

# 4. 画像自動生成の実装 
以下のスクリプトを作成してください。

``` make_image.py
import bpy
import numpy as np
import os
import re

def object_shift(object, locate_x, locate_y, locate_z, rotate_x, rotate_y, rotate_z):
    '''-----------------------
    関数obj_shiftの役目
    object:移動したいオブジェクト
    locate_xyz:移動座標
    rotate_xyz:変更方向
    ------------------------'''
    obj = object
    obj.location.x = locate_x
    obj.location.y = locate_y
    obj.location.z = locate_z
    obj.rotation_euler[0] = rotate_x
    obj.rotation_euler[1] = rotate_y
    obj.rotation_euler[2] = rotate_z

def rendering(camera_num,size):
    '''-----------------------
    camera_num:正の整数:カメラの角度
    size:配列:画像サイズ
    -----------------------'''
    scene = bpy.context.scene
    scene.render.resolution_x = size[0]
    scene.render.resolution_y = size[1]
    scene.render.resolution_percentage = 100
    #カラー表示
    scene.render.image_settings.color_mode = 'RGBA'
    #背景が白っぽくなる？
    bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[0].default_value = (1.0, 1.0, 1.0, 1)
    #レンダリングの処理で終わるまで次の実行はできない
    bpy.ops.render.render()
    bpy.data.images['Render Result'].save_render(filepath = os.path.dirname(os.path.abspath(__file__)) + '/image/' + str("{0:03d}".format(camera_num)) +'.jpg')

if __name__ == "__main__":
    
    #カメラ以外のオブジェクト削除
    for item in bpy.data.objects:
        item_name = item.name
        if(item_name != "Camera"):
            bpy.data.objects.remove(item)
    for item in bpy.data.meshes:
        bpy.data.meshes.remove(item)
    for item in bpy.data.materials:
        bpy.data.materials.remove(item)
    
    # path取得
    path = os.path.dirname(os.path.abspath(__file__)) + '/' + 'bugatti' + '/'
    
    # オブジェクトファイルの名前の取得
    file_list = os.listdir(path)

    # 'obj'がつくデータobj_listに追加
    obj_list = [item for item in file_list if item[-3:] == 'obj']
    item = obj_list[0]
    full_path_to_file = os.path.join(path, item)

    #オブジェクトを配置
    bpy.ops.import_scene.obj(filepath=full_path_to_file, axis_forward='X', axis_up='Y')
    
    #設定
    objectfile_name = 'bugatti' #変換したいオブジェクトが入ったファイル名
    rendering_count = 36 #1つのオブジェクトごとに作成する画像の数
    image_size = [512,512] #作成する画像サイズ
    rendering_distance = 25 #オブジェクトとカメラの距離
    camera_height = 2 #カメラの高さ
    
    # カメラの取得
    obj = bpy.data.objects["Camera"]
    
    # カメラを回しながら画像をレンダリング
    for camera_num in range(rendering_count):
        rad = camera_num * np.pi * 2 / rendering_count
        object_shift(obj, rendering_distance * np.cos(rad),rendering_distance * np.sin(rad)+2,camera_height,np.pi/2,0,rad + np.pi/2)
        rendering(camera_num=camera_num,
                  size =image_size)

    # blenderを終了
    bpy.ops.wm.quit_blender()

```

スクリプトを作成したディレクトリで以下をターミナルで実行

```
$ blender -P make_image.py
```

# 5. 実装結果

生成した画像が以下になります。
今回使った３Dモデルがオブジェクト単体ではなかったので、背景オブジェクトが配置されているせいで一部の角度では車が見えなくなっています。

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/a4a06cbb-e4f8-c563-f25f-584a7a2e8823.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/000527b7-121b-af0b-c6e8-41cf0b1ddec3.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/702da1fb-2850-9c59-2c57-12df27d89785.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/50691cf3-4dd1-4000-7738-5cdb77690526.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/f7db9b9a-b340-d69e-ae10-3678c1504689.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/f5679df7-0f36-db33-057b-645ae5fdd1e9.jpeg width="100" height=456px >

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/3053821e-14d7-554f-a4b1-0d90ae3c8dd2.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/f1fc6f3c-9578-3814-2f8f-50107673816f.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/e5efc034-a947-3e97-e7eb-562f61a9a0a0.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/49103c27-0744-39d8-49e7-ae4f604bbdcf.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/a873bcdd-b8a0-4fe4-d8ae-4c3da837e05a.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/c89d0103-3c58-82e4-0a5a-d69da04fa78f.jpeg width="100" height=456px >

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/c876bcfc-feba-8eae-5751-82aa7e882923.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/70f990ce-c551-3ca2-3ae8-3eef2264e5b6.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/e104c172-212c-e672-d84e-411bbec47ff5.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/7aeb08d2-f8ab-9495-4335-11c58110d921.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/4fe21cd0-7476-7ec1-2a8d-c66f38ea59b2.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/978df448-a6f8-ddc7-294d-607f9814106e.jpeg width="100" height=456px >

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/b7998ce2-f796-f185-4d58-5c839d61bcce.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/c08050d7-3acf-ba64-0142-9e0b77e75fcf.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/9106c5a0-494d-16c4-fa6c-bbd31f8769ca.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/e359c92c-abff-82b5-0bda-ed06afeb8e88.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/4310f1e3-da13-7df1-f584-5d73913bf7c0.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/7107d26e-8b08-8645-f5f6-e067c79b30ce.jpeg width="100" height=456px >

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/a8408906-fa9a-5fdb-1127-2936169ccb7f.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/134d8814-eb8d-a9a4-0db5-0ac26a3fb630.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/4cd591b7-e8ae-64dd-4c1a-864d7bc17e3a.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/8c59d843-8246-8031-2567-444557621dc4.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/9839bae3-349a-7bb7-77fa-2d94507be753.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/88ed9744-2837-297c-3d72-2b2d030de676.jpeg width="100" height=456px >

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/747b7d7c-e9ec-1c9d-d0c9-dab778d5dc89.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/56193a84-368a-df44-a8af-ff8f020cd1e7.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/1304dd7d-03fe-06e7-5313-39984218ef7d.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/bff666fe-d73c-8cc1-4c10-2fe99d457e02.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/13aad9de-5f76-534b-6855-92dfefa0d340.jpeg width="100" height=456px ><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875049/d7fd3368-3989-dc2f-3302-f283427fab07.jpeg width="100" height=456px >

# 6. 問題点
実装当時（blender2.79以前）は、背景色の指定で以下のコードを使うことができました。

```.py
# 背景色の指定
bpy.context.scene.world.horizon_color = (1.0,1.0,1.0)
```
しかし、blender2.8以降では使えなくなっていました。
代用で以下のコードを使ったものの、全体が白っぽくなるだけで、背景色の変化がありませんでした。

```.py
#背景が白っぽくなる？
bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[0].default_value = (1.0, 1.0, 1.0, 1)
```

bpy.context.scene.world.colorの色指定ができるらしいのですが、指定しても出力される画像の背景色は変わりませんでした。

# 7. まとめ
今回は３Dモデルから２D画像の生成を行いました。
少ない３Dモデルの数でもこの方法で機械学習の学習データの水増しをすることができます。
**※精度は保証できません**
今回の実装を書き換えて、フォルダ内のobjファイルをリストで読み込んで一気に大量の画像を自動生成することも可能です。

# 8. おわりに
今回、blenderのAPIを使った実装をご紹介しました。
この手法を使うことで、少ない３Dデータから大量のデータを生成することができるので、手間をかけずに機械学習の実装を行うことができました。
お時間のある方は是非試してみてはいかがでしょうか。

以上、
「[Applibot Advent Calendar 2020](https://qiita.com/advent-calendar/2020/applibot)」 4日目の記事でした！
明日は @ref3000 さんです！
よろしくお願いします！

# 参考文献
[1][Blender 2.91.0 Python API Documentation](https://docs.blender.org/api/current/index.html)
[2][blender 2.8, python立体着色]
(https://qiita.com/naohiko7/items/44e10ab8f997612c590a)
[3][ターミナルからBlenderを起動]
(https://blenderapproach.wordpress.com/2014/08/09/%E3%82%BF%E3%83%BC%E3%83%9F%E3%83%8A%E3%83%AB%E3%81%8B%E3%82%89blender%E3%82%92%E8%B5%B7%E5%8B%95/)