OpenCV+学習済みモデルで、まぁまぁな線画抽出を目指す

# TL;DR
- [そこそこな線画を目指す OpenCV](https://qiita.com/khsk/items/6cf4bae0166e4b12b942)の追加実験
- 元記事＋学習済み深層学習モデルを使って線画を抽出した
- イラストは良い感じになる場合も。リアルは全くダメ
- 自前で画像to線画のデータセットを用意して、モデルを学習できるのであればそれで良いと思う
- 学習済みモデルおよびコードは商用不可なので注意

# 使用データ
線画抽出は主にイラストで行われているタスクだと思っていますが、リアル画像についても実験してみました。

## イラストデータ
[danbooru2018](https://www.gwern.net/Danbooru2018)という、アニメマンガのファンアートを集めた画像データセットを使いました。
画像サイズは512x512ピクセルで、一時期話題を集めていた[style2paints](https://github.com/lllyasviel/style2paints)のv3の論文にはこのdanbooruデータセットの画像を60万枚使って学習を行ったと書いていました。

style2paintsの自動着色例↓
https://twitter.com/jaguring1/status/1055103639323197441

## リアル画像
リアル画像として以下の[ディープニコラス - Gizmode](https://www.gizmodo.jp/2018/02/face-off-ai.html)でおなじみのニコラスケイジ氏の画像を使いました
[映画『魔法使いの弟子』予告編 - Youtube](https://www.youtube.com/watch?time_continue=1&v=ju8nCuQuKzo)の16秒くらいの場面をキャプチャしています。
![nikolas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190157/ae892d78-9a14-e199-5c65-0683492df3f1.png)


# 画像to線画のコード

線画抽出のコードは元記事とほとんど変わらないです。
変更点は

- 関数にまとめた
- 近傍の数を8or24で選ぶことができるようにした

あたりです。

```python:線画抽出.py
import numpy as np
import cv2

def get_line_drawing(fpath, neighbor=8):
    n_neighbor_rows = 3 if neighbor == 8 else 5  # 8近傍か24近傍か
    neighbor_matrix = np.ones((n_neighbor_rows, n_neighbor_rows), dtype=np.uint8)

    gray   = cv2.imread(fpath, 0)
    dilate = cv2.dilate(gray, neighbor_matrix, iterations=1)
    diff   = cv2.absdiff(gray, dilate)
    line   = cv2.bitwise_not(diff)
    
    return line
```

なおこの関数の実行により得られる線画はこんな感じ

![スクリーンショット 2019-06-07 23.13.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190157/bf06dc84-b996-41d2-2493-dede23fe88b8.png)
![スクリーンショット 2019-06-07 23.17.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190157/18fc385c-5975-dbda-43a4-86e1b15113bf.png)
![スクリーンショット 2019-06-07 23.37.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190157/b9092cb4-6d48-7f59-ae5f-92c1fa217f38.png)




# 深層学習で線画を得る

[bobbens/sketch_simplification](https://github.com/bobbens/sketch_simplification/find/master)
このリポジトリを動かしていきます。

## なぜこのモデルか

- このモデルは`ラフスケッチto線画`を学習させたモデル
- OpenCVによって獲得できた粗い線画をラフスケッチと見立てて、それに対して適用すれば良いのではと思った。

## インストール

pyTorchの0.4系でないと動かず、少し苦戦したので書いておく
[INSTALLING PREVIOUS VERSIONS OF PYTORCH](https://pytorch.org/get-started/previous-versions/)を参考にしました

また、`torchvision`も現行の最新バージョンだと動かないので`0.2.2`をインストールするようにします

### pipの場合

```shell
$ pip install torchvision=0.2.2
$ pip install torch==0.4.1 -f https://download.pytorch.org/whl/cu80/stable # CUDA 8.0 build
```

使っているcudaのバージョン(`nvcc -V`で確認)によって２行目は書き換えてください
例えば9.0であれば

```shell
$ pip install torch==0.4.1 -f https://download.pytorch.org/whl/cu90/stable # CUDA 9.0 build
```

### condaの場合

```
$ conda install pytorch=0.4.1 cuda80 -c pytorch
```

## 実行
`--img`引数に画像のパスを入れると`out.png`に結果が出てきます

```shell
python simplify.py --img /path/to/input_image
```

## 結果

### イラスト
![スクリーンショット 2019-06-08 1.14.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190157/4d2b4df8-73e9-f8c8-36fb-1340c4796ccd.png)
![スクリーンショット 2019-06-08 1.15.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190157/1b5ebb55-4681-a96f-1484-75789ead0e3b.png)

### リアル写真
![スクリーンショット 2019-06-08 1.17.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190157/57f91637-da59-ad51-c525-b9f3aacd4f8d.png)

## 所感
イラストの一枚目はスカートの部分が途切れてしまっているが、他の部分に関しては割と上手く行っている。
リアルの方で全く上手く行っていないのもおそらく、濃淡がある一定以上強いと綺麗に抽出できるのではないかと思う。


# 参考
- [画像から線画をDeepLearningで抽出してみた](https://qiita.com/derui/items/9719efa14f44a792362b): 画像to線画のモデルを構築、学習している。結果も綺麗
- [そこそこな線画を目指す OpenCV](https://qiita.com/khsk/items/6cf4bae0166e4b12b942): OpenCVのみで行っている記事