着彩済イラストから綺麗に線画を抽出する方法

機械学習のテーマの一つとして自動着彩があります。この中で、特にイラストの自動着彩を考えると 未着彩と着彩済みのペアが学習用サンプルとして大量に必要となりますが、まとまった量を入手するのはなかなか難しいという問題があります。

すると、カラーイラストから線画を抽出することを考えたくなるのですが、 一般的な輪郭検出を用いると「輪郭線自体の輪郭」が抽出されてしまい、線がぼやけてしまうという問題があります。

例えば f:id:mizti:20170121224135j:plain に対して輪郭検出を実施すると、 f:id:mizti:20170121224155j:plain となります。 (拡大) f:id:mizti:20170121224451j:plain

右頬の輪郭線に対して、肌側、背景側それぞれの境界が検出されてしまい、線が2本引かれてしまっていることがわかります。

で、綺麗な輪郭抽出ができず困っていたのですが、ピーFN(一体何FNなんだ...)のtaizanさんが投稿されたこちらのエントリ

qiita.com

では非常に綺麗に線画抽出ができており、どのようにやっているか気になっていたところ

f:id:mizti:20170121224813p:plain

との情報が。ということでやってみました。

f:id:mizti:20170121230350j:plain 拡大 f:id:mizti:20170121230510j:plain

線がだぶることなく、綺麗に抽出できているようです。すごい!

(ここまでの絵は村田蓮爾氏のものを引用させていただいています)

手順詳細

以下、手法の詳細についてです。

以降の絵はpixivで見つけたLpipさんイラスト を例にさせていただいてます。

今回使ったのはcv2のpythonライブラリです。

画像を開く

I = cv2.imread('data/before.png')

dilationする

kernel = np.ones((5,5), np.uint8)
dilation = cv2.dilate(I, kernel, iterations = 1)

f:id:mizti:20170121232739p:plain

元画像とのdiffを取る

diff = cv2.subtract(I, dilation)

f:id:mizti:20170121233257p:plain

白黒反転する

diff_inv = 255 - diff

f:id:mizti:20170121232842p:plain

グレースケール化して書き出し

diff_inv_binarized = cv2.threshold(diff_inv, 100, 255, cv2.THRESH_BINARY)
cv2.imwrite('after.png', diff_inv)

f:id:mizti:20170122103923p:plain

まとめると

I = cv2.imread('data/before.png')
kernel = np.ones((5,5), np.uint8)
dilation = cv2.dilate(I, kernel, iterations = 1)

diff = cv2.subtract(I, dilation)
diff_inv = 255 - diff
diff_inv_binarized = cv2.threshold(diff_inv, 100, 255, cv2.THRESH_BINARY)
cv2.imwrite('after.png', diff_inv)

です