pillowのImageFilter.ModeFilter
の有効な使い方がわかったので、それを使って絵画調フィルタを作ります。
numpyなどは使いません、pillowのみです。
元画像
変換後
ソースはこれだけです。
from PIL import Image, ImageChops, ImageOps, ImageFilter
img = Image.open("test.jpeg")
qant_img = img.quantize(8, kmeans=True).convert("RGB")
t = qant_img
t = t.filter(ImageFilter.ModeFilter(12))
t = t.filter(ImageFilter.GaussianBlur(5))
t = t.filter(ImageFilter.ModeFilter(12))
t = t.filter(ImageFilter.GaussianBlur(1))
color_img = t.convert("RGB")
gray = img.convert("L")
gray2 = gray.filter(ImageFilter.MaxFilter(5))
line_inv = ImageChops.difference(gray, gray2)
line_img = ImageOps.invert(line_inv).convert("RGB")
ImageChops.multiply(color_img, line_img)
2017/02/24 ソースにエラーがあったので修正しました
解説
減色する
まず、色数が多いと絵画調になりにくいので減色します、減色はImage.quantize
を使います。
色数は何色でも良いのですが、色数が少ないほどベタ塗りになります、ここではkmeans法を使って8色に減色します。
色数が多い画像は、16〜32色にしたほうがいいかもしれません。
qant_img = img.quantize(8, kmeans=True).convert("RGB")
塗り画像を作る
ImageFilter.ModeFilter
は、今までいまいち使い方が不明でしたが、かけると色が単調化します。
カーネルウインドウの中の最頻値を選択する畳込み演算をするので、色の領域が広がっていきます。
t = qant_img.filter(ImageFilter.ModeFilter(12))
絵画っぽくなってきましたね。
ガウシアンブラーと単調化を交互に繰り返す
今回は「にじみ」を表現したいので、更にガウシアンブラーをかけて再び単調化します。
「単調化→ガウシアンブラー」を繰り返していくと、画像がどんどんにじんでいきます。
最後に、少しぼかした感じにしたいので軽くガウシアンブラーをかけます。
t = t.filter(ImageFilter.GaussianBlur(5))
t = t.filter(ModeFilter(12))
t = t.filter(ImageFilter.GaussianBlur(1))
もっとにじみを出したい場合は「単調化→ガウシアンブラー」をもっと繰り返しますが、今回はこれくらいで良いでしょう。
線画をつくる
最後に元画像から線画を作成します、アルゴリズムはこちらを参考にしました。
この方法は単純ながら、非常に綺麗な線画を生成できるのでお薦めします。
gray = img.convert("L")
gray2 = gray.filter(ImageFilter.MaxFilter(5))
line_inv = ImageChops.difference(gray, gray2)
line_img = ImageOps.invert(line_inv).convert("RGB")
塗り画像と線画を合成する
最後にこの2つを合成すれば完成です。
ImageChops.multiply(color_img, line_img)
まとめ
もう少しパラメータを調整すれば、もっと綺麗な表現ができるかもしれません。
ImageFilter.ModeFilter
とガウシアンブラーの組み合わせで「にじみ」が出てくるので、何かと使えるかと思います。
やはり、パラメータ調整は必要でImageFilter.ModeFilter
の値を5くらいにすると「にじみ」がやわらかくなります。