はじめに
画像をアスキーアート化するツールを作りました.こちらから遊べます.
他の画像処理ツール(画像ピクセル並び替え)を作ったついでにこちらも実装しました. 以前に別の機会で画像のアスキーアート化のコードを書いたことがあり,アルゴリズムの中身はほとんどその時のままです.
作ったもの
画像を入力とし,何行かの文字列を出力します. 画像を小さなタイルに分割し,各タイルごとに文字と画像が対応付くように計算を行います.
文字と画像の対応の付け方として,2 種類の手法を実装しました.
- エッジベース
- 明るさベース
まず,これら共通の処理として,画像をグレイスケールに変換します.
エッジベース
エッジの強度の計算
ピクセル の明るさを としたとき,方向ベクトル に対するエッジの強度を と定義します. この差分は指定方向に対して直交する方向のエッジ成分をとらえますが,文字側・画像側の双方で同じ定義を用いて計算し,比較を行うため問題ありません.
下図に示す 8 方向について各画素のエッジの強度を計算します.
文字ごとの事前計算
全ての候補文字について,事前に描画し,各ピクセルについて同様に 8 方向のエッジの強度を計算し,総和を取り,正規化します. これにより,各文字は 8 次元のエッジプロファイルベクトルとして表されます.
入力画像の処理
画像を小さなタイルに分割し,それぞれのタイルについて同様に 8 方向のエッジ強度を計算し,総和をとり,8 次元のベクトルとします. このベクトルと各文字のエッジプロファイルとの内積を類似度とし,最も類似度が高い文字をそのタイルに割り当てます.
さらに,各タイルについての類似度(の最大値)を用いて,エッジが強いタイルほど文字を出す,弱いタイルは空白にするといった間引きを行い,より全体の形状がくっきりと浮かび上がります.
特徴と利点
ラプラシアンフィルタなどの畳み込みベースのエッジ抽出処理を行わず, 各方向ごとに差分を直接計算することで,高速かつシンプルに方向別のエッジ成分を得られます.
Web サイト上では 1 枚の画像に対しての変換を行っていますが,Web カメラで得た画像のリアルタイム変換も可能です(別の企画で作ったやつなので公開していません,ごめんね).
明るさベース
画像をグレイスケール化したのちに,タイルごとに平均の明るさを計算します.
一方で,あらかじめ全ての候補文字について描画し,画素のうちストロークが占める割合(密度)を求めます.
W
や %
は密度が高く,-
や '
は密度が低いです.
ただし,文字は描画領域全体と比べると空白部分が多いので,これを補正する必要があります.
そこで,密度をスケーリングして画像の明るさと比較できるようにし,最も近い値を持つ文字を各タイルに割り当てます.
おわりに
画像をアスキーアート化するツールを作りました.こちらから遊べます.
画像処理はフロントエンド(各々の端末上)で処理されるように書いています. 入力画像はサーバーに送信されないので,お気軽にお試しください.
特にエッジベースの変換が気に入っています. 以下は実際にエッジベースの変換を行った結果です.
使用結果
アスキーアート
-22>2>============~~S~_-
222~<C2 $$SSSSS_
-222>==========E==Z2?%C ^$SSS ~S< <S SS$$SSSS-
2??2$ //#Q $S\ 1 \S\(SSSS
/2??8 / # H \ \^$SS\
/7???2S XS\ \^8S\
/77/C% /I\ /Z \\ \ V$S \\ \\^$\
/77?//# /L /R /\ A \ V$\ \\^\\
/7?7?/#\ /\ /1\ /\ ^$\ \ N%A\
//?%Q /Q /&R /5 |/TVS\ /I\ VS \ V^\ Q\ VXA\
/?& //Q/# /V /# /77%L\ /TP\V$S\ VP\S\ V\ V\\ N\\ |AR\
/#\ /\ V /#\ / /7?Q /\ /L/\ V$$S /\V$\\ $L VV\ \ V\\ VI\
# /R /L # //#\ |"\ R /\|R ^SS V\ V$S VA\ V\ N!N\
/#\ /\ /#Q |R |\ /\ ^SSSA\EEXSS==E%\E |\ /R
/\ /\ 2/#L===EZ5 V\ /\ V\ ??>3C V($ V$\S\ %\ N N V\
/\ |IR?/P\ /# V\ /I\ V\ (S$$\ O |\ V"\
H\\ RR V$\|LA\#\ V$\ |F^\S\V&\ ^\\ /\ /\
H\ /NL UH\NLV?E----___---------$S\ NP\ ^\\\)\ VR\ 1\ P\
/\ MN UH/7?2722-=//2---~<SS-<<SS\\\/\ ^S8S Z22--_----------- |A\ /\ | /\
/\ |N |/\//Y /H /W ^\ V\NI\ /222-------_==_--<S\|A\ /T=~-\ N |L
"\ N U\|\ /\ 2?\V\\_7/\ / )\ //72 |R\ |LN ^\\ | P\
/\ 1NN PV\\\\ |NR/2S-2Z<\M\ /\ |LN N H\ /\
A\N /\N\ /\L\\\\\ |N\S---<?/V !\ H\ //A |/\
|\\\ VA\N |I/\ SSS - SS~==2? /Q ##/V#/W#|T L /4I>?X/IS\\| |1\
|\\\ |C\\ "H\ #/ /R|L\ | P\ /\\ V\\ |/\
|\\ V\\\ |F\ 7###A## /222--_6S~S\\ /74\ /\/S5S\S |^\ ^\\|RZ_=-/2=S\
V\\ |AN\\ VM\\S\ /7? %C /\ 27??X |\ /\/\\\ V\\\^\\ VQ\/RI V$\ |A\\
V\ VRNR\ |T V$SSS-- |"\ ///\ Z222?8V |\V#\#\ X\S ^\\$S\ ^$LS\ |\\ /RQ\
V\ |\\^\\V\ N K$S~~S~~~~=_- ^---------2 22222?) \ 4|/L/T V$\\ C? V\\ V\\-/\VT
|L |T\V(\V\ 4\ \$J~~~~====~E=======E>2>22/TC V\ /F%/H#\ V\S\ |$SE?$F) P\
LN /\ VQR\ A U\ 2=>E===E======/N /R~=======EI5==55CS%###2========57 %SS\ /T
H| |L VC\ 726>23233 |E==3# |E59\ /#Q)S-~=====_- ( #\
|L| |T -22??S P\ & |H\ # /#S~SSSSS\$~5======C\ X\
/LW /L/722><-- ^\S /\ /T //# XSSSS\ \ V\\ \
#N /##\ \\\^SS\ A\ -<|/\ /7?Y ^SSSS\ V\\
/RV //Q \\\\S |R\SS-=>=====~~=>>>6/#\ //7% ^$SSSSV\
/AV /#Q222 \\\\\S |AS23> $SSS?# //7? /2/?^QO\
/AV /#2?/ \\\S\S ^\\ /#\ 2?2? /7? /\\
/#| //98 \\SSS\ ^\\ //Q //??% /#\ /7?%"\