2018 05 19

電光掲示板を作るの2

昨日の続き。 今日の目標は、文字を描画したキャンバスからサンプリングして、電光掲示板に見立てた別のキャンバスに再現するところまで。

サンプリングの元画像となる部分は昨日のをほぼそのまま使う。 違うのは背景色設定。 昨日は試しに文字を描いてみただけなので何も指定していなかったが、今日は電光掲示板らしく背景を黒にしておく。

そしてサンプリング。

適当な間隔で画素をピックアップして色を調べ、その色でサンプリング間隔と同じサイズの円を描く。 いや、サンプリング間隔と同じサイズだと、詰まり過ぎな気がするな。 円同士は1ピクセルぐらい隙間を空けておくか。 描画する円の半径を r とすると、サンプリング間隔は 2r+1 となる関係。

適当な間隔。 適当な円の大きさ。 なんて簡単に言うが 「適当」 っでどの程度だろう。 大き過ぎると文字の線が拾えなかったり文字が潰れたりする。 小さくすればそんな問題は無くなるが、今度は電光掲示板に見えなくなりそう。 加減が難しい。

いや、いきなり正解を出そうとするから難しいんだよな。 何が適当なのかを探るところから始めればいいのだ。

ということで適当を探るために、この辺りのどれかだろうと思われる円の半径を試せるようにしてみた。

まず、上のキャンバスに文字を表示しておく。 その状態で、下の方の選択肢で円の半径を指定して適用ボタンをクリックすると、上のキャンバスからサンプリングした結果を下のキャンバスに描画する。

下の部分のソース。

<select id="s2"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> </select> <input type="button" value="適用" onclick="apply2()"/> <canvas id="c2" width="400" height="100" style="background:black;"></canvas> <script> function apply2() { const ct1 = document.getElementById("c1").getContext("2d"); const ct2 = document.getElementById("c2").getContext("2d"); const r = Number( document.getElementById("s2").value ); const d = r * 2 + 1; ct2.clearRect( 0, 0, 400, 100 ); for ( let x = r + 1; x < 400; x += d ) { for ( let y = r + 1; y < 100; y += d ) { ct2.fillStyle = "rgba(" + ct1.getImageData( x, y, 1, 1 ).data.slice( 0, 4 ).join( "," ) + " )"; ct2.beginPath(); ct2.arc( x, y, r, 0, Math.PI * 2, true ); ct2.fill(); } } } </script>

実際にやってみた結果が意外に電光掲示板だった。

半径1はちょっと古いAV機器のLEDディスプレイという感じだが、これはこれでありだろう。 半径2はいかにもな電光掲示板。 半径3から先も電光掲示板だが、文字が読めなくなってしまった。

文字が読めなくなったのは、文字を構成する線の幅よりもサンプリング間隔の方が大きくなってしまったから。 円の中心付近の1点だけではなく、円に該当する領域内の何箇所かをサンプリングして平均値や最頻値を使うようにすれば、もう少し見れるものになるだろう。 でも良くなったところでモザイクレベルなんだよな。

ということで、この場における 「適当」 の正解は半径1か2となった。

しかし実際の電光掲示板はどうなってるんだろうね。 高幡不動の参道のは、こんな強引な変換じゃなくて、画素数に応じて見易さ優先で設計されたフォントを使っていると思うのだが。 クリーニング屋の軒先にあるような低解像度だが派手な演出のやつは、発光パターンを人が手作りしてるんじゃないかって気もするな。

今日はここまで。 明日はこの変換結果を動かす。