ポケコンで Bad Apple !!

ちょっと仕事が落ち着いたので,ひさびさにwonderflで小ネタを投稿.3分40秒の動画データと音声データを圧縮して,465px 四方の1枚の PNG 画像に収めてみました.

Movie data in one PNG image | Si+ (wonderfl.net)
元ネタは,
【東方】Bad Apple!! PV【影絵】
【第5回MMD杯本選】 Bad AApple!!
ニコニコでかなり有名な動画です.


という訳でブログでは技術的ネタバレ.
3分40秒の動画が1枚のpng画像にって聞くとちょっとすごそうに見えますが,実はあんまりたいした事はしてません.
まず音声については SiONMML をそのまま埋め込んでます.MML基本的に 1音=1Ascii文字=1Byte(7bits),かつ,繰り返し指定が可能なため,データ効率が非常に良いフォーマットです.ByteArray.compress()を使えばさらに相当圧縮されるのですが(MMLは出現する文字や表現が大きく偏っているためハフマン符号との相性が良い),そのままでも動画データと比べれば些細な差なので,今回はそのまま埋め込みました(こうするとデータ内のMMLの部分が薄暗くなるので視覚的に面白いという理由もあります).
つぎに動画ですが,実は各フレームの画像に対して単純なランレングス圧縮しかしてません.実際には,多少の試行錯誤の結果,単純なランレングス圧縮が一番効率が良かったため,この方法を選択しています.映像内の動体の重心移動を考慮して差分画像をとれば,もっと圧縮できるかなとも思ったんですが,計算時間がかかりそうな割にそこまでの効果はなさそうだったのでやめました.
ランレングス圧縮はシンプルに縦方向に走査していって同色が連続するピクセル数を保存します.たとえば,黒100個が連続した後,白50個,黒80個連続してる場合,100,50,80と順に書き込みます.数字はMIDIフォーマットで用いられてるザックリとした可変長符号を使ってます(http://www.pluto.dti.ne.jp/~daiki/Midi/AboutSMF_Inside.html 変長数値の項を参照ください).今回の映像は横画面に対して立ち絵が多いため縦方向走査の方が同色が連続しやすく,この走査方向を選択しています.これについては,各フレームごとに圧縮率の高い走査方向を選択する方が若干圧縮率は上がったのですが,煩雑になるのでやめました.
あとは,このランレングス圧縮結果にByteArray.compress()をかけることで圧縮して,バイトデータを256階調のpngイメージとして吐き出せば完成です.


元ネタの影絵ムービーを見たときから,ランレングス圧縮すれば相当縮むなーとか考えてて,アイディア自体は相当昔から暖めてたのですが,MMLを準備するのが面倒だったので,暖めすぎて腐りかけてました.そんななか kashiwa 氏が MMLTalks に素敵なメガドラアレンジBad Apple!! feat. nomico [ 16bit C-S-Mix ] by kashiwaをアップしてくれたので,メロディ部分だけ差し替えて一気に作ってみました.
ムービーpngイメージの作成を含めても2日しかかかってませんが,その割にはなかなかインパクトのある Flash コンテンツが出来たかなーと思います.こういう低負荷で面白いネタが量産できると楽しいんですが,なかなか難しいですね.


PS. 今回のネタでは,Bad AApple出現の隠しコマンドで,お約束のクレジット音が鳴るのが,実は一番のお気に入りだったり.