CartoonGANでgifをアニメ風にする

# TL;DR
[CartoonGANのコード](https://github.com/Yijunmaverick/CartoonGAN-Test-Pytorch-Torch)をgifに対応させて試した

# はじめに
最近、GAN(Generative Adversarial Network)の実装で、面白いものが増えて来ているような気がしています。
今まで自分でGANを実装したことは無いんですが、[CartoonGAN](http://openaccess.thecvf.com/content_cvpr_2018/papers/Chen_CartoonGAN_Generative_Adversarial_CVPR_2018_paper.pdf)の[実装](https://github.com/Yijunmaverick/CartoonGAN-Test-Pytorch-Torch)が公開されていたので、触って遊んで見ました。
画像をアニメ風に変換するのは、パッケージをいくつかインストールするだけで、すぐに出来ました。
サンプルでgifの結果もあったので、試してみようと思ったのですが、

```python
valid_ext = ['.jpg', '.png']
```
[このように](https://github.com/Yijunmaverick/CartoonGAN-Test-Pytorch-Torch/blob/master/test.py#L21)なっていたのでjpgとpngしか変換できませんでした。
また[それ以降の処理](https://github.com/Yijunmaverick/CartoonGAN-Test-Pytorch-Torch/blob/master/test.py#L42:L53)を見ても、画像にしか対応しておらず、gifには対応していないようでした。
少々手間だとは思いつつも、せっかくなのでgifでもCartoonGANでアニメ風に変換できるようにしようと思い
ちまちまといじって見ました。
具体的には、gifをフレームごとに分解してCartoonGANで変換し、その画像を一つのgifにまとめただけです。

# 変更点
## [gifを一枚ずつ分解する](https://github.com/t-Asai/CartoonGAN-Test-Pytorch-Torch/blob/master/test.py#L113:L124)
まずはgifを読み込みます。

```python
input_gif = Image.open(os.path.join(opt.input_dir, filename))
```
読み込んだgifは
>.seek(number)

とすることで、フレーム毎に分割することができるようなので、一枚ずつ分割して、CartoonGANに入力できるようにしておきます。
> convert_image

という関数に画像を入力することで、変換された画像が手に入るように書き換えています。

```python
for nframe in range(input_gif.n_frames):
    input_gif.seek(nframe)
    output_image = convert_image(
        model, input_gif.split()[0].convert("RGB"))
```
CartoonGANで変換された画像を、順番にファイル名をつけながらtmp/以下に保存します。


完成形はこちらです。

```python
input_gif = Image.open(os.path.join(opt.input_dir, filename))
for nframe in range(input_gif.n_frames):
    print("  {} / {}".format(nframe, input_gif.n_frames), end="\r")
    input_gif.seek(nframe)
    output_image = convert_image(
        model, input_gif.split()[0].convert("RGB"))
    save(image=output_image,
         name="tmp/{name}_{nframe:04d}.jpg".format(**{
             "dir": opt.output_dir,
             "name": "{}_{}".format(filename[:-4], opt.style),
             "nframe": nframe
         }))
```
次で、ファイルを一括で読み込み、gifに整形するのですが、
その時に読み込む順番が変わってしまわないようにするために
> {nframe:04d}

としています。
フレームが4桁を超える場合は、ここの数字をいじる必要があります。


## [複数のjpgをgifにまとめる](https://github.com/t-Asai/CartoonGAN-Test-Pytorch-Torch/blob/master/test.py#L62:L78)
次に、先ほど保存したjpgをgifにまとめていきます。
先ほど、ファイル名に数字を割り振り順番が分かるようにしたので
sortしてからjpgファイルを順に読み込み、listにします。
> sorted(os.listdir("tmp")):

元のgifファイルの速度と揃えるために、パラメータを与えて保存します。
完成形はこちらです。

```python
def jpg_to_gif(input_image, input_filename):
    images = []
    for filename in sorted(os.listdir("tmp")):
        ext = os.path.splitext(filename)[1]
        if ext not in valid_ext:
            continue
        image = Image.open("tmp/{}".format(filename)).convert("RGB")
        images.append(image)
    images[0].save("{dir}/{name}.gif".format(**{
        "dir": opt.output_dir,
        "name": "{}_{}".format(input_filename[:-4], opt.style)
    }),
        save_all=True,
        append_images=images[1:],
        optimize=False,
        duration=input_image.info["duration"],
        loop=0)
```
gifファイルの扱いは、知らなければ掴みにくいのですが
分かれば、手間なだけで、特段引っかかることはなさそうです。


# [Colaboratory](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)の使い方
ローカルで実行しても良いのですが、pcがすごい熱を発するのでやめておいた方が良いでしょう。
また、cpuでdeep learningを実行するのは、処理時間的に考えてもあまり良い手段では無いと思います。
ということで、いつも通りgoogleに[Colaboratory](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)を使わせてもらいます。
gpuで実行したいので、ランタイムがgpuになっていることを確認してから、次のコマンドを実行します。

```python
!git clone https://github.com/t-Asai/CartoonGAN-Test-Pytorch-Torch.git
%cd "CartoonGAN-Test-Pytorch-Torch"
!pip install -r requirement.txt
!sh pretrained_model/download_pth.sh
```
これで最低限必要な準備は整っているはずです。
オプションをいくつか付けることも可能ですが、一旦何もつけずに実行します。
~~この実行の仕方だと、jupyter notebookである必要性がカケラもないのは目を瞑る~~

```python
!python test.py
```
これで、test_output以下に、結果が保存されていることが分かるかと思います。
ここまでで動作確認は終わりです。

次は、自分のgifファイルを良しなに変更するために
colaboratory上にファイルをアップロードしたり、変換後のファイルをダウンロードする方法について説明します。
やり方を見つけるまでは苦労するかもしれませんが、
見つけてしまえば単純です。
> google.colab

を使ってアップロードとダウンロードが可能です。
「zipにしてからダウンロードしたい」などの要望がある場合は、shellをcolaboratory上で実行すれば可能です。

```python
from google.colab import files
import os
files = files.upload()
for filename in sorted(os.listdir("test_output")):
  files.download("test_output/{}".format(filename))
```

google driveとcolaboratoryを同期させる方法もありますが、
個人的に、あまり好きな方法ではないので使いません。



# 結果
gifファイルでも、問題なくアニメ風に変換することができるようになりました。

![demo_ori.gif](https://qiita-image-store.s3.amazonaws.com/0/239586/e851a142-15b6-936a-7511-6d3807edcbef.gif)![demo.gif](https://qiita-image-store.s3.amazonaws.com/0/239586/c8471f27-f1a0-3421-f504-7b3b3f8c5434.gif)