Posts
ぼやけた画像からドット絵を抽出する
おっさんホイホイなのか、一周まわって新鮮に見えるのか、ドット絵がプロモーションの手段として使われるケースが増えている。


JPEG や動画という形で公開されたこれらメディアの中から劣化したドット絵を、完璧ではないにしろそれなりの画質で抽出したいと思ったとき、その作業をペイントツールの手動補正でやろうとするとかなりの労力が必要になる。実際、CH-R の時は一枚のドット絵を仕上げるのに何時間もかかってしまった。
この作業をプログラムにやらせることで少しは楽ができないか、ということでドット絵を抽出する方法について考えてみた。
ピクセル境界の検出
少ないピクセルで構成されているドット絵は画像がとても小さいので、HD 動画など高画質メディアのサイズに合わせて拡大したものが使われている。これを元のドット絵、いわゆるドットバイドットに戻すには、1 ピクセルがどれだけの大きさなのか、別の言い方をすると各ピクセルの境界がどこにあるのか調べる必要がある。
自動検出 (拡大率から計算)
対象のドット絵だけ切り出した後、目視でピクセルを数え、全ピクセル数をドット絵のピクセル数で割ればひとつの点に何ピクセル使われているのか求めることができる。例えば 100x100 の画像に 20x20 のドット絵が描かれているのであれば、1 ピクセルが 5x5 で構成されているということが分かる。
ただ、ドット絵が整数倍で綺麗に拡大されていることは稀で、拡大率が小数になってしまい各ピクセルの大きさを正確に計算できないケースがほとんど。

ピクセルの大きさが 2, 1, 2, 1 ... といった具合で規則性があるなら対処のしようもあるが、拡大時に特殊なアルゴリズムが使われているのか不規則なものもあったので、この方法は諦めることにした。
自動検出 (エッジ検出)
ピクセルの境界はエッジでもあるのでエッジ検出が使えるのか考えてみる。エッジ検出は輝度の変化を調べるものなので、
- バイリニア法などで拡大されたピクセルはぼやけているのでエッジが安定しない。
- JPEG ノイズをエッジとして誤検出してしまう。
という理由により、正確なピクセル数を求めるのには向いていない。
また、ピクセルが正方形に分割できる単位で分布していると元の大きさを判別できないという問題もある。

手動検出 (テキスト入力)
自動で検出するのが難しいとわかったので、潔く目視でピクセルを数えることにした。
何らかの方法でプログラムにピクセル数を伝える必要があるので、テキストファイルに適当な書式でピクセル数を書いて、それを読み込むプログラムを作成。
[WIDTH] 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 [HEIGHT] 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1
実際にやってみると入力作業が結構大変で、大きいドット絵になると大量の数値で目がチカチカして、どの位置を編集しているのかわからなくなる。
手動検出 (マーカー設定)
テキスト入力は実用面で厳しかったので、画像の外周にマーキングする方法に変更した。同じピクセルと判断したところを同じ色で塗りつぶすというルール。

マーカーを読み取るプログラムを書く手間はあるものの、この方法だとかなり効率よく設定ができる。
ピクセル色の決定
ピクセルの境界が分かったら、構成ピクセルの中から代表となるピクセルを決定する。
メディアンフィルター
まず思いつくのは全ピクセルの平均を求めるという単純な方法。正確なピクセルも不正確なピクセルも全て拾ってしまうので良くもなく悪くもない結果になる。JPEG のようなノイズが多い画像だと悪い方向に寄ってしまう。

こういう時にはノイズに強いメディアンフィルターというアルゴリズムを使用する。平均値ではなく中央値を使うのでノイズのような突出した値の影響を受けにくい。
JPEG のノイズは輝度が変化しているものと色相が変化しているものがあるので、RGB のセットで中央値を計算するより、各 RGB 値の中央値を求めた後に合算した方が、より良い結果が得られた。
ピクセル中央からの距離
メディアンフィルターのおかげでそこそこの画質は得られるようになったが、激しくぼやけた画像だと結果はいまいち。原因は隣接したピクセルの影響で外周のピクセルが変色しているため。

そこでピクセルの集合をピクセル中心からの距離でソートした後、適当な閾値で上位のピクセルだけ残すというアルゴリズムに変更した。

結果は良好で、メディアンフィルターのみの時よりパッキリとした画像が得られた。
実際に変換してみる
これまで書いた仕様を元に作ったプログラムを使って抽出実験を行った。今回のサンプルは「鳩に困ったら雨宮」という動画に出てくる格闘ゲームパートのスナップ画像。

