3次元描画ソフトPovrayのLinuxコマンドをpythonで管理

# 目的
Povrayは強力な3次元描画ソフト．
ImageMagickのgif動画作成コマンドと組み合わせて3次元動画の作成が可能．
Linuxコマンドはpythonのsubprocessモジュールで実行が可能．
pythonを用いてこれら処理の自動化を目指す．
細かな注意事項など，備忘録的なまとめ．

# 環境
OS: Linux系

- python 2.7系
- povray
- ImageMagick

# 作業の流れ

1. Povrayのソースコードの自動作成（基本はテキスト処理であるため省略）
2. ソースコードをPovrayに用いてpng画像生成
3. png画像群からgif動画の作成

# 使用モジュール
```python:モジュール
import subprocess
import glob
import shutil
import os
```

# ソースコードをPovrayに用いてpng画像生成

あらかじめ，何らかの関数でソースコードを生成したと仮定．
最終的に動画を作成するため，時間tなどでfor loopを作るのを想定．

```python:ソースコード作成
for t in t_list:
  for ....:
    srcname = srcdir+'/'+srcfile
    CreatePovRaySrc(srcname, t, x, y,...)
```

png画像の名前の指定．
今回は例として，保存先と拡張子のみを置換する．
（補足）Povrayのデフォルトはソースと同じ場所に.pngに置換したファイルを出力する．

```python:png画像名指定
    figname = srcname.replace('.pov','.png').replace(srcdir, pngdir)
```

png画像の作成．
subprocess.call()でLinuxコマンドを実行する．
Linuxコマンドが povray $srcname なら， subprocess.call(['povray',srcname]) とすればよい．
さらに，povrayコマンドのオプションを下記のように追加する．

```python:png画像の作成
    subprocess.call(['povray',srcname,'-d','-O'+figname])
```
### オプションの解説

- -d: 描画中に一時的にwindowを開いて画像を表示するモードをoffにする．
- -O{ファイル名}: 出力パスを指定する（デフォルトは先述の通り）．
- その他，画像の高さと幅を指定する+H{px数}，+W{px数}などがよく用いられる．

コマンドラインのフラグの詳細は[＊リンク＊](https://www.povray.org/documentation/3.7.0/r3_2.html#r3_2_2_1)を参照する．


# png画像群からgif動画の作成

## gif動画作成：旧やり方

各作業を関数で区分けする．

gif動画作成のためには，必要となるpng画像群が連番である必要がある．
今回は，上記で作成したファイル名群が連番ではないが辞書式に並べられると仮定する．
まず，辞書式のpngファイル名リストを作成する．
sorted()を使用しないと，辞書式に並んでくれないので注意する．

```python:辞書式pngファイル名配列
png_list = sorted(glob.glob(pngdir+'pov_*.png'))
```

gif動画用の一時ファイルを作成する．
'{:03d}'.format(i) で，'000'，'001'，'002'，...といった0埋め連番を作成する．
tailは，一番最後の画像を連番で複製するフラグである．
これを含めた理由は後述する．

```python:gif動画用の一時pngファイルを複製
def tmp_copy(pngdir, png_list, tail=0):
  print 'COPY(temporal) png files.'
  for (i,f) in enumerate(png_list):
    shutil.copy(f,pngdir+'tmp{:03d}.png'.format(i))
  if tail > 0:
    for n in xrange(tail):
  
    shutil.copy(pngdir+ 'tmp{:03d}.png'.format(i),pngdir+'tmp{:03d}.png'.format(i+1))
      i+=1
```

一時ファイルを用いて，gif動画の作成を行う．
ImageMagickのconvertコマンドを用いる．

```python:gif動画作成
def tmp_png2gif(pngdir, gifname, span=100):
  print 'PNG2GIF: create', gifname
  subprocess.call(['convert', '-delay', str(span), '-loop', '0',
    pngdir+'tmp*.png', gifname])
```

### オプションの解説

- -delay {1/100秒数}: 動画の画像遷移スピード
- -loop {回数}: ループ回数の指定（0で無限ループ）

先ほど，一時png生成時にtailで最後の画像を複製したのは，無限ループをしつつループの終点を少し長時間表示させる動画にしたかったためである．

その他，convertのフラグの詳細は[＊リンク＊](https://imagemagick.org/script/convert.php)を参照する．
個人的によく使うオプション以外あまり詳しくはないので，どれくらいフレキシブルに動画生成できるかは把握できていない．

最後に，一時png画像群を削除する．

```python:一時png画像の削除
def tmp_remove(pngdir):
  print 'REMOVE(temporal) png files.'
  for f in glob.glob(pngdir+'tmp*.png'):
    os.remove(f)
```

以上をまとめて，次のようなメイン関数を実行する．

```python:メイン関数
def main():
  png_list = sorted(glob.glob(pngdir+'pov_*.png'))

  # gif動画用の一時pngファイルを複製
  tmp_copy(pngdir, png_list, tail=3)

  # gif動画作成
  gifname = gifdir + giffile
  tmp_png2gif(pngdir, gifname, span=50)

  # 一時png画像の削除
  tmp_remove(paths.pngdir)
```

## gif動画作成：新やり方

わざわざ連番を生成しなくても，ソートしたpng画像群をコマンドに順に指定すれば問題なし．

```python:gif動画作成
def png2gif(gifname, png_list, span=100, tail=0):
  # ImageMagick用ベースのコマンド
  cmd = ['convert', '-delay', str(span), '-loop', '0']
  # png画像群の追加
  for f in png_list:
    cmd.append(f)
  # 最後のpng画像を複製
  if tail > 0:
    for n in xrange(tail):
      cmd.append(f)
  # gif動画ファイル名の追加
  cmd.append(gifname)
  # コマンド実行
  subprocess.call(cmd)

def main():
  png_list = sorted(glob.glob(pngdir+'pov_*.png'))
  gifname = gifdir + giffile

  # gif動画作成
  png2gif(gifname, png_list, span=50, tail=3)
```