ハーフトーンを削除する方法を考える

コンピュータ

スキャンした書籍の画像を、大き目のPCモニターに合わせてWaifu2xで拡大処理をしています。
印刷物などで点描で濃淡を表現する方法があり、ハーフトーンというらしいのですが、そのままWaifu2xで拡大すると、点が格子状の模様となって現れます。点を削除しベタ塗状態にすることが出来ないか考えてみます。

おおまかな方法としては、ぼかし処理をすることで独立した点同志を結合します。次にぼかし処理でボケてしまった輪郭などの境界部を、なんらかな方法でメリハリをつけてあげます。

1.縮小、拡大処理をする。
点同志を結合させる方法として縮小処理の補完機能を使います。その後縮小された画像を拡大することで元の大きさに戻します。
補完方法によって結果が異なります。
デメリットとして縮小することで画像の情報量が大幅に減少するため、ディティールが損失の大きい点です。
また、拡大率で調整するため、細かな調整は不向きです。

2.ガウシアンフィルタとアンシャープマスキングフィルタ
ぼかし処理にガウシアンフィルタを使い、シャープ処理にアンシャープマスキングフィルタを使います。
効果的ですが、ガウシアンフィルタを強くかけると、境界部が失われます。また、アンシャープマスキングフィルタで境界が不自然に補強される場合があります。
ガウシアンフィルタの強弱とアンシャープマスキングフィルタの強弱で調整。1.の拡大縮小よりは細かな調整が利きます。

3.バイラテラルフィルタとラプラシアンフィルタ
ぼかし処理にバイラテラルフィルタを使います。バイラテラルフィルタは境界部を残しそれ以外をぼかす処理をしてくれます。
理想的なフィルタですが、強くフィルタをかけすぎると境界部もボケてきます。
あと、パラメータを強めで1回で処理する場合と、パラメータを弱めで複数実行する場合で結果が異なります。

また、OpenCVにはfastNlMeansDenoisingという強力なノイズ除去フィルタがあります。強力過ぎてディテールをつぶしてしまう場合があるので、フィルターの最終段階で仕上げに使うと良さそうです。

次にラプラシアンフィルタで輪郭を抽出し、その輪郭部分を補強してあげることで境界部にメリハリをつけます。
ラプラシアンフィルタをかけた画像は、境界部が明るい白に近づき、それ以外が暗い黒になります。
PythonとNumpyの話になりますが、ぼかし処理を行った画像からラプラシアンフィルタで抽出した輪郭画像を減算すると、境界部が補強されます。文字通り数値を引き算するような書式で表現できる点はNumpyは良くできているなと感じました。
アンシャープフィルタマスクほど強力では無いですが、かなり自然な仕上がりになります。
(追記、調べてみるとこの手順は平滑化(バイラテラルフィルタ)した画像にラプラシアンフィルタを演算しているあたり、はからずもアンシャープマスキングを手動でやっているような代物になっています。)

この補強で満足できない場合はさらにアンシャープフィルタマスクを施します。その場合アンシャープフィルタマスクでノイズも補強されて画像が荒れるようであれば、さらにfastNlMeansDenoisingでノイズを消してあげます。

輪郭抽出のフィルタはラプラシアンフィルタ以外にも複数存在しますので、試しても見るのも良いと思います。

フィルターを多数かけることで理想の画像に近づけることは出来ますが、代わりに元画像の情報は失われます。何を残し何を消すか、何をぼかし何を強調するか、この調整が楽しいと感じるか、終わりの無い作業と感じるかは人それぞれです。

実装はPython+Numpy+OpenCVの組み合わせで試行錯誤していますが、フィルタの組み合わせを調整することで点が消え、ベタ塗に変換される結果を見ると不思議な感じがします。不可逆な処理だと思いますので、完璧に理想通りにはならないとは思いますが、うまく調整できた場合は、苦労にみあった達成感を感じることが出来ると思います。

追記

最近はバイラテラルフィルタの代わりに以下のカーネルを引数にfilter2Dを数回実行します。

kernel = np.array([[0,2/16,0],[2/16,8/16,2/16],[0,2/16,0]])

バイラテラルフィルタと比べて輪郭はボケますが、格子状のパターンをいい感じに壊してくれます。

バイラテラルフィルタはパラメータを調整すると輪郭を残したまま、ぼかし処理をすることが出来ますが、輪郭を意識しすぎるとぼかしが甘く、輪郭と同色の点が残ったり、濃淡の全くないベタ塗状態になったりします。ぼかす範囲を大きくとるか、処理を数回重ねることで対処できますが、今度は輪郭がボケてきます。理想的なフィルタだと思うのですが、自分には扱いきれない感じです。

上記フィルタで、荒れた感じになるので、次にfastNlMeansDenoisingフィルタを適度に施すことでベタ塗りになります。

この段階で輪郭がボケてくるのでアンシャープマスキングフィルタで輪郭を強調してあげます。

k = 2.0
kernel = np.array([[-k/9,-k/9,-k/9],[-k/9,1+8*k/9,-k/9],[-k/9,-k/9,-k/9]])

これまでは、ラプラシアンフィルタで輪郭を抽出して強調していたのですが、綺麗に輪郭を抽出できる画像であれば、いい感じに効果を発揮してくれますが、輪郭以外が残ってしまうと、それがノイズとして強調されしまうため、扱いが難しいので止めました。
その点アンシャープマスキングフィルタは、kの値を調整することで強調度合いを調整出来ますので、対象画像ごとに設定をつめることで理想の画像に近づけることが出来ます。

追記20211227

セルシス「CLIP STUDIO PAINT EX」の「トーンを消去」機能を使うと、トーンを消去して線画に変換したり、トーン部分をグレースケールに変換することが出来ます。

このソフトはイラストを描く人向けの定番ソフトらしく、私がそういった人種ではない為、まったくチェックしていませんでした。値段は3万円前後と結構いい価格でイラストを描かない人間には厳しいですが、この機能のために購入してしまいました。

仕上がりとして、線同士が込み合った部分など若干トーンが残る場合がありますが、線のディテールをしっかり残した状態でトーン部分のみを処理してくれます。自分が欲していた機能がそのものが入手することが出来ました。

使った感じクラウド上のサービスを利用しているので、時間帯によって処理時間が異なります。多量の画像を一括処理する方法があるとよいのですが無いようなので、とりあえず「オートアクション」機能でトーン消去→上書き保存→画像を閉じるマクロ手順を組んで、複数の画像をドラックアンドドロップでまとめて開いてた状態で1枚1枚オートアクションの再生ボタンをポチポチしています。

まだ先行プレビューですので、トーンが残る部分もありますが、OpenCVのフィルターを駆使して消すことが出来ないか挑戦しています。

追記20220322
「CLIP STUDIO PAIN EX」トーン消去機能は、かなり目的に近い動作をしてくれるようになりました。
なるべくトーンを綺麗に消去してくれるようにスキャン画像の前処理を行います。
モノクロの書類をカラースキャン
GIMPで脱色(またはグレースケール)
GIMPで色→レベル補正→自動入力レベルボタン。雑誌など紙の色が白色でない場合は、レベルの補正で入力レベルのグラフで白色が一番盛り上がる場所まで、手動でつまみを調整する。
GIMPでPNG形式でエクスポート
「CLIP STUDIO PAIN EX」トーン消去
複数の画像を処理する場合は、「CLIP STUDIO PAIN EX」のオートアクションでトーン消去→PNG形式で保存→閉じるを手順化しておくと便利。
さらに「おーとくりっか~」などのクリックを自動化してくれるソフトを使い「オートアクション」のボタンを押すようにするとさらに便利。
複数画像をD&Dで開き→「おーとくりっか~」→オートアクションの流れで自動化すると疑似的な一括処理をしてくれます。
窓の杜
「おーとくりっか~」あらかじめ指定した座標でマウスを自動クリックさせるソフト

トーン消去後若干残るトーンの点と色合いの補正はPython-OpenCVでスクリプトで補正します。
実行するフィルタとして、ガンマ補正→バイラテラルフィルタ→ローカルミーンフィルタ→アンシャープマスキングフィルタ→ローカルミーンフィルタを実行します。
フィルタの効果が強いとトーンの点も消えますが、画像自体がボケたり荒れたりしますので、処理をするかは画像によって判断します。

20220606追記

「Topaz Labs Gigapixel AI」はAIで画像を拡大するアプリケーションなのすが、AIモードを「Very Compressed」にしてSettingを調整したところ、拡大後ハーフトーンが除去されていました。クリップスタジオのトーン消去はある程度解像度が高くないとハーフトーンを構成するドットがつぶれてしまいハーフトーンとして認識できない場合があります。

Waifu2xの拡大でつぶれたドットが復活する場合もありますが、基本的に600DPIぐらいでスキャンした画像が必要になります。その点「Topaz Labs Gigapixel AI」は比較的低解像度の画像でもハーフトーンを除去していました。

多分つぶれたドットをノイズとして認識しノイズ除去処理をしていると思われ結果としてトーンの除去となっているのだと推察します。境界部分は線として認識し拡大して引き直しており、縮小画像なので詳細部分は曖昧ですが、全体的には結構くっきりとした画像になります。

元画像はグレイスケールですが、拡大処理をした画像の線はインクで描画したような感じで若干青く滲んだ感じのカラー画像になります。処理後の画像をGIMPで脱色してグレースケールに変換してあげると変換後の着色部を見ると鉛筆で濃淡を表現したような感じになりました。かなり希望に近い形の処理をしてくれるソフトが見つかりました。

 

20230107追記

今のところ「Topaz Labs Gigapixel AI」を超えるツールは発見していないので「Topaz Labs Gigapixel AI」を愛用しています。「Topaz Labs Gigapixel AI」任せは楽で良いのですが、もう少し精度を上げたいので拡大処理を行う前に以下の処理を施しています。
OpenCV-Pythonで
kernel = np.array([[0,1/8,0],[1/8,4/8,1/8],[0,1/8,0]])
dst = cv2.fliter2D(src, -1, kernel)
で格子状になったトーンを壊してあげます。このフィルタを画像によって1~3回実行します。
このkernelは斜め方向を0にしたガウシアンフィルタのようなもので、ぼかし機能が弱目です。
次にローカルミーンフィルタをh=12ぐらいで実行します。
この段階で格子状のトーンがベタ塗状態または模様のような感じになります。
ただし、ぼかし処理を施したため境界線がボケてしまいますので、その後アンシャープマスキングフィルターで境界部分を引き締めてあげます。
この下準備で取り切れないトーンは「Topaz Labs Gigapixel AI」の拡大処理に任せることが出来ますので、余り完璧を求めないほうが良さそうです。下準備段階で加工をかけすぎると、仕上がりがのっぺりした感じになります。
ここまでして、無加工の画像「Topaz Labs Gigapixel AI」を拡大処理したものと比べて、良い方を採用する感じになります。

20230215

前回と同様に「Topaz Labs Gigapixel AI」を軸に処理を行っています。

前処理でトーンを荒すためフィルタを施しますが、なるべく回数を少な目にしています。

OpenCV-Pythonで
kernel = np.array([[0,1/8,0],[1/8,4/8,1/8],[0,1/8,0]])
dst = cv2.fliter2D(src, -1, kernel)

後処理である「Topaz Labs Gigapixel AI」の仕上がりを見て回数を調整するようにしています。

「Topaz Labs Gigapixel AI」の拡大のモデルは「Very Compress」でオプションは上のつまみ(Blur)が100%下のつまみは0%にセットしています。結果がカラーになりますので、グレースケールに変換します。

概ねベタ塗状態になるのですが、若干黒っぽい筋のようなノイズが乗ります。その後仕上げとして、fastNLMeanフィルタを施していましたが、ノイズも消えますがディテールも消える問題があるので、バイラテラルフィルタにしてみました。

dst = cv2.bilateralFilter(img, 7, sigmaColor=150, sigmaSpace=150)

画像に合わせたとパラメータの調整が必要ですが、fastNLMeanと比べて境界部分が気持ちくっきりしているように見受けられます。

新しい方法を見つけるたび、数百枚からの画像を処理しなおすのですが、出来れば1枚1枚満足いくまで調整したいところですが、なかなか時間的に厳しいものがあります。パフォーマンスの高いPCが欲しくなってきます。

20230307
「Topaz Labs Gigapixel AI」で拡大処理を行いopencvでノイズ除去した画像を、オリジナルサイズに縮小し、Real-CUGANでまた拡大する。
すこし寝ぼけた輪郭線がくっきりする。

20230317
現在の手順
「Topaz Labs Gigapixel AI」の処理は等倍(拡大しない)でノイズ除去だけを行います。その際モデルは「Very Compress」でBlurは100%にしています。その後「Real-CUGAN」で4倍に拡大します。

Real-CUGANは拡大処理というよりAI絵を描き直している感じなので、オリジナルと比べて若干やりすぎな感じもしますが、境界部分はかなりくっきりはっきりとした絵になります。

さらに、Anime2Sketchを使うと輪郭部分を抽出することが出来ます。その際パラメタのload_sizeは2048ぐらいにすると、結構良い制度の線画が出来上がります。

画像系AIのソフトはローカル実行の環境構築のハードルが高いのですが、なかなか興味深いソフトが多く試すのが楽しい分野だと思います。

20230525
現在の手順
fiter2Dでカーネルを以下のものを使用

kernel = np.array([[0,1/8,0],[1/8,4/8,1/8],[0,1/8,0]])

こちらのフィルターを1~3回施す
次にfastNlMeansDenoising(mat, h=12)フィルターを施す
この段階で画像がぼけますがトーン部分はほぼべた塗になります。

次にReal-CUGANで4倍に拡大します。
Real-CUGANのおかげでぼけた境界部がくっきりします。

ハーフトーンをベタ加工するために結構出費をしていますが、気が付いたら無料ソフトの組み合わせが好みに合った感じです。
ただ、かなりパキッとした絵になりますが、少しやりすぎ感が否めないので今後はそのあたりを何とかしたいです。
アイディアとしてはReal-CUGANで生成した画像を縮小し「Topaz Labs Gigapixel AI」で拡大してみたいと思います。
「Topaz Labs Gigapixel AI」はmodeなどのパラメータをいじることが出来ますのでなるべく自然な仕上がりになることを期待しています。

20230721
20230215の処理手順
1/4に縮小(AREA)
REAL-CUGANで4倍に拡大

20230727
・「python-OpenCV」fiter2D

kernel = np.array([[0,1/8,0],[1/8,4/8,1/8],[0,1/8,0]])

・「Topaz Labs Gigapixel AI」2倍に拡大、Very Compressed Suppress Noise:100 Remove Blur: 0
・「python-OpenCV」cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)→1/2に縮小 cv2.INTER_AREA
・「Real-CUGAN」4倍に拡大

pythonのfilter2Dを使ってトーンを荒らすフィルタを便宜上十字フィルタとします。このフィルタの効果は平滑フィルタ(ぼかし)やガウシアンフィルタなどと比べて極めて弱く、逆に考えてるとディテールを壊さない点がメリットになります。
ただし、素材によっては十字フィルタでは弱すぎてガウシアンフィルタでは強すぎる場合もあり得ます。
中間になるようなフィルターとして拡大→縮小をする方法を見つけました。縮小⇒拡大では効果は高いのですが明らかに画質が悪化してしまいます。その点拡大→縮小ではいい感じにトーンを壊してくれます。拡大率やフィルターの回数によって効果の強弱の調整が出来ます。
拡大縮小の補管方法は拡大がLANCZOS4で縮小がAREAを使っています。倍率は大きい方が効果が高いのですがLANCZOS4は重いので小さめの1.5倍ぐらいで、フィルタの回数で効果を高めるようにしています。また、倍率はキリが良い2の倍数ではなく中途半端な数値を選ぶ方が効果が高い感じがします。

20230906

前処理として実行していた十字フィルタや拡大→縮小フィルタをやめました。
AIによる拡大処理後にfastNlMeansDenoising()フィルタを追加してノイズ除去をしています。
・「Topaz Labs Gigapixel AI」4倍に拡大、Very Compressed Suppress Noise:100 Remove Blur: 0
・「python-OpenCV」cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)→cv2.fastNlMeansDenoising(gary, h=7)→1/4に縮小 cv2.INTER_AREA
・「Real-CUGAN」4倍に拡大
「Topaz Labs Gigapixel AI」で拡大した画像の線が画鉛筆ぽい感じで、詳細を見ると境界部分が少し曖昧だったりして加工しずらいので、「Real-CUGAN」で再描画しています。

コメント