JSONを用いた動画生成

## 概要
AIのセグメンテーション結果の流し見など、色々な画像を動画に合わせて同時にみたいという時があります。
その時、いちいちコード上でレイアウトを変更して...とかやっているとミスが多いため、すべてをjson上でまとめてそこで管理すればいいのでは？と考えてコードを作成してみたので公開します。

## コード内容
### 動作環境
- opencv
- PIL
- python 3.10
### ファイル構成

```
.
├── README.md
├── main.py      # まとめて動かすためのコード
├── outline.json # レイアウトを記載
├── pictures     # 複数の元画像を結合した画像を格納
├── src 
│   ├── movie_maker.py    # 動画作成
│   └── picture_maker.py  # 画像生成
├── venv
├── video.mp4
└── video_output
```

### JSONによる画像の配置の設定
１フレームごとの画像を生成する際、複数の画像の配置やサイズ、背景画像の大きさを設定すれば良い。

```outline.json
{
    "movie_property": {
      "framerate": 60
    },
    "picture_property":{
        "picture_number":2{
    "movie_property": {
      "framerate": 60
    },
    "picture_property":{
        "picture_number":2,
        "background": {
        "width": 300,
        "height": 300
        },
        "pictures": [{
        "source": "sample/img/sample1",
        "positionX": 0,
        "positionY": 0
        },
        {
        "source": "sample/img/sample2",
        "positionX": 150,
        "positionY": 0
        }]
    }
},
        "background": {
        "width": 300,
        "height": 300
        },
        "pictures": [{
        "source": "sample/img/sample1",
        "positionX": 0,
        "positionY": 0
        },
        {
        "source": "sample/img/sample2",
        "positionX": 150,
        "positionY": 0
        }]
    }
}
```

movie_propertyにはfpsなどの動画作成に必要なものを記載
picture_propertyには画像のソースや配置などを記載

### picture_makerによるフレームごとの画像の生成
あとはPILを使ってファイルの検索を行い、JSONでの指示にしたがって並べる。
※画像ファイルには00000.pngなど、ゼロ詰めにしておかないと順番がおかしくなる。

```picture_maker.py
from PIL import Image
from pathlib import Path
import os
def make_picture(outline: dict):
    #backgroundの作成
    background_width = outline["background"]["width"]
    background_height = outline["background"]["height"]
    # 背景画像の作成
    dst = Image.new(mode='RGB', size=(background_width, background_height))
    
    # 各フォルダに対してfileリストを作成
    image_pathlist = []
    for i in range(len(outline['pictures'])):
        path = Path(outline['pictures'][i]['source'])
        plist = path.glob('*.png')
        image_pathlist.append(sorted(list(plist)))

    print(image_pathlist)

    # 1個目のファイルリストの長さ分の画像を作る。
    for i in range(len(image_pathlist[0])):
        # 背景画像の生成list(plist)list(plist)
        dst = Image.new(mode='RGB', size=(background_width, background_height))
        
        # 画像の貼り付け
        for j in range(len(image_pathlist)):
            im = Image.open(image_pathlist[j][i])
            dst.paste(im,
                      (outline['pictures'][j]['positionX'], outline['pictures'][j]['positionY']))
        # 画像の保存
        if not(os.path.isdir('pictures')):
            os.mkdir('pictures')
        dst.save(f'pictures/{str(i).zfill(5)}.png')
from PIL import Image
from pathlib import Path
import os
def make_picture(outline: dict):
    #backgroundの作成
    background_width = outline["background"]["width"]
    background_height = outline["background"]["height"]
    # 背景画像の作成
    dst = Image.new(mode='RGB', size=(background_width, background_height))
    
    # 各フォルダに対してfileリストを作成
    image_pathlist = []
    for i in range(len(outline['pictures'])):
        path = Path(outline['pictures'][i]['source'])
        plist = path.glob('*.png')
        image_pathlist.append(sorted(list(plist)))

    print(image_pathlist)

    # 1個目のファイルリストの長さ分の画像を作る。
    for i in range(len(image_pathlist[0])):
        # 背景画像の生成list(plist)list(plist)
        dst = Image.new(mode='RGB', size=(background_width, background_height))
        
        # 画像の貼り付け
        for j in range(len(image_pathlist)):
            im = Image.open(image_pathlist[j][i])
            dst.paste(im,
                      (outline['pictures'][j]['positionX'], outline['pictures'][j]['positionY']))
        # 画像の保存
        if not(os.path.isdir('pictures')):
            os.mkdir('pictures')
        dst.save(f'pictures/{str(i).zfill(5)}.png')
```

### movie_makerによる動画の生成
下記サイトを参考にしつつ作成
https://note.nkmk.me/python-opencv-video-to-still-image/

``` movie_maker.py
import cv2
import os

def make_movie(config):
    cap = cv2.VideoCapture(0)
    codec = cv2.VideoWriter_fourcc(*'mp4v')
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    out_file_name = 'output/video.mp4'
    video = cv2.VideoWriter(out_file_name,fourcc, 
                            config['movie_property']['framerate'],
                            (config['picture_property']['background']['width'],
                              config['picture_property']['background']['height'])
                            )
    image_list = sorted(os.listdir('pictures'))
    for frame in image_list:
        img = cv2.imread(os.path.join('pictures',frame))
        video.write(img)
    video.release()
```

### 今後の展開
- エラー対応(画像のファイル数が違うなど)
- 透過率への対応
- その他必要に応じて作成を行うよて