Watershedによる下塗りの効率化


絵に色を塗ろうとして、以下の様な状況に遭遇したことはないでしょうか。

1. 塗りつぶしツール（バケツ）で楽をしようとしたら線が繋がっていなかった。
2. どんな色で塗ったらいいのかわからない。
3. そもそも色を塗るのが面倒くさい。

私はあります。しかもわりといつもだったり。特に1のケースがとても多く、なんとかして色を塗る作業を効率化出来ないか？と思って試してみたという話です。

> 注意・絵を描くことが主目的なら、自分にあったペイントツールを探したり便利な機能を覚えた方が良いです。


## 課題の解決方法
上で挙げた3つの問題はそれぞれ、

1. 塗りつぶしツール（バケツ）で楽をしようとしたら線が繋がっていなかった。
 * **線が繋がっていなくても特定の場所だけを塗りつぶせるようにする。**
2. どんな色で塗ったらいいのかわからない。
 * **塗った後からも色を変更可能にする。**
3. そもそも色を塗るのが面倒くさい。
 * **（下塗りに関してなら）単純作業を軽減させる。**

といった方法で解決が出来そうです。

全てを解決するのはやや無理（とくに3）ですが、ここではある程度単純化するための塗りつぶし専用ツールを作成する事にしました。

> 注意・絵を描くことからプログラムを作成する事に話題がすり替わっています。


### 解決方法

結論から言うと1と2は、Watershedを使用する事で（ある程度）解決出来ます。
具体的にはこんな感じになります。

|**画像**|**説明**|
|---|---|
|<img src="https://qiita-image-store.s3.amazonaws.com/0/28538/f2dbdd58-f225-4ee1-12bd-063dae8b9968.png" width="128px" />|塗りたい線画|
|<img src="https://qiita-image-store.s3.amazonaws.com/0/28538/7c18d1c1-2879-8945-ac18-3e1215aa8b46.png" width="128px" />|理想|
|<img src="https://qiita-image-store.s3.amazonaws.com/0/28538/63165bb8-ad84-dfcc-2546-a586edb50ebc.png" width="128px" />|現実|
|<img src="https://qiita-image-store.s3.amazonaws.com/0/28538/38d66a9f-b1c0-65d4-c941-801ff1579018.png" width="128px" />|Watershed|

#### 実際に色を塗っているところ

<img src="https://qiita-image-store.s3.amazonaws.com/0/28538/c50d19e8-16b2-2d53-345d-5e093d588ea8.jpeg" width="320px" />

> 普通に塗った方がマシ？それ言うと、話が終わってしまいますんで…


### Watershedについて
<img src="https://qiita-image-store.s3.amazonaws.com/0/28538/0c357fc4-57a8-d882-602c-e85115031f8c.jpeg" width="320px" />

[Watershed](https://en.wikipedia.org/wiki/Watershed_%28image_processing%29)というのは画像の領域分割技法のひとつで、物体認識をする際に領域の抽出や判定を行うために使用するアルゴリズムです。画素同士の輝度を勾配とみなして、そこに水が流れ込ませるような手法からこの名前がついています。

Qiitaだと、ysdytさんの投稿された[「物体セグメンテーションアルゴリズム"watershed"を詳しく」](http://qiita.com/ysdyt/items/5972c9520acf6a094d90)という記事でアルゴリズムの解説がされています。

上の様なカラー写真に対して処理する場合は、輝度補正等の前処理をしないと適用しづらい場合がありますが、イラストの線画の場合は元々がモノクロ、またはグレイスケールですので、わりと上手くいきます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/28538/82df2cbe-3f69-f848-88da-fcc885d85ec3.png" width="320px" />

> 前フリが長いですが、つまりこういうのを塗りたいわけです。


## Watershedの実装について
画像処理といえば、まずは[OpenCV](http://opencv.org)あたりを使って…となるのですが、OpenCVにはそのものズバリなサンプルがついていたりします。というわけで、今回の作業としては単にGUIをそれっぽくしただけです。

開発言語はPython 3.6.1で、以下のライブラリを使用しました。

* cx-Freeze (5.0.1)
* PyOpenCV (3.2)
* numpy (1.12.1)
* PyQt5 (5.8.1)
* PyYAML (3.12)


## 動作確認

Watershedでの塗りつぶしですが、一般的なペイントアプリとは異なる挙動となります。（そりゃ、目的が異なってますし）

まず、OpenCVのWatershed実装では最低２色以上（塗っていない領域を含めると３色）が必要です。１色だけを置いた場合は、対象がどのような画像であっても領域全体が塗りつぶされます。

ピンを置いた場所から塗りつぶしが開始されますが、線が閉じていない場所でどのように塗りつぶしがとまるかは、ピンの置かれた距離と他のピンとの距離で決まります。思った様に塗りつぶされない場合はピンの場所を動かしたり、ピンの数を増やしてみたりが必要になります。

### 線画に適用してみる

![watershed_image_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/28538/0114efb6-ec74-f8da-f5c8-fbb861aebccd.jpeg "watershed_image_1.jpg")

> 塗りつぶし方が気に食わない場合は、ピンを削除して打ち直すか移動させる事が出来ます。

![watershed_image_2.jpg](https://qiita-image-store.s3.amazonaws.com/0/28538/9b002314-a89e-4455-1174-160296b81ebd.jpeg "watershed_image_2.jpg")

> わりと良い感じかも…

![watershed_image_3.jpg](https://qiita-image-store.s3.amazonaws.com/0/28538/9e3ad604-2bb8-16ca-cc86-74285969c33b.jpeg "watershed_image_3.jpg")

> 塗り分けが完了したものは、Export Pictureを実行する事で、アンチエイリアスのかかっていない領域分割画像として出力されます。

![watershed_image_4.jpg](https://qiita-image-store.s3.amazonaws.com/0/28538/ec4d2da1-5a00-9674-74f9-d6cf3e28108e.jpeg "watershed_image_4.jpg")

> 一度塗りつぶしが完了した後も、左側ツリーのパレットを変更すると色を変更する事が出来ます。

わりと良い感じに動作してくれました。


## プログラムについて

プログラム自体はGithubにアップロードしましたので、以下の場所から取得可能です。

- GitHub
  - https://github.com/MizunagiKB/watershed_layer

- WindowsとMac版のバイナリもcx_Freezeで作成してみました。
  - http://www.mizunagi-works.com/wp/2017/04/06/6468

### 使用法

自分専用なツールなのですが一応使い方とかを。

#### 画像ファイルの読み込み
現在対応している画像は、PNGとJPEG形式のみとなります。
Import Pictureボタンを押して、ファイル選択ダイアログを呼び出すか、ウィンドウにファイルをドロップする事で読み込まれます。

#### 画像ファイルの書き出し
Export Pictureボタンを押すと、ファイル保存ダイアログが表示されますので、名前をつけて保存してください。ファイル形式は拡張子にあわせたもので保存されます。

#### ピンファイルの読み込み
塗りつぶし箇所の指定と現在のパレットを保存する事が出来ます。

#### ピンファイルの書き出し
塗りつぶし箇所の指定と現在のパレットを保存する事が出来ます。
簡単な構造のYAMLでの保存となります。（画像は保存されません。）

#### その他操作

- マウスホイールで拡大縮小
- スペースキーを押しながらマウス左ボタンドラッグで表示領域の移動
- DEL(BS)キーで指定したピンの削除