2024年8月3日土曜日

画像を線画に変換する Lineart Converter を作った

AIを使わないで写真を線画へ変換するアプリ Lineart Converter を公開しました。 絵柄が変わることなく高速・省メモリに動作します。 線画以外も生成できます。素材生成にご利用ください。



何も設定しないでもこれくらいの変換はできます。 設定するともっと色々できます。



最近の AI は高性能なのでみんなが色々なことをやっていますが、 私はメインマシンのメモリが 4GB なので GPU ゴリゴリの話はあんまりなあと思っています。 ただ AI を使って色々やっている人のを見ていて、 画像から線画を作るくらいなら OpenCV で十分じゃないのと思ったので、 お手軽な線画変換ツールを作ってみました。色塗りで遊ぶくらいならこれで十分そうです。

Wasm + SIMD + Threads でサクサク動作しますが、JavaScript もかなり高速なので、1つでも処理をミスると JavaScript のほうが早かったりします。 たとえば白黒画像を透明化する機能も作ってはみたのですが (GUI にはないけど…)、JavaScript のほうが早かったです。 JavaScript は 24bit 画像に直接書き込めるので最適化しやすいですが、 OpenCV だと 8bit にしたり 24bit にしたり色々やることが多く最適化しにくいので、場合によっては遅くなるということです。 OpenCV の詳しいコードまで見ていないので正確なことはわかりませんが、 きちんと最適化すると案外 SIMD を使わなくても JavaScript のほうが早いケースが結構あるかも。

アルゴリズム

線画を作るアルゴリズムは Canny と adaptiveThreshold と dilate の 3つに大別できると思います。

Canny

Canny はベタ塗りや太い線が白抜きになり、また線が正確に抽出できなかったり、閉路になりにくい欠点があります。 特に線画正確に抽出できないのが今回は厳しいので、何らかの改善がないと利用は厳しいかなあと思っています。 改善手法などを軽く探していたら、計算過程を可視化した良いエントリがありました。 線画に適したものは初期段階で十分抽出できていますが、 途中で大量に drop することがわかります。線画には使えそうにないかな。

adaptiveThreshold

adaptiveThreshold はベタ塗りや太い線が保持され、線が正確に残る利点があります。 局所的な陰影を頑張って拾うのでたいていの写真で申し分ない二値化ができますが、 アニメ絵のように割とフラットな画像では、頑張った結果ノイズになることもあります。 境界の薄い画像は、灰色化した後に二値化することから、エッジが失われる可能性がそれなりにあります。 その場合は平凡な threshold で輝度をフィルターすると良いことが多いです。 cv.LUT() で輝度フィルターした後に adaptiveThreshold を 適用する方法も考慮の余地はありますが、そこまですべきかは微妙です。 threshold の輝度フィルターで色の薄い部分が除去できていると考えれば大差ない気もします。

dilate

dilate は 1回適用しただけで、濃淡の薄い画像も含めてほぼ完璧なエッジが得られる利点があります。 ただし見えにくいノイズがたくさんあって、またエッジが取れすぎてしまうので、除去が難しいことが欠点です。 結局は adaptiveThreshold か threshold を利用することになりそうです。 やはり adaptiveThreshold がうまくいくケースが多いですが、 ノイズが複雑なときは大局的なフィルターのほうが良い時もたまにあります。 薄い線をバキッと消したい時には threshold も使えます。

TODO

人間が調整する必要はありますが、AI と十分勝負はできてると思います。 課題があるとすれば、(1) ギザギザの線の平滑化、(2) 濃淡のないラフ画への対応などでしょうか。 1 は頑張れば解決できる気はしますが、2 は線を消すのが難しそう。 考えるべきことは色々ありますが、だいたい動くようになったので、いったんリリースです。

作ってみて改めて思ったのは、 AI を使わないで漫画風やアニメ風の画像を用意するのが、すごく大変ということでした。 雑にどこかの著作物である画像をサンプルに載せるのは簡単なのですが、 きちんと権利関係を処理しながら載せようとすると、サンプルを用意するだけでも大変でした。 AI を使うとそのへんの問題がサクッとしてしまうので、うーんという気分です。

0 件のコメント:

コメントを投稿