Posts
ぼやけた画像からドット絵を抽出する
おっさんホイホイなのか、一周まわって新鮮に見えるのか、ドット絵がプロモーションの手段として使われるケースが増えている。
JPEG や動画という形で公開されたこれらメディアの中から、完璧ではないにしろそれなりの画質でドット絵を抽出したいと思ったとき、これを手作業でやろうとすると大変かつ膨大な労力が必要になる。実際、CH-R を抽出したときは一枚のドット絵を仕上げるのに何時間もかかってしまった。
この作業をプログラムにやらせることで少しは楽ができないか、ということでドット絵を抽出する方法について考えてみた。
ピクセル境界の検出 (自動)
ドット絵自体はとても小さいものなので、高解像度のメディアに合うよう拡大処理が施される。これを元のドット絵、いわゆるドットバイドットに戻すには、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 要素の中央値を求めた後に合算した方が良い結果が得られた。
距離フィルター
メディアンフィルターのおかげでそこそこの画質は得られるようになったが、激しくぼやけた画像だと結果はいまいち。原因は、外周のピクセルが隣接したピクセルの影響を受けて変色しているため。
そこでピクセルの集合をピクセル中心からの距離でソートした後、適当な閾値で上位のピクセルだけ残すというアルゴリズムに変更した。
結果は良好で、メディアンフィルター単体よりパッキリとした画像が得られるようになった。
実際に変換してみる
これまで書いた仕様を元に作ったプログラムを使って抽出実験を行った。今回のサンプルは「鳩に困ったら雨宮」という動画に出てくる格闘ゲームパートのスナップ画像。