Imperialとは

Reversalの動画の対戦組み合わせ一覧を出している部分で使っている、動画サイト(現在はニコニコ動画のみ対応)の動画から対戦の組み合わせを推測するプログラムです。 先日の技術メモで書きましたがPython3.5 + OpenCV3 + numpyで動作しています。

実際にサーバ上ではHTTPリクエストに対してJSONで対戦の組み合わせを返すだけのAPIサーバとして動作しています。 リクエストのタイミングでニコニコ動画から動画ファイルを取得し、画像ファイルにエンコードするようになっているため自身は状態を一切持たないDockerコンテナです。

現時点ではミカドの動画しか対象に出来ていなかったり、自分の再確認のメモのため、内部がどうなっているか軽く書いておこうと思います。

前身thrush

Imperialには前身となるthrushというプロジェクトが存在し、協力してくれたエンジニアに雛形を作って貰っていました。が、いくつか越えられなさそうな問題があり実装を大きく変えることになりました。 thrushは大量の動画から静止画を画像として切り出してディープラーニングさせることで、キャラのパターンを組み合わせとして推測させようというものでした。

具体的には計算時間がかかりすぎた(手元のマシンで1動画5-6時間・GPUを搭載した高速なマシンを用意してCuPyに移植して6分程度)のと精度を上げるのに難易度が高めの追加実装が必要そうだったために採用できませんでした。

処理概要

特定座標の画像切り抜きと、その画像と学習素材のパターン認識で対戦を検出しています。 パターン認識のアルゴリズムについてはほぼ初学なため、説明できる部分はありません。

実装当時の学習用素材の豊富さと対象動画の多さから、ミカド動画から切り出した静止画を学習用に利用させて頂いています。 大体片側で1キャラにつき10枚前後、1Pと2Pを合わせて400枚の40px*40pxの画像を学習に使っています。

このあたりが録画環境によっておそらく特徴量の出方が違ってしまっているため他の動画については今のところ検出があまり上手くいっていません。 今なら家庭用で高画質でキャプチャした画像を素材にして学習させることで、対応する動画が増えるかもしれません。

以下のような画像を元に学習させています。

image

処理の流れ

学習

事前にキャラの顔アイコンから特徴量の学習を行います。 アルゴリズムとしてはKNNを用いていますが、精度を少しでも上げるためにまとめてではなく、1キャラずつ20個の検出器を作成しています。

動画の読み込み

FlaskでHTTPリクエストを受け取り、指定された動画をdumpしながらffmpegで標準入力から画像ファイルに変換してます。以下具体的なコードの一部です。

os.system("nicovideo-dump http://www.nicovideo.jp/watch/" + video_id + " | ffmpeg -i pipe:0 -filter_complex '[0:v]fps=1, scale=640:-1, split[tmp1][tmp2]; [tmp1]crop=40:40:20:0[out1]; [tmp2]crop=40:40:580:0[out2]' -map '[out1]' " + fetch_dir + "/1p/%04d.jpg -map '[out2]' " + fetch_dir + "/2p/%04d.jpg")

ffmpegで最初に1fpsに変換し、横幅640にスケーリングした後にx:20, y:0とx:580, y:0の座標から40px*40pxの画像をcropしてそれぞれのディレクトリに出力しています。 具体的には体力バーの横の顔アイコンの座標なのですが、この時点で顔アイコンがこの位置からずれる動画は対象外になります(何らかの編集でゲーム画面以外に枠が入っている動画など)

スコアの計算

検出器を使って1Pと2Pからそれぞれのスコアをフレーム毎に20次元のデータとして取得しています。 その後取得したデータを前後20フレームで畳み込み演算し、対戦画面ではない部分を丸め込んでいます。

畳み込み演算した20次元の値からこの時点で最もスコアが高いペアを1組ずつ抽出し、そのフレームの対戦組み合わせとして推測しています。

対戦組み合わせの足切り

手に入った対戦組み合わせに対してスコアが一定以上のフレーム以上を真として扱い、真である連続した組み合わせのみを取りだして対戦組み合わせの候補を選出しています。 その後、一定フレーム以下しか存在しない対戦組み合わせの候補を削除します。

API出力

以上で得られた結果を開始時間・1Pキャラ名・2Pキャラ名のオブジェクトのリストを返すJSONとしてHTTPレスポンスで出力しています。

やってみて

画像認識の問題に取り組んだことがなかったため、2週間程度かかりっきりでどうにかそれなりの精度を出す実装を作ることができました。 精度については特徴量からの検出に別のアルゴリズムを採用するなり、枠がある場合でもゲーム画面自体を検出してキャプチャリングする機能なりを実装することで色々と改善できる部分はあるので今後の課題としていきたいと思っています。