論文紹介: Sketch Generation with Drawing Process Guided by Vector Flow and Grayscale

紹介論文

最近以下の論文がArxivに投稿されました[1]。(AAAI 2021にも採択されているようです。) 画像を基に鉛筆画を生成する技術ということですが、個人的に興味がある分野なので読んでみました。またこの記事中の図は特記しない限り原論文[1]から引用しています。

arxiv.org

公開実装

著者らにより実装が公開されています。

github.com

ただ手元の画像ですぐに試すには少し不便だったので、Google colaboratoryで簡単なノートブックを作成しました。

手元の画像でCPUインスタンスで実行すると完了までに数時間かかったので、場合によっては小さい画像で試したほうが良いかもしれません。

colab.research.google.com

論文内容

目的と新規性

この論文で取り扱うタスクの目的は、入力画像を鉛筆画に変換することです。

3DモデルとNon-Photorealistic Renderingを用いるアプローチなど、このタスクに対して従来から様々な手法が提案されています。 しかしこれらの従来手法は最終的な鉛筆画のみを出力し、鉛筆画を描いていくプロセスを出力することはできませんでした。

この研究の新規性は最終的な出力のみでなく、以下の図のように一本一本線を引いていくプロセスも出力できることです。

f:id:ronwall1701:20201222182537p:plain

手法

提案手法は鉛筆画とその描画のプロセスを出力するための問題を以下の2つに分解して解いています。

  • 鉛筆の線のシミュレーション (stroke simulation)
  • どのような鉛筆の線を引くかの決定 (stroke drawing)

Stroke simulation

まず最初のstroke simulationでは、実際の鉛筆画の線に近い線を再現することが目的になります。 この目的を達成するため、この研究では実際の鉛筆画で線がどのように引かれているかを分析しています。

線を水平にする

実際の鉛筆画はある一部分のみに注目すると、平行な線によって構成されていることがわかります。 そこで、処理の最初の段階でこのような平行な線が並んでいる部分を切り出し、線が水平になるように回転させます。

線の明度分布を得る

次に、線に垂直な方向(以下の図(a)におけるyの矢印)に並ぶ各ピクセルの明度がどのように変化するかを調べます。 以下の図の例では、この明度の変化の例が(b)に示されています。 この図をみるとわかるように、それぞれの鉛筆の線は明度の変化のグラフ上でV字型のパターンとして現れます。

この特徴を利用すると、グラフ上の明度の極大値の位置で区切る((b)における赤の点線)ことにより、個々の線ごとの「V字型」、すなわち鉛筆の線を垂直に切ったときの明度変化のパターンを取り出すことができます。 さらにこの処理を図(a)のピクセル列(x座標が同じピクセル)それぞれに対して実行することで、それぞれの線毎に複数の「V字型」パターンを得ることができます。

この論文では、この「V字型」に関して以下の仮定をしています

  • 同じ線に注目したとき、線の中心(最も明度が低いピクセル)からの距離が同じピクセルの明度はi.i.d.にサンプルされる
    • この分布をガウス分布と仮定し、平均と分散を計算する
  • 上記平均と分散は、線の中心からの距離 d、中心明度 G 、線の幅  W により以下のようにモデル化される
    •  mean(d)=G+(255-G)\times \frac{2d}{W-1}
    •  variance(d)=(255-G)\times cos \frac{\pi d}{W-1}

f:id:ronwall1701:20201222182826p:plain

Stroke drawing

以上のstroke simulationの節で説明した処理によって、鉛筆画の線を統計的にモデル化することができました。

よって画像を鉛筆画に変換するためには、どのような濃さ、幅、長さの線を、どこに、どの方向に向かって描くかを決めればよいです。

方向の決定

線を描く方向は、入力画像の勾配情報を用いて決定します。

より具体的には、Edge Tangent Flow (ETF) [2]と呼ばれる手法を利用したそうです。勾配に対して垂直な方向が線を引く方向になります。

最後にピクセルごとに得られた方向を予め決められた粒度に量子化し、それぞれの領域の中では平行な線が引かれるような領域に分割してやります。

線を引く

線を引く処理は、上の処理で作成された平行な線が引かれる領域ごとに行われます。

まず最初に各ピクセルの明度を量子化します。この量子化された明度が線の中心の濃さになります。

そしてこの濃さに基づいて、描画対象の領域に予め与えられた線の間の幅(+少しのノイズ)だけ離れた平行線を引いていきます。

  • なお、ここでstroke simulationで導入したモデルにより、線の幅と中心の濃さが決まれば線の幅方向の明度の分布を決めるていると思われます。

複数領域の線の合成

以上の処理で領域ごとに平行線を引いていくことができました。

しかしこれを単に合成すると以下の図の左側のように、領域の境界部分でうまく線がつながらなかったりする現象が起きてしまいます。

この問題を解消するため、後処理として境界部分の線を線の幅だけ伸ばしてやる処理を行います。この後処理を行った例が以下の図の右側です。 ただしこの図からもわかるとおり、この後処理によりぼやけていまう箇所がでてきてしまいます。 そこで、エッジを強調するため、edge mapを最後に乗算する処理も行います。

f:id:ronwall1701:20201223185443p:plain

線の描画順の決定

これまでの処理で鉛筆画を構成する線のリストが得られたため、後はこれらの線をどのような順番で描いていくかを決めればよいです。

実際の画家が鉛筆画を描くときにはくっきりとした物体の輪郭に最初に線を引く、という観察に基づき、この論文では以下のように線の明度 Gと線に対応する領域 D内のピクセルにおける勾配の大きさ T_iに基づくスコアを計算して大きい順に線を引いていきます。

 S=(255-G) \times \sum_{i\in D}T_i

実際に使ってみた

最初の方で紹介したcolabノートブックを使って、実際に写真を鉛筆画に変換してみました。以下の写真は論文から引用したものではなく私が用意したものになります。

入力画像はこちらのうな重の画像です。

f:id:ronwall1701:20201223194223j:plain

そして最終的な出力はこちらのようになりました。

f:id:ronwall1701:20201223194452j:plain

またこの手法の特徴として、以下のように鉛筆画生成の途中結果も生成することができます。

f:id:ronwall1701:20201223194903j:plain

うな重のお盆に筋状の模様が入っているせいか、そこに引きずられてスプーンやおさらの輪郭より前にお盆のディテールを描いてしまっているところが目に付きますね。 Semantic segmentationとかの出力を使って上手くこの辺を改善できないかが気になります。

感想

論文を見かけたときにはもう少し機械学習要素が強い手法かと思っていましたが、実際に読んでみるとかなり地道な分析や処理を積み上げているようで少し意外でした。

写真から手書き風の背景を作成するなど、創作向けの応用で使えないかな〜といろいろ考えています。

最後までお読み頂きありがとうございました。なにか間違えているところなどあればコメントなどでご連絡ください!それでは!

参考文献

[1] Tong, Zhengyan, et al. "Sketch Generation with Drawing Process Guided by Vector Flow and Grayscale." arXiv preprint arXiv:2012.09004 (2020).

[2] Kang, Henry, Seungyong Lee, and Charles K. Chui. "Coherent line drawing." Proceedings of the 5th international symposium on Non-photorealistic animation and rendering. 2007.